diff --git a/checkstyle.xml b/checkstyle.xml index f1f100e69bea2..5408a167ec447 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -37,6 +37,9 @@ + + + diff --git a/external-service-impl/mqtt/pom.xml b/external-service-impl/mqtt/pom.xml index eb9a918102162..64cabf3288c9d 100644 --- a/external-service-impl/mqtt/pom.xml +++ b/external-service-impl/mqtt/pom.xml @@ -111,11 +111,6 @@ ${tsfile.version} provided - - com.google.guava - guava - provided - org.apache.iotdb iotdb-thrift diff --git a/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/JSONPayloadFormatter.java b/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/JSONPayloadFormatter.java index 72adde783df42..658da21501c1b 100644 --- a/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/JSONPayloadFormatter.java +++ b/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/JSONPayloadFormatter.java @@ -18,9 +18,9 @@ package org.apache.iotdb.mqtt; +import org.apache.iotdb.google.common.collect.Lists; import org.apache.iotdb.mqtt.i18n.MqttMessages; -import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; diff --git a/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/PayloadFormatManager.java b/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/PayloadFormatManager.java index 94ca77a8178e0..775b70bdd546b 100644 --- a/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/PayloadFormatManager.java +++ b/external-service-impl/mqtt/src/main/java/org/apache/iotdb/mqtt/PayloadFormatManager.java @@ -20,9 +20,9 @@ import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.google.common.base.Preconditions; import org.apache.iotdb.mqtt.i18n.MqttMessages; -import com.google.common.base.Preconditions; import org.apache.tsfile.external.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/external-service-impl/rest/pom.xml b/external-service-impl/rest/pom.xml index f6cedd0079684..d5cd93ad9d870 100644 --- a/external-service-impl/rest/pom.xml +++ b/external-service-impl/rest/pom.xml @@ -122,11 +122,6 @@ 2.0.11-SNAPSHOT provided - - com.google.guava - guava - provided - org.slf4j slf4j-api diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/impl/GrafanaApiServiceImpl.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/impl/GrafanaApiServiceImpl.java index 94ecdc61e4d61..159df72d9aa20 100644 --- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/impl/GrafanaApiServiceImpl.java +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v1/impl/GrafanaApiServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.crud.QueryStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowStatement; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.base.Joiner; import org.apache.iotdb.rest.protocol.handler.AuthorizationHandler; import org.apache.iotdb.rest.protocol.handler.QueryRowLimitUtils; import org.apache.iotdb.rest.protocol.v1.GrafanaApiService; @@ -47,7 +48,6 @@ import org.apache.iotdb.rest.protocol.v1.model.SQL; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.base.Joiner; import org.apache.tsfile.external.commons.lang3.StringUtils; import jakarta.ws.rs.core.Response; diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/impl/GrafanaApiServiceImpl.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/impl/GrafanaApiServiceImpl.java index e6e7cdf3a5dc2..f703604aef5c0 100644 --- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/impl/GrafanaApiServiceImpl.java +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/protocol/v2/impl/GrafanaApiServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.crud.QueryStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowStatement; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.base.Joiner; import org.apache.iotdb.rest.protocol.handler.AuthorizationHandler; import org.apache.iotdb.rest.protocol.handler.QueryRowLimitUtils; import org.apache.iotdb.rest.protocol.v2.GrafanaApiService; @@ -47,7 +48,6 @@ import org.apache.iotdb.rest.protocol.v2.model.SQL; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.base.Joiner; import org.apache.tsfile.external.commons.lang3.StringUtils; import jakarta.ws.rs.core.Response; diff --git a/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java b/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java index 8f5567c7c9cf7..2061c96e34416 100644 --- a/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java +++ b/integration-test/src/test/java/org/apache/iotdb/ainode/utils/AINodeTestUtils.java @@ -19,10 +19,10 @@ package org.apache.iotdb.ainode.utils; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.itbase.env.BaseEnv; -import com.google.common.collect.ImmutableSet; import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadLastCacheIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadLastCacheIT.java index 4fd1f6fab9139..6a97f97e42a94 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadLastCacheIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadLastCacheIT.java @@ -21,13 +21,13 @@ import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.LastCacheLoadStrategy; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.it.utils.TsFileGenerator; import org.apache.iotdb.itbase.category.ClusterIT; import org.apache.iotdb.itbase.category.LocalStandaloneIT; import org.apache.iotdb.jdbc.IoTDBSQLException; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.enums.ColumnCategory; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.ColumnSchema; diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBVerifyConnectionIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBVerifyConnectionIT.java index 378f47c0c385d..adf86cae8d56c 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBVerifyConnectionIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBVerifyConnectionIT.java @@ -21,11 +21,11 @@ import org.apache.iotdb.commons.service.ThriftService; import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.it.framework.IoTDBTestRunner; import org.apache.iotdb.itbase.category.ClusterIT; -import com.google.common.collect.ImmutableMap; import org.junit.After; import org.junit.Assert; import org.junit.Before; diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/audit/AuditLogSet.java b/integration-test/src/test/java/org/apache/iotdb/db/it/audit/AuditLogSet.java index 9fff64a90079d..089bd5f615203 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/audit/AuditLogSet.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/audit/AuditLogSet.java @@ -19,7 +19,8 @@ package org.apache.iotdb.db.it.audit; -import com.google.common.collect.HashMultiset; +import org.apache.iotdb.google.common.collect.HashMultiset; + import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java index 1d451edf9730b..d4a95e3fc676a 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java @@ -22,6 +22,7 @@ import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.db.it.utils.TestUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.it.framework.IoTDBTestRunner; import org.apache.iotdb.itbase.category.ClusterIT; @@ -30,7 +31,6 @@ import org.apache.iotdb.jdbc.IoTDBSQLException; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; import org.junit.*; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBQueryDemoIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBQueryDemoIT.java index 198ea449dcc71..47ed70b0e14cc 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBQueryDemoIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBQueryDemoIT.java @@ -18,13 +18,13 @@ */ package org.apache.iotdb.db.it.query; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.isession.ISession; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.it.framework.IoTDBTestRunner; import org.apache.iotdb.itbase.category.ClusterIT; import org.apache.iotdb.itbase.category.LocalStandaloneIT; -import com.google.common.collect.ImmutableList; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java index f8cc92699e765..88a9a7e0ceac1 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java @@ -19,13 +19,13 @@ package org.apache.iotdb.relational.it.query.old; import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.it.framework.IoTDBTestRunner; import org.apache.iotdb.itbase.category.TableClusterIT; import org.apache.iotdb.itbase.category.TableLocalStandaloneIT; import org.apache.iotdb.itbase.env.BaseEnv; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.junit.After; import org.junit.Assert; diff --git a/iotdb-core/calc-commons/pom.xml b/iotdb-core/calc-commons/pom.xml index baa6d16bad444..266a843a00a38 100644 --- a/iotdb-core/calc-commons/pom.xml +++ b/iotdb-core/calc-commons/pom.xml @@ -91,10 +91,6 @@ com.google.code.gson gson - - com.google.guava - guava - at.yawk.lz4 lz4-java diff --git a/iotdb-core/calc-commons/src/main/codegen/templates/ModeAccumulator.ftl b/iotdb-core/calc-commons/src/main/codegen/templates/ModeAccumulator.ftl index c881219a97cf1..734dcfcc881d5 100644 --- a/iotdb-core/calc-commons/src/main/codegen/templates/ModeAccumulator.ftl +++ b/iotdb-core/calc-commons/src/main/codegen/templates/ModeAccumulator.ftl @@ -25,7 +25,7 @@ package org.apache.iotdb.calc.execution.aggregation; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.tsfile.external.commons.collections4.comparators.ComparatorChain; import org.apache.tsfile.enums.TSDataType; @@ -45,7 +45,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /* * This class is generated using freemarker and the ${.template_name} template. diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/FutureStateChange.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/FutureStateChange.java index aa1db91e1779b..ef66e7305e0ba 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/FutureStateChange.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/FutureStateChange.java @@ -20,10 +20,9 @@ package org.apache.iotdb.calc.execution; import org.apache.iotdb.calc.i18n.CalcMessages; - -import com.google.common.collect.ImmutableSet; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; @@ -32,8 +31,8 @@ import java.util.Set; import java.util.concurrent.Executor; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; @ThreadSafe public class FutureStateChange { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/StateMachine.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/StateMachine.java index 43dc38b64b277..fba590309a941 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/StateMachine.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/StateMachine.java @@ -20,11 +20,11 @@ package org.apache.iotdb.calc.execution; import org.apache.iotdb.calc.i18n.CalcMessages; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.util.concurrent.ListenableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,9 +40,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.util.concurrent.Futures.immediateFuture; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; @ThreadSafe public class StateMachine { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java index c8c0ab7446827..30c5bf316904b 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CentralMomentAccumulator.java @@ -25,7 +25,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CentralMomentAccumulator implements Accumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CorrelationAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CorrelationAccumulator.java index 97ad313c2b6db..3771db075b25e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CorrelationAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CorrelationAccumulator.java @@ -30,7 +30,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CorrelationAccumulator implements Accumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CovarianceAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CovarianceAccumulator.java index dc2110ce098c9..14d4a79613964 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CovarianceAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/CovarianceAccumulator.java @@ -30,7 +30,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CovarianceAccumulator implements Accumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/RegressionAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/RegressionAccumulator.java index ca58ba7a68fb7..ba37361983e3e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/RegressionAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/RegressionAccumulator.java @@ -25,7 +25,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class RegressionAccumulator implements Accumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/VarianceAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/VarianceAccumulator.java index 528f12982724d..886271a104687 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/VarianceAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/aggregation/VarianceAccumulator.java @@ -32,7 +32,7 @@ import java.util.Arrays; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class VarianceAccumulator implements Accumulator { public enum VarianceType { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberAccumulator.java index 0343359dc572b..62d743b16c081 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberAccumulator.java @@ -29,10 +29,10 @@ import java.util.function.LongConsumer; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Verify.verify; public class GroupedTopNRowNumberAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberBuilder.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberBuilder.java index 78c01ef14c071..b24b8ace392e4 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberBuilder.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/GroupedTopNRowNumberBuilder.java @@ -21,9 +21,9 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array.LongBigArray; import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.GroupByHash; +import org.apache.iotdb.google.common.collect.AbstractIterator; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.AbstractIterator; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/Operator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/Operator.java index a2c937e0bb7c3..d69a01eb30e11 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/Operator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/Operator.java @@ -19,11 +19,12 @@ package org.apache.iotdb.calc.execution.operator; -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; + import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.Accountable; -import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; public interface Operator extends AutoCloseable, Accountable { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/RowReferenceTsBlockManager.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/RowReferenceTsBlockManager.java index a3cc842833136..10bc8e4035bff 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/RowReferenceTsBlockManager.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/RowReferenceTsBlockManager.java @@ -29,9 +29,9 @@ import java.util.Arrays; import java.util.HashSet; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verify; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Verify.verify; public class RowReferenceTsBlockManager { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractConsumeAllOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractConsumeAllOperator.java index c774023ca90dd..60adfe678ac07 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractConsumeAllOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractConsumeAllOperator.java @@ -22,15 +22,15 @@ import org.apache.iotdb.calc.execution.operator.AbstractOperator; import org.apache.iotdb.calc.execution.operator.CommonOperatorContext; import org.apache.iotdb.calc.execution.operator.Operator; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import static com.google.common.util.concurrent.Futures.successfulAsList; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; /** ConsumeAllOperator will consume all children's result every time. */ public abstract class AbstractConsumeAllOperator extends AbstractOperator diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractFillOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractFillOperator.java index fe56c91c643a5..d23a65ea394dc 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractFillOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractFillOperator.java @@ -24,14 +24,14 @@ import org.apache.iotdb.calc.execution.operator.process.fill.IFill; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** Used for previous and constant value fill. */ public abstract class AbstractFillOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractLinearFillOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractLinearFillOperator.java index d25259b926ffb..9b9851c2e2d79 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractLinearFillOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractLinearFillOperator.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.execution.operator.process.fill.ILinearFill; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; @@ -34,8 +34,8 @@ import java.util.Arrays; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public abstract class AbstractLinearFillOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractSortOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractSortOperator.java index c9f73f8493747..7bbac56cffe7d 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractSortOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AbstractSortOperator.java @@ -31,8 +31,8 @@ import org.apache.iotdb.calc.utils.sort.SortReader; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.exception.IoTDBException; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AggregationMergeSortOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AggregationMergeSortOperator.java index 6f106d680df4e..406a4af307ec3 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AggregationMergeSortOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AggregationMergeSortOperator.java @@ -26,8 +26,8 @@ import org.apache.iotdb.calc.utils.datastructure.MergeSortKey; import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; @@ -43,7 +43,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.google.common.util.concurrent.Futures.successfulAsList; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; public class AggregationMergeSortOperator extends AbstractConsumeAllOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AssignUniqueIdOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AssignUniqueIdOperator.java index 0fc91b955d87b..47900871fc344 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AssignUniqueIdOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/AssignUniqueIdOperator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.read.common.block.TsBlock; @@ -33,8 +33,8 @@ import java.util.concurrent.atomic.AtomicLong; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verify; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Verify.verify; public class AssignUniqueIdOperator implements ProcessOperator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/CollectOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/CollectOperator.java index 921d273d04e8a..078877ea6edeb 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/CollectOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/CollectOperator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/EnforceSingleRowOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/EnforceSingleRowOperator.java index 6a300c2cdfe64..2cc7386973066 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/EnforceSingleRowOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/EnforceSingleRowOperator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.commons.exception.SemanticException; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/FilterAndProjectOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/FilterAndProjectOperator.java index 5ab7e58a6962f..03c097b0b1e2d 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/FilterAndProjectOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/FilterAndProjectOperator.java @@ -33,8 +33,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ternary.TernaryColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.UnaryColumnTransformer; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/LimitOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/LimitOperator.java index 5a99df33b2758..26431edaf6d42 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/LimitOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/LimitOperator.java @@ -23,13 +23,13 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class LimitOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/MergeSortOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/MergeSortOperator.java index 613d3f549d722..0cf263cd6bd76 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/MergeSortOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/MergeSortOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.calc.utils.datastructure.MergeSortKey; import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; @@ -40,7 +40,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.google.common.util.concurrent.Futures.successfulAsList; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; public abstract class MergeSortOperator extends AbstractConsumeAllOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/OffsetOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/OffsetOperator.java index 2c8a87cade248..30056a0a9265d 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/OffsetOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/OffsetOperator.java @@ -23,13 +23,13 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class OffsetOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PatternRecognitionOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PatternRecognitionOperator.java index d0ee968c0e569..0428e1694dee6 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PatternRecognitionOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PatternRecognitionOperator.java @@ -32,9 +32,9 @@ import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.RowsPerMatch; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SkipToPosition; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PreviousFillWithGroupOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PreviousFillWithGroupOperator.java index 2810d6a182a52..20ed3f3b9caac 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PreviousFillWithGroupOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/PreviousFillWithGroupOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; @@ -37,8 +37,8 @@ import java.util.Comparator; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** Used for table previous fill with group. */ public class PreviousFillWithGroupOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TableNextFillOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TableNextFillOperator.java index 3bcc67ade8155..b3940238ce38c 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TableNextFillOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TableNextFillOperator.java @@ -27,7 +27,7 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.TsBlock; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableNextFillOperator extends AbstractLinearFillOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TopKOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TopKOperator.java index b2754cbbdd67f..6a79e34f44562 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TopKOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/TopKOperator.java @@ -27,8 +27,8 @@ import org.apache.iotdb.calc.utils.datastructure.MergeSortKey; import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; @@ -51,7 +51,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; -import static com.google.common.util.concurrent.Futures.successfulAsList; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; public abstract class TopKOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/ValuesOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/ValuesOperator.java index b1a3e46d4b6da..af10f989e47b1 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/ValuesOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/ValuesOperator.java @@ -23,9 +23,9 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/fill/linear/LinearFill.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/fill/linear/LinearFill.java index ea1071361b65c..6ac33e9982b65 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/fill/linear/LinearFill.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/fill/linear/LinearFill.java @@ -27,7 +27,7 @@ import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * The result of Linear Fill functions at timestamp "T" is calculated by performing a linear fitting diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionLeafOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionLeafOperator.java index 589e175e146e7..29a29aa64a041 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionLeafOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionLeafOperator.java @@ -24,12 +24,12 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.commons.exception.IoTDBRuntimeException; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.udf.api.IoTDBLocal; import org.apache.iotdb.udf.api.relational.table.TableFunctionProcessorProvider; import org.apache.iotdb.udf.api.relational.table.processor.TableFunctionLeafProcessor; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; @@ -41,7 +41,7 @@ import java.util.Arrays; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; // only one input source is supported now public class TableFunctionLeafOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionOperator.java index fb9b90c9f604a..d4b362b822016 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/function/TableFunctionOperator.java @@ -28,12 +28,12 @@ import org.apache.iotdb.calc.execution.operator.process.function.partition.Slice; import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.udf.api.IoTDBLocal; import org.apache.iotdb.udf.api.relational.access.Record; import org.apache.iotdb.udf.api.relational.table.TableFunctionProcessorProvider; import org.apache.iotdb.udf.api.relational.table.processor.TableFunctionDataProcessor; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; @@ -53,7 +53,7 @@ import java.util.Optional; import java.util.Queue; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; // only one input source is supported now public class TableFunctionOperator extends AbstractOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/gapfill/AbstractGapFillOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/gapfill/AbstractGapFillOperator.java index 172fd371faa0e..997845fbd526a 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/gapfill/AbstractGapFillOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/gapfill/AbstractGapFillOperator.java @@ -26,8 +26,8 @@ import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; @@ -38,7 +38,7 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; abstract class AbstractGapFillOperator implements ProcessOperator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/SimpleNestedLoopCrossJoinOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/SimpleNestedLoopCrossJoinOperator.java index 8e005fbc97d0b..ea73372d6a61e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/SimpleNestedLoopCrossJoinOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/join/SimpleNestedLoopCrossJoinOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/LogicalIndexNavigation.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/LogicalIndexNavigation.java index ae2fd42439b14..8ef33779f396e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/LogicalIndexNavigation.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/LogicalIndexNavigation.java @@ -29,9 +29,9 @@ import java.util.Objects; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class LogicalIndexNavigation { public static final LogicalIndexNavigation NO_OP = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregationTracker.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregationTracker.java index 64280ba60d3b5..5b9913ac134f4 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregationTracker.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregationTracker.java @@ -25,9 +25,9 @@ import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** * This class returns a set of positions to aggregate over for an aggregate function in row pattern diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregators.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregators.java index b0ed21701bee3..813c08e5c0ed0 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregators.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternAggregators.java @@ -24,7 +24,7 @@ import java.util.Arrays; import java.util.List; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class PatternAggregators { private PatternAggregator[][] values; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternPartitionExecutor.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternPartitionExecutor.java index 82637f82125ef..c86e347ab3ebd 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternPartitionExecutor.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/PatternPartitionExecutor.java @@ -31,8 +31,8 @@ import org.apache.iotdb.commons.exception.SemanticException; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.RowsPerMatch; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SkipToPosition; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; @@ -44,8 +44,8 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static java.lang.Math.max; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public final class PatternPartitionExecutor { private final int partitionStart; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/ArrayView.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/ArrayView.java index 5615ef140c19f..659bedff749a5 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/ArrayView.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/ArrayView.java @@ -20,13 +20,12 @@ package org.apache.iotdb.calc.execution.operator.process.rowpattern.matcher; import org.apache.iotdb.calc.i18n.CalcMessages; - -import com.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; import java.util.Arrays; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class ArrayView { public static final ArrayView EMPTY = new ArrayView(new int[] {}, 0); diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/IrRowPatternToProgramRewriter.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/IrRowPatternToProgramRewriter.java index f6073d1d373ef..57b8f9736548e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/IrRowPatternToProgramRewriter.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/IrRowPatternToProgramRewriter.java @@ -38,10 +38,10 @@ import java.util.Map; import java.util.stream.IntStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Collections2.orderedPermutations; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.Collections2.orderedPermutations; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class IrRowPatternToProgramRewriter { private IrRowPatternToProgramRewriter() {} diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/Program.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/Program.java index f43e5f876d565..662dc5d5f0f6c 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/Program.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/rowpattern/matcher/Program.java @@ -19,7 +19,7 @@ package org.apache.iotdb.calc.execution.operator.process.rowpattern.matcher; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/RowNumberOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/RowNumberOperator.java index e676c3a7da419..14a165c7397b5 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/RowNumberOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/RowNumberOperator.java @@ -25,9 +25,9 @@ import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory; import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.GroupByHash; import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TableWindowOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TableWindowOperator.java index bdbabcefa3d4b..ec738393b5d74 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TableWindowOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TableWindowOperator.java @@ -33,9 +33,9 @@ import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TopKRankingOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TopKRankingOperator.java index 1fea078a4e50e..fcb9c9d08444e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TopKRankingOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/TopKRankingOperator.java @@ -34,10 +34,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKRankingNode; import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.base.Suppliers; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/function/aggregate/WindowAggregator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/function/aggregate/WindowAggregator.java index aecdbbae8763b..784c710da9882 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/function/aggregate/WindowAggregator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/function/aggregate/WindowAggregator.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.execution.operator.source.relational.aggregation.TableAccumulator; import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; +import org.apache.iotdb.google.common.primitives.Ints; -import com.google.common.primitives.Ints; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/PartitionExecutor.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/PartitionExecutor.java index 5cfcf3ac1730a..b967e5fa129b6 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/PartitionExecutor.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/PartitionExecutor.java @@ -29,8 +29,8 @@ import org.apache.iotdb.calc.execution.operator.process.window.utils.Range; import org.apache.iotdb.calc.execution.operator.process.window.utils.RowComparator; import org.apache.iotdb.calc.i18n.CalcMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/GroupsFrame.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/GroupsFrame.java index f689e1273ce19..8daaa542ecb4a 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/GroupsFrame.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/GroupsFrame.java @@ -28,7 +28,7 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupsFrame implements Frame { private final Partition partition; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RangeFrame.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RangeFrame.java index cf59324bdd996..de5802d1df47a 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RangeFrame.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RangeFrame.java @@ -31,10 +31,10 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.process.window.partition.frame.FrameInfo.FrameBoundType.CURRENT_ROW; import static org.apache.iotdb.calc.execution.operator.process.window.partition.frame.FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING; import static org.apache.iotdb.calc.execution.operator.process.window.partition.frame.FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class RangeFrame implements Frame { private final Partition partition; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RowsFrame.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RowsFrame.java index 9860bc608df18..d5740b6cbfff0 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RowsFrame.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/process/window/partition/frame/RowsFrame.java @@ -24,7 +24,7 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.exception.SemanticException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class RowsFrame implements Frame { private final Partition partition; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/AbstractMergeSortJoinOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/AbstractMergeSortJoinOperator.java index bd12e7fc71ed5..afc567b7c0934 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/AbstractMergeSortJoinOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/AbstractMergeSortJoinOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.calc.execution.operator.process.join.merge.comparator.JoinKeyComparator; import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; @@ -37,8 +37,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import static com.google.common.util.concurrent.Futures.successfulAsList; import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.MAX_RESERVED_MEMORY; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; public abstract class AbstractMergeSortJoinOperator extends AbstractOperator { protected boolean leftFinished; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MarkDistinctOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MarkDistinctOperator.java index ee81140a65ee0..465819ff2eaaf 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MarkDistinctOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/MarkDistinctOperator.java @@ -27,10 +27,10 @@ import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory; import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.MarkDistinctHash; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.primitives.Ints; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java index 8e90bd8e6aedb..a4f47e8f62519 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AccumulatorFactory.java @@ -82,11 +82,11 @@ import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; import org.apache.iotdb.commons.queryengine.plan.udf.TableUDFUtils; import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.udf.api.IoTDBLocal; import org.apache.iotdb.udf.api.customizer.parameter.FunctionArguments; import org.apache.iotdb.udf.api.relational.AggregateFunction; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; @@ -106,8 +106,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isMeasurementColumn; import static org.apache.iotdb.calc.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isTimeColumn; @@ -115,6 +113,8 @@ import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction.FIRST_BY; import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction.LAST; import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction.LAST_BY; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; import static org.apache.tsfile.read.common.type.IntType.INT32; public class AccumulatorFactory { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationMask.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationMask.java index d9f93774a874e..3997d0c9c1492 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationMask.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationMask.java @@ -28,9 +28,9 @@ import java.util.Arrays; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public final class AggregationMask { private static final int[] NO_SELECTED_POSITIONS = new int[0]; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationOperator.java index 047894fa8bcb7..959f3f251cb7e 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AggregationOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; @@ -37,7 +37,7 @@ import java.util.List; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class AggregationOperator implements ProcessOperator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java index 4c5f15e4e04f1..f7a0ec6ec53ce 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/ApproxCountDistinctAccumulator.java @@ -32,8 +32,8 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate.HyperLogLog.DEFAULT_STANDARD_ERROR; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class ApproxCountDistinctAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java index 0c4834569b30b..3882d6e6784fa 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/AvgAccumulator.java @@ -33,7 +33,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class AvgAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAccumulator.java index 6e50c4cce300f..eb6438bd8c7f1 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAccumulator.java @@ -25,7 +25,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CountAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAllAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAllAccumulator.java index 5ca5fdcab127b..ca9f9066dc943 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAllAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/CountAllAccumulator.java @@ -26,7 +26,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CountAllAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java index fe2faa9ca57a6..02776238f90ca 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstAccumulator.java @@ -37,8 +37,8 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class FirstAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java index 22b6da926beab..7a96699a2f882 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/FirstByAccumulator.java @@ -36,8 +36,8 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class FirstByAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java index 5918a640c303e..9bbe7cdf3baf0 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastAccumulator.java @@ -37,8 +37,8 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class LastAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java index c5dfd41eacbb7..c7c3de6fc6403 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/LastByAccumulator.java @@ -34,8 +34,8 @@ import org.apache.tsfile.utils.TsPrimitiveType; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class LastByAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java index 37047146d011d..9adc777573cd8 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MaxAccumulator.java @@ -33,7 +33,7 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MaxAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java index 08f99b53b0358..3ecb6867f1473 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/MinAccumulator.java @@ -33,7 +33,7 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MinAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java index 98fee0156072f..b7c1576358f40 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/SumAccumulator.java @@ -28,7 +28,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class SumAccumulator implements TableAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableAggregator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableAggregator.java index e22641ab1e1e0..c4b67dad15cb4 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableAggregator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableAggregator.java @@ -22,8 +22,8 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; +import org.apache.iotdb.google.common.primitives.Ints; -import com.google.common.primitives.Ints; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; @@ -34,8 +34,8 @@ import java.util.List; import java.util.OptionalInt; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableAggregator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java index 82e1c04225c58..2fc79def95b93 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCentralMomentAccumulator.java @@ -30,7 +30,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableCentralMomentAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCorrelationAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCorrelationAccumulator.java index 8a99e89d71833..d7967b83fc90f 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCorrelationAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCorrelationAccumulator.java @@ -29,7 +29,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableCorrelationAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCovarianceAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCovarianceAccumulator.java index 0c07073c44344..2e5c0441a1063 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCovarianceAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableCovarianceAccumulator.java @@ -30,7 +30,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableCovarianceAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java index f26159d3fa2d5..d4cc52a93551f 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableMaxMinByBaseAccumulator.java @@ -36,9 +36,9 @@ import java.util.Collections; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.calcTypeSize; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeValue; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** max(x,y) returns the value of x associated with the maximum value of y over all input values. */ public abstract class TableMaxMinByBaseAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java index af9635a4f42b4..e6107f82dae65 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableModeAccumulator.java @@ -36,9 +36,9 @@ import java.util.HashMap; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.UNSUPPORTED_TYPE_MESSAGE; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeBinaryValue; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.bytesToBool; import static org.apache.tsfile.utils.BytesUtils.bytesToLongFromOffset; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableRegressionAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableRegressionAccumulator.java index 79f008eddc6a5..bbd0f33caa1e3 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableRegressionAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableRegressionAccumulator.java @@ -30,7 +30,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableRegressionAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableVarianceAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableVarianceAccumulator.java index 3b9c552d9546f..24d639eb493f6 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableVarianceAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/TableVarianceAccumulator.java @@ -34,7 +34,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableVarianceAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/UserDefinedAggregateFunctionAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/UserDefinedAggregateFunctionAccumulator.java index 53cd2987c6b7d..e8e0f08911389 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/UserDefinedAggregateFunctionAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/UserDefinedAggregateFunctionAccumulator.java @@ -42,7 +42,7 @@ import java.util.Arrays; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.rpc.TSStatusCode.EXECUTE_UDF_ERROR; public class UserDefinedAggregateFunctionAccumulator implements TableAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/HyperLogLog.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/HyperLogLog.java index 9da01acf70c33..3ee26c60839df 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/HyperLogLog.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/HyperLogLog.java @@ -21,10 +21,10 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.commons.exception.IoTDBRuntimeException; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.hash.HashFunction; +import org.apache.iotdb.google.common.hash.Hashing; -import com.google.common.base.Preconditions; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/SpaceSaving.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/SpaceSaving.java index 0ac134cfa0a14..74697332945c0 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/SpaceSaving.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/approximate/SpaceSaving.java @@ -19,7 +19,8 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.approximate; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; + import org.apache.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAggregator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAggregator.java index 21332ebbc67e5..3a63a1d504380 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAggregator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAggregator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; +import org.apache.iotdb.google.common.primitives.Ints; -import com.google.common.primitives.Ints; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; @@ -34,8 +34,8 @@ import java.util.List; import java.util.OptionalInt; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedAggregator { private final GroupedAccumulator accumulator; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java index e241f2c1d7b79..de40510f34e72 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedAvgAccumulator.java @@ -34,7 +34,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.doubleToBytes; import static org.apache.tsfile.utils.BytesUtils.longToBytes; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java index 2571c33a82492..7c5ef5da5bde3 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCentralMomentAccumulator.java @@ -37,7 +37,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedCentralMomentAccumulator implements GroupedAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCorrelationAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCorrelationAccumulator.java index fd73c66db7b21..465eac2078f24 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCorrelationAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCorrelationAccumulator.java @@ -36,7 +36,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedCorrelationAccumulator implements GroupedAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCovarianceAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCovarianceAccumulator.java index 5b615133d015a..6dea04fe34b10 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCovarianceAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedCovarianceAccumulator.java @@ -37,7 +37,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedCovarianceAccumulator implements GroupedAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java index 640d6a5a4739b..af53b2e1453fa 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstAccumulator.java @@ -39,7 +39,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.boolToBytes; import static org.apache.tsfile.utils.BytesUtils.doubleToBytes; import static org.apache.tsfile.utils.BytesUtils.floatToBytes; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java index 378f188cf7347..65833db253065 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedFirstByAccumulator.java @@ -39,7 +39,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.boolToBytes; import static org.apache.tsfile.utils.BytesUtils.doubleToBytes; import static org.apache.tsfile.utils.BytesUtils.floatToBytes; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java index abdd939b58251..005d248c982b8 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastAccumulator.java @@ -39,7 +39,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.boolToBytes; import static org.apache.tsfile.utils.BytesUtils.doubleToBytes; import static org.apache.tsfile.utils.BytesUtils.floatToBytes; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java index fd29bbdb376b7..98b7374bd2550 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedLastByAccumulator.java @@ -39,7 +39,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.boolToBytes; import static org.apache.tsfile.utils.BytesUtils.doubleToBytes; import static org.apache.tsfile.utils.BytesUtils.floatToBytes; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java index 18bf03c3dddc1..55f982bcb778f 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedMaxMinByBaseAccumulator.java @@ -42,7 +42,7 @@ import java.util.Collections; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.boolToBytes; import static org.apache.tsfile.utils.BytesUtils.doubleToBytes; import static org.apache.tsfile.utils.BytesUtils.floatToBytes; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java index a7560ebb2bfaf..6775d967e41aa 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedModeAccumulator.java @@ -39,9 +39,9 @@ import java.util.HashMap; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.UNSUPPORTED_TYPE_MESSAGE; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeBinaryValue; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.utils.BytesUtils.bytesToBool; import static org.apache.tsfile.utils.BytesUtils.bytesToLongFromOffset; import static org.apache.tsfile.utils.TsPrimitiveType.getByType; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedRegressionAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedRegressionAccumulator.java index 944307f41a718..0076768723844 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedRegressionAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedRegressionAccumulator.java @@ -37,7 +37,7 @@ import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedRegressionAccumulator implements GroupedAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java index d01acb8fd5e57..99f74c02e7df7 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedSumAccumulator.java @@ -29,7 +29,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedSumAccumulator implements GroupedAccumulator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedUserDefinedAggregateAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedUserDefinedAggregateAccumulator.java index 71b71f3ca96a8..f5ad5ac31a464 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedUserDefinedAggregateAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedUserDefinedAggregateAccumulator.java @@ -44,7 +44,7 @@ import java.util.Arrays; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.rpc.TSStatusCode.EXECUTE_UDF_ERROR; public class GroupedUserDefinedAggregateAccumulator implements GroupedAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedVarianceAccumulator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedVarianceAccumulator.java index 9596da3401b79..5fb989ab0760d 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedVarianceAccumulator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/GroupedVarianceAccumulator.java @@ -36,7 +36,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class GroupedVarianceAccumulator implements GroupedAccumulator { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/HashAggregationOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/HashAggregationOperator.java index 7a58000f5ab88..81d60bc1f1b53 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/HashAggregationOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/HashAggregationOperator.java @@ -28,9 +28,9 @@ import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.RamUsageEstimator; @@ -38,9 +38,9 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.CURRENT_USED_MEMORY; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory.NOOP; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class HashAggregationOperator extends AbstractOperator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingAggregationOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingAggregationOperator.java index 537884f0db952..930d2d85056e8 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingAggregationOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingAggregationOperator.java @@ -28,9 +28,9 @@ import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.primitives.Ints; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.read.common.block.TsBlock; @@ -46,8 +46,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class StreamingAggregationOperator extends AbstractOperator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingHashAggregationOperator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingHashAggregationOperator.java index b85a2d509737b..4f35663ced282 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingHashAggregationOperator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/StreamingHashAggregationOperator.java @@ -29,9 +29,9 @@ import org.apache.iotdb.calc.utils.datastructure.SortKey; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.primitives.Ints; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; @@ -44,11 +44,11 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.CURRENT_USED_MEMORY; import static org.apache.iotdb.calc.plan.planner.CommonOperatorUtils.MAX_USED_MEMORY; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory.NOOP; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class StreamingHashAggregationOperator extends AbstractOperator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/array/ShortBigArray.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/array/ShortBigArray.java index ce4b5bb69b8b9..1ff4391bda447 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/array/ShortBigArray.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/array/ShortBigArray.java @@ -19,7 +19,7 @@ package org.apache.iotdb.calc.execution.operator.source.relational.aggregation.grouped.array; -import com.google.common.primitives.Shorts; +import org.apache.iotdb.google.common.primitives.Shorts; import java.util.Arrays; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/builder/InMemoryHashAggregationBuilder.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/builder/InMemoryHashAggregationBuilder.java index 07e4cb9adbb47..cdd407479ccb2 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/builder/InMemoryHashAggregationBuilder.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/operator/source/relational/aggregation/grouped/builder/InMemoryHashAggregationBuilder.java @@ -26,9 +26,9 @@ import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.GroupByHash; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.primitives.Ints; -import com.google.common.collect.ImmutableList; -import com.google.common.primitives.Ints; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java index 15397f8c09064..cf0302226c581 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/relational/ColumnTransformerBuilder.java @@ -231,7 +231,6 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.plan.relational.metadata.CommonMetadataUtils.isBlobType; import static org.apache.iotdb.calc.plan.relational.metadata.CommonMetadataUtils.isCharType; import static org.apache.iotdb.calc.transformation.dag.column.FailFunctionColumnTransformer.FAIL_FUNCTION_NAME; @@ -243,6 +242,7 @@ import static org.apache.iotdb.calc.transformation.dag.util.CommonTransformUtils.isStringLiteral; import static org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager.getTSDataType; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toTypeSignature; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.read.common.type.BlobType.BLOB; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingQueue.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingQueue.java index fed7a6077a639..abaf4b4c23348 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingQueue.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingQueue.java @@ -20,8 +20,7 @@ package org.apache.iotdb.calc.execution.schedule.queue; import org.apache.iotdb.calc.i18n.CalcMessages; - -import com.google.common.base.Preconditions; +import org.apache.iotdb.google.common.base.Preconditions; /** * The base class of a special kind of blocking queue, which has these characters: diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingReserveQueue.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingReserveQueue.java index b1040e7c64a85..3c7be846d8e42 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingReserveQueue.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/execution/schedule/queue/IndexedBlockingReserveQueue.java @@ -20,8 +20,7 @@ package org.apache.iotdb.calc.execution.schedule.queue; import org.apache.iotdb.calc.i18n.CalcMessages; - -import com.google.common.base.Preconditions; +import org.apache.iotdb.google.common.base.Preconditions; /** * This class is different from IndexedBlockingQueue in that it will reserve space diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java index ed52de7958b0d..0dc172f3f60ed 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/TableOperatorGenerator.java @@ -171,14 +171,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Literal; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ListMultimap; import org.apache.iotdb.udf.api.IoTDBLocal; import org.apache.iotdb.udf.api.relational.TableFunction; import org.apache.iotdb.udf.api.relational.table.TableFunctionProcessorProvider; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ListMultimap; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.common.conf.TSFileDescriptor; @@ -210,10 +210,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.execution.operator.process.join.merge.MergeSortComparator.getComparatorForTable; import static org.apache.iotdb.calc.execution.operator.process.rowpattern.PhysicalValuePointer.CLASSIFIER; @@ -241,6 +237,10 @@ import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager.getTSDataType; import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction.getAggregationTypeByFuncName; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; import static org.apache.tsfile.read.common.type.LongType.INT64; import static org.apache.tsfile.read.common.type.StringType.STRING; import static org.apache.tsfile.read.common.type.TimestampType.TIMESTAMP; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Match.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Match.java index 9650a6e81bf7f..16e2793fb4df1 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Match.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Match.java @@ -22,8 +22,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class Match { public static Match of(Captures captures) { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Pattern.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Pattern.java index 5fb7f8bcdb6b3..8d611e020f792 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Pattern.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/relational/utils/matching/Pattern.java @@ -23,16 +23,15 @@ import org.apache.iotdb.calc.plan.relational.utils.matching.pattern.FilterPattern; import org.apache.iotdb.calc.plan.relational.utils.matching.pattern.TypeOfPattern; import org.apache.iotdb.calc.plan.relational.utils.matching.pattern.WithPattern; - -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Iterables; import java.util.Optional; import java.util.function.BiPredicate; import java.util.function.Predicate; import java.util.stream.Stream; -import static com.google.common.base.Predicates.not; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Predicates.not; public abstract class Pattern { private final Optional> previous; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/ColumnCache.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/ColumnCache.java index 6ada45d6d10e4..c0abd2819362f 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/ColumnCache.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/ColumnCache.java @@ -23,7 +23,7 @@ import org.apache.tsfile.block.column.Column; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class ColumnCache { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/factory/HmacStrategiesFactory.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/factory/HmacStrategiesFactory.java index 6d7fb46fb21b7..db950061758c5 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/factory/HmacStrategiesFactory.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/factory/HmacStrategiesFactory.java @@ -22,9 +22,8 @@ import org.apache.iotdb.calc.i18n.CalcMessages; import org.apache.iotdb.calc.transformation.dag.column.binary.strategies.HmacStrategy; import org.apache.iotdb.commons.exception.SemanticException; - -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; +import org.apache.iotdb.google.common.hash.HashFunction; +import org.apache.iotdb.google.common.hash.Hashing; public final class HmacStrategiesFactory { diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/factory/CodecStrategiesFactory.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/factory/CodecStrategiesFactory.java index 3a3964f14b22f..f6188bede9bd8 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/factory/CodecStrategiesFactory.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/unary/scalar/factory/CodecStrategiesFactory.java @@ -24,9 +24,9 @@ import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.util.HexUtils; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.util.SpookyHashV2Utils; import org.apache.iotdb.commons.exception.SemanticException; +import org.apache.iotdb.google.common.hash.Hashing; +import org.apache.iotdb.google.common.io.BaseEncoding; -import com.google.common.hash.Hashing; -import com.google.common.io.BaseEncoding; import net.jpountz.xxhash.XXHashFactory; import org.apache.tsfile.common.conf.TSFileConfig; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/datastructure/tv/SerializableTVList.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/datastructure/tv/SerializableTVList.java index 70d781c97623f..da82068481d41 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/datastructure/tv/SerializableTVList.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/datastructure/tv/SerializableTVList.java @@ -37,10 +37,10 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.calc.transformation.datastructure.util.BinaryUtils.MIN_ARRAY_HEADER_SIZE; import static org.apache.iotdb.calc.transformation.datastructure.util.BinaryUtils.MIN_OBJECT_HEADER_SIZE; import static org.apache.iotdb.commons.conf.IoTDBConstant.MB; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class SerializableTVList implements SerializableList { protected final SerializationRecorder serializationRecorder; diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/HeapTraversal.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/HeapTraversal.java index ac6875377e076..d31f13cacc4c3 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/HeapTraversal.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/utils/HeapTraversal.java @@ -23,8 +23,8 @@ import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class HeapTraversal { public enum Child { diff --git a/iotdb-core/confignode/pom.xml b/iotdb-core/confignode/pom.xml index 4bf643b871e67..162c2bc2b08e5 100644 --- a/iotdb-core/confignode/pom.xml +++ b/iotdb-core/confignode/pom.xml @@ -131,10 +131,6 @@ jakarta.validation jakarta.validation-api - - com.google.guava - guava - org.apache.thrift libthrift diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncAINodeClientPool.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncAINodeClientPool.java index c6886e91527e6..fcb615aa50a64 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncAINodeClientPool.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncAINodeClientPool.java @@ -26,9 +26,9 @@ import org.apache.iotdb.commons.client.sync.SyncAINodeClient; import org.apache.iotdb.commons.exception.UncheckedStartupException; import org.apache.iotdb.confignode.i18n.ConfigNodeMessages; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableMap; import org.apache.ratis.util.function.CheckedBiFunction; import org.apache.thrift.TException; import org.slf4j.Logger; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java index f35b6d906a525..77651d60db537 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java @@ -29,6 +29,7 @@ import org.apache.iotdb.commons.client.sync.SyncDataNodeInternalServiceClient; import org.apache.iotdb.commons.exception.UncheckedStartupException; import org.apache.iotdb.confignode.i18n.ConfigNodeMessages; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.mpp.rpc.thrift.TCleanDataNodeCacheReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; import org.apache.iotdb.mpp.rpc.thrift.TCreatePeerReq; @@ -45,7 +46,6 @@ import org.apache.iotdb.mpp.rpc.thrift.TUpdateTemplateReq; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableMap; import org.apache.ratis.util.function.CheckedBiFunction; import org.apache.thrift.TException; import org.slf4j.Logger; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java index ab561bb5df9fe..1cd82ac411901 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/externalservice/ExternalServiceManager.java @@ -47,7 +47,7 @@ import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class ExternalServiceManager { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/detector/PhiAccrualDetector.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/detector/PhiAccrualDetector.java index ca422aed50a0b..598f41e9e0baa 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/detector/PhiAccrualDetector.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/detector/PhiAccrualDetector.java @@ -24,9 +24,9 @@ import org.apache.iotdb.confignode.manager.load.cache.IFailureDetector; import org.apache.iotdb.confignode.manager.load.cache.node.NodeHeartbeatSample; import org.apache.iotdb.confignode.manager.load.cache.region.RegionHeartbeatSample; +import org.apache.iotdb.google.common.cache.Cache; +import org.apache.iotdb.google.common.cache.CacheBuilder; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; import org.apache.tsfile.utils.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/service/EventService.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/service/EventService.java index 5974e5ceb58c4..92ccde9b1de6e 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/service/EventService.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/service/EventService.java @@ -34,9 +34,9 @@ import org.apache.iotdb.confignode.manager.load.subscriber.IClusterStatusSubscriber; import org.apache.iotdb.confignode.manager.load.subscriber.NodeStatisticsChangeEvent; import org.apache.iotdb.confignode.manager.load.subscriber.RegionGroupStatisticsChangeEvent; +import org.apache.iotdb.google.common.eventbus.AsyncEventBus; +import org.apache.iotdb.google.common.eventbus.EventBus; -import com.google.common.eventbus.AsyncEventBus; -import com.google.common.eventbus.EventBus; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/subscriber/IClusterStatusSubscriber.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/subscriber/IClusterStatusSubscriber.java index 83f1dc1bc83b3..6a69405ff5162 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/subscriber/IClusterStatusSubscriber.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/subscriber/IClusterStatusSubscriber.java @@ -19,8 +19,8 @@ package org.apache.iotdb.confignode.manager.load.subscriber; -import com.google.common.eventbus.AllowConcurrentEvents; -import com.google.common.eventbus.Subscribe; +import org.apache.iotdb.google.common.eventbus.AllowConcurrentEvents; +import org.apache.iotdb.google.common.eventbus.Subscribe; public interface IClusterStatusSubscriber { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeConfigNodeRemainingTimeMetrics.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeConfigNodeRemainingTimeMetrics.java index 0e9c44f05d2ad..bf099f745e075 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeConfigNodeRemainingTimeMetrics.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeConfigNodeRemainingTimeMetrics.java @@ -24,12 +24,12 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.pipe.source.IoTDBConfigRegionSource; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeTemporaryMetaInCoordinatorMetrics.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeTemporaryMetaInCoordinatorMetrics.java index 19604e0d6c17b..2748a848aade7 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeTemporaryMetaInCoordinatorMetrics.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/overview/PipeTemporaryMetaInCoordinatorMetrics.java @@ -25,12 +25,12 @@ import org.apache.iotdb.commons.service.metric.enums.Metric; import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.confignode.i18n.ManagerMessages; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/sink/PipeConfigRegionSinkMetrics.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/sink/PipeConfigRegionSinkMetrics.java index 0ca45d4c3648d..2a84c6570a4c9 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/sink/PipeConfigRegionSinkMetrics.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/sink/PipeConfigRegionSinkMetrics.java @@ -23,13 +23,13 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.pipe.agent.task.PipeConfigNodeSubtask; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/source/PipeConfigRegionSourceMetrics.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/source/PipeConfigRegionSourceMetrics.java index 428b4b4026344..5d3cac72649bd 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/source/PipeConfigRegionSourceMetrics.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/metric/source/PipeConfigRegionSourceMetrics.java @@ -23,12 +23,12 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.pipe.source.IoTDBConfigRegionSource; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java index b5f9a021426b1..48d2100516dcf 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java @@ -29,8 +29,8 @@ import org.apache.iotdb.confignode.procedure.state.ProcedureLockState; import org.apache.iotdb.confignode.procedure.state.ProcedureState; import org.apache.iotdb.confignode.procedure.store.IProcedureStore; +import org.apache.iotdb.google.common.base.Preconditions; -import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/ConvertToThriftRespTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/ConvertToThriftRespTest.java index 8d264ed000942..119c02302f0c0 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/ConvertToThriftRespTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/ConvertToThriftRespTest.java @@ -37,11 +37,11 @@ import org.apache.iotdb.confignode.rpc.thrift.TGetTriggerTableResp; import org.apache.iotdb.confignode.rpc.thrift.TGetUDFTableResp; import org.apache.iotdb.confignode.rpc.thrift.TTriggerState; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.trigger.api.enums.FailureStrategy; import org.apache.iotdb.trigger.api.enums.TriggerEvent; -import com.google.common.collect.ImmutableList; import org.junit.Assert; import org.junit.Test; diff --git a/iotdb-core/consensus/pom.xml b/iotdb-core/consensus/pom.xml index 5d4b785ef73f0..e491f6509672f 100644 --- a/iotdb-core/consensus/pom.xml +++ b/iotdb-core/consensus/pom.xml @@ -124,10 +124,6 @@ junit test - - com.google.guava - guava - diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java index 3cab3498b36a0..4eb53d96a2333 100644 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java +++ b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java @@ -63,10 +63,10 @@ import org.apache.iotdb.consensus.iot.service.IoTConsensusRPCService; import org.apache.iotdb.consensus.iot.service.IoTConsensusRPCServiceProcessor; import org.apache.iotdb.consensus.iot.snapshot.IoTConsensusRateLimiter; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java index 4cb109f0eb137..556b5baca4999 100644 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java +++ b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java @@ -75,10 +75,10 @@ import org.apache.iotdb.consensus.iot.thrift.TWaitReleaseAllRegionRelatedResourceRes; import org.apache.iotdb.consensus.iot.thrift.TWaitSyncLogCompleteReq; import org.apache.iotdb.consensus.iot.thrift.TWaitSyncLogCompleteRes; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; import org.apache.thrift.TException; import org.apache.tsfile.external.commons.io.FileUtils; import org.slf4j.Logger; diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java index 4a20733156490..0babdc4c28ee9 100644 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java +++ b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java @@ -54,10 +54,10 @@ import org.apache.iotdb.consensus.i18n.IoTConsensusV2Messages; import org.apache.iotdb.consensus.pipe.service.IoTConsensusV2RPCService; import org.apache.iotdb.consensus.pipe.service.IoTConsensusV2RPCServiceProcessor; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2PeerManager.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2PeerManager.java index 9aab2cb1b40b0..589b165e129ae 100644 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2PeerManager.java +++ b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2PeerManager.java @@ -21,8 +21,8 @@ import org.apache.iotdb.consensus.common.Peer; import org.apache.iotdb.consensus.i18n.ConsensusMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/pom.xml b/iotdb-core/datanode/pom.xml index b615061fc745f..708e5de44895a 100644 --- a/iotdb-core/datanode/pom.xml +++ b/iotdb-core/datanode/pom.xml @@ -240,21 +240,6 @@ - - com.github.ben-manes.caffeine - caffeine - - - - org.checkerframework - checker-qual - - - - - com.google.guava - guava - com.google.code.gson gson diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/PasswordChangeAuditTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/PasswordChangeAuditTask.java index f8080ee4a2595..0840197dc3ee3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/PasswordChangeAuditTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/PasswordChangeAuditTask.java @@ -23,13 +23,12 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.FutureCallback; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.MoreExecutors; import org.apache.iotdb.rpc.RpcUtils; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; - import jakarta.validation.constraints.NotNull; /** Records the final result of a password-change config task without changing its execution. */ diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/UserRoleModificationAuditContext.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/UserRoleModificationAuditContext.java index ed7556b44cb0f..b87a58dbc1300 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/UserRoleModificationAuditContext.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/UserRoleModificationAuditContext.java @@ -29,14 +29,13 @@ import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; +import org.apache.iotdb.google.common.util.concurrent.FutureCallback; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.MoreExecutors; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; - import jakarta.validation.constraints.NotNull; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java index 0593db10840ba..439c76856a093 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java @@ -53,9 +53,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.apache.ratis.util.MemoizedSupplier; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java index 83965effec044..e0dc74bd59c83 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java @@ -22,9 +22,9 @@ import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java index 1dd61c5b60939..a81d774fcc6b6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java @@ -59,10 +59,10 @@ import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; import org.apache.iotdb.db.schemaengine.lease.MetadataLeaseManager; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java index 8193456eb1590..b46176042a001 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java @@ -28,8 +28,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; - -import com.google.common.util.concurrent.SettableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import java.util.Collection; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/partition/DataPartitionTableGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/partition/DataPartitionTableGenerator.java index ed85dda076175..bec7998a31ff6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/partition/DataPartitionTableGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/partition/DataPartitionTableGenerator.java @@ -33,8 +33,8 @@ import org.apache.iotdb.db.storageengine.dataregion.DataRegion; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.file.metadata.IDeviceID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java index 0a95465b50e45..8859038617153 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java @@ -43,14 +43,14 @@ import org.apache.iotdb.db.pipe.metric.processor.PipeProcessorMetrics; import org.apache.iotdb.db.pipe.processor.iotconsensusv2.IoTConsensusV2Processor; import org.apache.iotdb.db.storageengine.StorageEngine; +import org.apache.iotdb.google.common.util.concurrent.ListeningExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningScheduledExecutorService; import org.apache.iotdb.pipe.api.PipeProcessor; import org.apache.iotdb.pipe.api.event.Event; import org.apache.iotdb.pipe.api.event.dml.insertion.TabletInsertionEvent; import org.apache.iotdb.pipe.api.event.dml.insertion.TsFileInsertionEvent; import org.apache.iotdb.pipe.api.exception.PipeException; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; import org.apache.tsfile.external.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorker.java index bc967cbc2e9ea..47cf7965a0a02 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorker.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorker.java @@ -22,9 +22,9 @@ import org.apache.iotdb.commons.concurrent.WrappedRunnable; import org.apache.iotdb.commons.pipe.event.EnrichedEvent; import org.apache.iotdb.db.i18n.DataNodePipeMessages; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; import org.apache.iotdb.pipe.api.event.Event; -import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerManager.java index ac2dd2cd7b575..6dfb957d97984 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerManager.java @@ -21,9 +21,8 @@ import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil; import org.apache.iotdb.commons.pipe.config.PipeConfig; - -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/consensus/deletion/DeletionResourceManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/consensus/deletion/DeletionResourceManager.java index 98f447a4b12a3..37469aecc7e83 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/consensus/deletion/DeletionResourceManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/consensus/deletion/DeletionResourceManager.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.pipe.consensus.deletion.persist.PageCacheDeletionBuffer; import org.apache.iotdb.db.pipe.consensus.deletion.recover.DeletionReader; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.AbstractDeleteDataNode; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeDataNodeSinglePipeMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeDataNodeSinglePipeMetrics.java index b6efd64ed2c3c..c7de3edadb6ae 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeDataNodeSinglePipeMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeDataNodeSinglePipeMetrics.java @@ -28,12 +28,12 @@ import org.apache.iotdb.db.pipe.resource.tsfile.PipeTsFileResourceManager; import org.apache.iotdb.db.pipe.source.dataregion.IoTDBDataRegionSource; import org.apache.iotdb.db.pipe.source.schemaregion.IoTDBSchemaRegionSource; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeTsFileToTabletsMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeTsFileToTabletsMetrics.java index 1a958c7d3a608..1d522f09dd033 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeTsFileToTabletsMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/overview/PipeTsFileToTabletsMetrics.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.source.dataregion.IoTDBDataRegionSource; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Counter; @@ -31,7 +32,6 @@ import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/processor/PipeProcessorMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/processor/PipeProcessorMetrics.java index 97bb7f4634117..7bcc46c731d41 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/processor/PipeProcessorMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/processor/PipeProcessorMetrics.java @@ -23,13 +23,13 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.agent.task.subtask.processor.PipeProcessorSubtask; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionListenerMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionListenerMetrics.java index 526ec7df281d1..217f1a089479f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionListenerMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionListenerMetrics.java @@ -23,12 +23,12 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.source.schemaregion.SchemaRegionListeningQueue; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSinkMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSinkMetrics.java index 869b2616e609d..ac41ad3200ae6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSinkMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSinkMetrics.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.agent.task.subtask.sink.PipeSinkSubtask; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Histogram; @@ -30,7 +31,6 @@ import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSourceMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSourceMetrics.java index 459fa9fcaa3cf..3ce25870ea728 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSourceMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/schema/PipeSchemaRegionSourceMetrics.java @@ -23,12 +23,12 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.source.schemaregion.IoTDBSchemaRegionSource; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/sink/PipeDataRegionSinkMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/sink/PipeDataRegionSinkMetrics.java index e2a391e8f709f..7ee0912416b81 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/sink/PipeDataRegionSinkMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/sink/PipeDataRegionSinkMetrics.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.agent.task.subtask.sink.PipeSinkSubtask; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Histogram; @@ -31,7 +32,6 @@ import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeAssignerMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeAssignerMetrics.java index 3665ec2429497..2aa5708274d45 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeAssignerMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeAssignerMetrics.java @@ -23,12 +23,12 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.source.dataregion.realtime.assigner.PipeDataRegionAssigner; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeDataRegionSourceMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeDataRegionSourceMetrics.java index f44799e055097..f5beeae3b939d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeDataRegionSourceMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/metric/source/PipeDataRegionSourceMetrics.java @@ -24,6 +24,7 @@ import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.source.dataregion.IoTDBDataRegionSource; import org.apache.iotdb.db.pipe.source.dataregion.realtime.epoch.TsFileEpoch; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Gauge; @@ -31,7 +32,6 @@ import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/PartialPathLastObjectCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/PartialPathLastObjectCache.java index 225e0c3e86eb8..8695792bafd06 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/PartialPathLastObjectCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/PartialPathLastObjectCache.java @@ -23,10 +23,10 @@ import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager; import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryBlock; import org.apache.iotdb.db.utils.MemUtils; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Weigher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 8257dadfc9992..28ce2672ba508 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -112,13 +112,13 @@ import org.apache.iotdb.db.storageengine.load.util.LoadUtil; import org.apache.iotdb.db.tools.schema.SRStatementGenerator; import org.apache.iotdb.db.tools.schema.SchemaRegionSnapshotParser; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.pipe.api.exception.PipeException; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; import org.apache.iotdb.service.rpc.thrift.TPipeTransferResp; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/client/IoTDBDataNodeCacheLeaderClientManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/client/IoTDBDataNodeCacheLeaderClientManager.java index f32c8cb72bbd5..f9f0cb9aea9d3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/client/IoTDBDataNodeCacheLeaderClientManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/client/IoTDBDataNodeCacheLeaderClientManager.java @@ -24,10 +24,10 @@ import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager; import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryBlock; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Weigher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaKeyStoreLoader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaKeyStoreLoader.java index 2e27ffc22777c..07427023f04dc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaKeyStoreLoader.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaKeyStoreLoader.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.i18n.DataNodePipeMessages; +import org.apache.iotdb.google.common.net.InetAddresses; -import com.google.common.net.InetAddresses; import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaServerBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaServerBuilder.java index 5b7d820f8f7bb..677e1445b35d9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaServerBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/opcua/server/OpcUaServerBuilder.java @@ -20,9 +20,9 @@ package org.apache.iotdb.db.pipe.sink.protocol.opcua.server; import org.apache.iotdb.db.i18n.DataNodePipeMessages; +import org.apache.iotdb.google.common.net.InetAddresses; import org.apache.iotdb.pipe.api.exception.PipeException; -import com.google.common.net.InetAddresses; import org.eclipse.milo.opcua.sdk.server.EndpointConfig; import org.eclipse.milo.opcua.sdk.server.OpcUaServer; import org.eclipse.milo.opcua.sdk.server.OpcUaServerConfig; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/IoTDBDataRegionAsyncSink.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/IoTDBDataRegionAsyncSink.java index 422dd70976b44..4396fb34c11ea 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/IoTDBDataRegionAsyncSink.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/IoTDBDataRegionAsyncSink.java @@ -58,6 +58,7 @@ import org.apache.iotdb.db.pipe.sink.protocol.thrift.async.handler.PipeTransferTsFileHandler; import org.apache.iotdb.db.pipe.sink.protocol.thrift.sync.IoTDBDataRegionSyncSink; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.type.Histogram; import org.apache.iotdb.pipe.api.PipeConnector; import org.apache.iotdb.pipe.api.annotation.TableModel; @@ -74,7 +75,6 @@ import org.apache.iotdb.rpc.UrlUtils; import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.exception.write.WriteProcessException; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java index 6ce67ac200b59..7bb27ee1dfee8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java @@ -63,6 +63,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.pipe.PipeEnrichedStatement; import org.apache.iotdb.db.schemaengine.schemaregion.utils.MetaFormatUtils; import org.apache.iotdb.db.storageengine.dataregion.wal.exception.WALPipeException; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.pipe.api.PipeConnector; import org.apache.iotdb.pipe.api.annotation.TableModel; import org.apache.iotdb.pipe.api.annotation.TreeModel; @@ -78,7 +79,6 @@ import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java index e4da82c465ce3..90e37bc0fe45f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java @@ -72,7 +72,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.toStringHelper; import static org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_END_TIME_KEY; import static org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_MODS_DEFAULT_VALUE; import static org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_MODS_ENABLE_DEFAULT_VALUE; @@ -92,6 +91,7 @@ import static org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_START_TIME_KEY; import static org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_TSFILE_PARSER_KEY; import static org.apache.iotdb.commons.pipe.source.IoTDBSource.getSkipIfNoPrivileges; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public abstract class PipeRealtimeDataRegionSource implements PipeExtractor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/epoch/TsFileEpochManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/epoch/TsFileEpochManager.java index df050211626c9..a1edbe73fab25 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/epoch/TsFileEpochManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/epoch/TsFileEpochManager.java @@ -26,8 +26,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsNode; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.base.Functions; -import com.google.common.base.Functions; import org.apache.tsfile.file.metadata.IDeviceID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java index cde5b09b4578f..6edb93c9a1078 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java @@ -228,6 +228,7 @@ import org.apache.iotdb.db.trigger.executor.TriggerFireResult; import org.apache.iotdb.db.trigger.service.TriggerManagementService; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.metrics.type.AutoGauge; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.SystemMetric; @@ -347,7 +348,6 @@ import org.apache.iotdb.trigger.api.enums.FailureStrategy; import org.apache.iotdb.trigger.api.enums.TriggerEvent; -import com.google.common.collect.ImmutableList; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.transport.TIOStreamTransport; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java index 495ebc42b65cf..8e86a6fb3af3f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java @@ -50,8 +50,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.function.tvf.read_tsfile.ExternalTsFileQueryResource; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExplainOutputFormat; import org.apache.iotdb.db.queryengine.statistics.QueryPlanStatistics; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/PlanFragmentId.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/PlanFragmentId.java index fb4bffef7e7a0..8e98d10fc1973 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/PlanFragmentId.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/PlanFragmentId.java @@ -29,8 +29,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PlanFragmentId { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeader.java index 585aa7de85bab..9e90c1bc1b8bc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeader.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeader.java @@ -22,8 +22,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.OutputNode; import org.apache.iotdb.commons.schema.column.ColumnHeader; +import org.apache.iotdb.google.common.primitives.Bytes; -import com.google.common.primitives.Bytes; import org.apache.tsfile.enums.TSDataType; import java.util.ArrayList; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryIdGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryIdGenerator.java index e4afcd18120cb..6b7fe4e138acb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryIdGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryIdGenerator.java @@ -27,13 +27,13 @@ import java.time.Instant; import java.time.format.DateTimeFormatter; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; import static java.lang.String.format; import static java.time.ZoneOffset.UTC; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; public class QueryIdGenerator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryState.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryState.java index 386c11f9304ef..389ed170388d1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryState.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryState.java @@ -22,7 +22,7 @@ import java.util.Set; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public enum QueryState { QUEUED(false), diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachine.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachine.java index 1bc3e2b0f324d..9902ee0d039ee 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachine.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachine.java @@ -26,14 +26,12 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.db.queryengine.plan.execution.QueryExecution; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.rpc.RpcUtils; -import com.google.common.util.concurrent.ListenableFuture; - import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicReference; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.utils.ErrorHandlingCommonUtils.getRootCause; import static org.apache.iotdb.db.queryengine.execution.QueryState.ABORTED; @@ -45,6 +43,7 @@ import static org.apache.iotdb.db.queryengine.execution.QueryState.PLANNED; import static org.apache.iotdb.db.queryengine.execution.QueryState.QUEUED; import static org.apache.iotdb.db.queryengine.execution.QueryState.RUNNING; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * State machine for a {@link QueryExecution}. It stores the states for the {@link QueryExecution}. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AccumulatorFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AccumulatorFactory.java index 6f626a0269c8b..c13beb96bdc09 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AccumulatorFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AccumulatorFactory.java @@ -44,7 +44,7 @@ import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class AccumulatorFactory { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AvgAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AvgAccumulator.java index 27da8f412efc5..ec37030bdce0c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AvgAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/AvgAccumulator.java @@ -30,7 +30,7 @@ import org.apache.tsfile.utils.BitMap; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class AvgAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountAccumulator.java index 38851f62d0c08..e405e18f1bbc7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountAccumulator.java @@ -28,7 +28,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.BitMap; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CountAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountIfAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountIfAccumulator.java index 25ae28d9c52ab..76f0a33f631fa 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountIfAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountIfAccumulator.java @@ -28,7 +28,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.BitMap; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CountIfAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountTimeAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountTimeAccumulator.java index 897e98da2edf8..f7431ccd0f99e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountTimeAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/CountTimeAccumulator.java @@ -28,7 +28,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.BitMap; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CountTimeAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/ExtremeAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/ExtremeAccumulator.java index 597e09d98197f..b112d2cc781f2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/ExtremeAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/ExtremeAccumulator.java @@ -30,7 +30,7 @@ import org.apache.tsfile.utils.TsPrimitiveType; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class ExtremeAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/FirstValueAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/FirstValueAccumulator.java index 899a33d43c1f0..b29bedddd8e1e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/FirstValueAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/FirstValueAccumulator.java @@ -34,7 +34,7 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class FirstValueAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java index 3c04e6093a62b..c4857d6292a0b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/LastValueAccumulator.java @@ -34,7 +34,7 @@ import java.nio.charset.StandardCharsets; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class LastValueAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxMinByBaseAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxMinByBaseAccumulator.java index fad106ccf2857..41aa9cd7d8311 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxMinByBaseAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxMinByBaseAccumulator.java @@ -38,7 +38,7 @@ import java.io.IOException; import java.util.Collections; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** max(x,y) returns the value of x associated with the maximum value of y over all input values. */ public abstract class MaxMinByBaseAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxTimeAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxTimeAccumulator.java index 492ad1383d840..71e1b9d227420 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxTimeAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxTimeAccumulator.java @@ -28,7 +28,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.BitMap; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MaxTimeAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxValueAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxValueAccumulator.java index 825c51c802430..caf56105f0bad 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxValueAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MaxValueAccumulator.java @@ -31,7 +31,7 @@ import org.apache.tsfile.utils.TsPrimitiveType; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MaxValueAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinTimeAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinTimeAccumulator.java index 7e0bd2806761c..f52318bddcd21 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinTimeAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinTimeAccumulator.java @@ -28,7 +28,7 @@ import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.utils.BitMap; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MinTimeAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinValueAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinValueAccumulator.java index 9d35b77fb0728..5857d52e3f82f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinValueAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/MinValueAccumulator.java @@ -31,7 +31,7 @@ import org.apache.tsfile.utils.TsPrimitiveType; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MinValueAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/SumAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/SumAccumulator.java index 66928b24f9593..bc8fe2577ec46 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/SumAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/SumAccumulator.java @@ -30,7 +30,7 @@ import org.apache.tsfile.utils.BitMap; import org.apache.tsfile.write.UnSupportedDataTypeException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class SumAccumulator implements Accumulator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/TreeAggregator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/TreeAggregator.java index 41dee7116df61..3af131a46720f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/TreeAggregator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/TreeAggregator.java @@ -34,7 +34,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TreeAggregator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/UDAFAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/UDAFAccumulator.java index a4a8e1a81f90a..d681c0b66ae7e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/UDAFAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/UDAFAccumulator.java @@ -46,7 +46,7 @@ import java.util.Map; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class UDAFAccumulator implements Accumulator { private static final Logger LOGGER = LoggerFactory.getLogger(UDAFAccumulator.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/slidingwindow/SlidingWindowAggregator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/slidingwindow/SlidingWindowAggregator.java index 6d4fbbfc65e4f..f9c11d4c74686 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/slidingwindow/SlidingWindowAggregator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/aggregation/slidingwindow/SlidingWindowAggregator.java @@ -36,7 +36,7 @@ import java.util.List; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public abstract class SlidingWindowAggregator extends TreeAggregator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/DataDriver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/DataDriver.java index c8612d9b563cc..ff45ec9754afa 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/DataDriver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/DataDriver.java @@ -27,16 +27,15 @@ import org.apache.iotdb.db.queryengine.execution.operator.source.DataSourceOperator; import org.apache.iotdb.db.queryengine.plan.planner.plan.FragmentInstance; import org.apache.iotdb.db.storageengine.dataregion.read.IQueryDataSource; - -import com.google.common.util.concurrent.SettableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import javax.annotation.concurrent.NotThreadSafe; import java.util.List; -import static com.google.common.base.Throwables.throwIfUnchecked; import static org.apache.iotdb.calc.metric.QueryExecutionMetricSet.QUERY_RESOURCE_INIT; import static org.apache.iotdb.db.storageengine.dataregion.VirtualDataRegion.UNFINISHED_QUERY_DATA_SOURCE; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; /** * One {@link DataDriver} is responsible for one {@link FragmentInstance} which is for data query, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/Driver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/Driver.java index 3d76cfd84f492..f52ae41632d96 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/Driver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/Driver.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTaskId; import org.apache.iotdb.db.queryengine.metric.QueryMetricsManager; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import io.airlift.units.Duration; import org.apache.tsfile.read.common.block.TsBlock; import org.slf4j.Logger; @@ -50,14 +50,14 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Throwables.throwIfUnchecked; -import static com.google.common.base.Verify.verify; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.lang.Boolean.TRUE; import static org.apache.iotdb.calc.execution.operator.Operator.NOT_BLOCKED; import static org.apache.iotdb.calc.metric.QueryExecutionMetricSet.DRIVER_INTERNAL_PROCESS; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; +import static org.apache.iotdb.google.common.base.Verify.verify; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; public abstract class Driver implements IDriver { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/IDriver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/IDriver.java index b424e3645eade..ebf2d21ad9600 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/IDriver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/IDriver.java @@ -22,8 +22,8 @@ import org.apache.iotdb.db.queryengine.common.FragmentInstanceId; import org.apache.iotdb.db.queryengine.execution.exchange.sink.ISink; import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTaskId; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; /** diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/SchemaDriver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/SchemaDriver.java index bbdbccdad1b3a..9ca36bc119194 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/SchemaDriver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/driver/SchemaDriver.java @@ -21,8 +21,7 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.db.queryengine.plan.planner.plan.FragmentInstance; - -import com.google.common.util.concurrent.SettableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import javax.annotation.concurrent.NotThreadSafe; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueue.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueue.java index 5812d4e37cdd3..f420873301eaa 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueue.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueue.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.execution.memory.LocalMemoryManager; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool.MemoryReservationResult; import org.apache.iotdb.db.utils.CommonUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.Pair; @@ -46,7 +46,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; /** This is not thread safe class, the caller should ensure multi-threads safety. */ @NotThreadSafe diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ISink.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ISink.java index 5ee71abb1cfde..32c05111dac3a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ISink.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ISink.java @@ -20,9 +20,9 @@ package org.apache.iotdb.db.queryengine.execution.exchange.sink; import org.apache.iotdb.db.queryengine.execution.driver.Driver; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.Accountable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/LocalSinkChannel.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/LocalSinkChannel.java index 90f213f40b408..3782281e05256 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/LocalSinkChannel.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/LocalSinkChannel.java @@ -23,9 +23,9 @@ import org.apache.iotdb.db.queryengine.execution.exchange.MPPDataExchangeManager.SinkListener; import org.apache.iotdb.db.queryengine.execution.exchange.SharedTsBlockQueue; import org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; @@ -35,9 +35,9 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; -import static com.google.common.util.concurrent.Futures.immediateVoidFuture; -import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.SINK_HANDLE_SEND_TSBLOCK_LOCAL; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.nonCancellationPropagating; public class LocalSinkChannel implements ISinkChannel { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ShuffleSinkHandle.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ShuffleSinkHandle.java index 78e4928fdc3ed..beef9edf4d5ff 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ShuffleSinkHandle.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/ShuffleSinkHandle.java @@ -23,9 +23,9 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.execution.exchange.MPPDataExchangeManager; import org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java index daaff9fdbcd29..3df3c35c6d064 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java @@ -33,12 +33,13 @@ import org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet; import org.apache.iotdb.db.queryengine.metric.DataExchangeCountMetricSet; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TEndOfDataBlockEvent; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; import org.apache.iotdb.mpp.rpc.thrift.TNewDataBlockEvent; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; + import org.apache.thrift.TApplicationException; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.external.commons.lang3.Validate; @@ -58,12 +59,12 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import static com.google.common.util.concurrent.Futures.immediateVoidFuture; -import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; import static org.apache.iotdb.db.queryengine.common.FragmentInstanceId.createFullId; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.SEND_NEW_DATA_BLOCK_EVENT_TASK_CALLER; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.SINK_HANDLE_SEND_TSBLOCK_REMOTE; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCountMetricSet.SEND_NEW_DATA_BLOCK_NUM_CALLER; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.nonCancellationPropagating; public class SinkChannel implements ISinkChannel { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/ISourceHandle.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/ISourceHandle.java index 32e5557b8f922..ca92158ab63a5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/ISourceHandle.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/ISourceHandle.java @@ -20,9 +20,9 @@ package org.apache.iotdb.db.queryengine.execution.exchange.source; import org.apache.iotdb.commons.exception.IoTDBException; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.Accountable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/LocalSourceHandle.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/LocalSourceHandle.java index 544421712669a..5f7e5753a3545 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/LocalSourceHandle.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/LocalSourceHandle.java @@ -26,10 +26,10 @@ import org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet; import org.apache.iotdb.db.utils.CommonUtils; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.TsBlockSerde; @@ -41,10 +41,10 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; -import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; import static org.apache.iotdb.db.queryengine.execution.exchange.MPPDataExchangeManager.createFullIdFrom; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.SOURCE_HANDLE_DESERIALIZE_TSBLOCK_LOCAL; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.SOURCE_HANDLE_GET_TSBLOCK_LOCAL; +import static org.apache.iotdb.google.common.util.concurrent.Futures.nonCancellationPropagating; public class LocalSourceHandle implements ISourceHandle { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java index 2cd33b2d769a6..65a7219c39e96 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java @@ -32,14 +32,14 @@ import org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet; import org.apache.iotdb.db.queryengine.metric.DataExchangeCountMetricSet; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.mpp.rpc.thrift.TAcknowledgeDataBlockEvent; import org.apache.iotdb.mpp.rpc.thrift.TCloseSinkChannelEvent; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; import org.apache.iotdb.mpp.rpc.thrift.TGetDataBlockRequest; import org.apache.iotdb.mpp.rpc.thrift.TGetDataBlockResponse; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.TsBlockSerde; @@ -56,7 +56,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; import static org.apache.iotdb.db.queryengine.execution.exchange.MPPDataExchangeManager.createFullIdFrom; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.GET_DATA_BLOCK_TASK_CALLER; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.ON_ACKNOWLEDGE_DATA_BLOCK_EVENT_TASK_CALLER; @@ -64,6 +63,7 @@ import static org.apache.iotdb.db.queryengine.metric.DataExchangeCostMetricSet.SOURCE_HANDLE_GET_TSBLOCK_REMOTE; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCountMetricSet.GET_DATA_BLOCK_NUM_CALLER; import static org.apache.iotdb.db.queryengine.metric.DataExchangeCountMetricSet.ON_ACKNOWLEDGE_DATA_BLOCK_NUM_CALLER; +import static org.apache.iotdb.google.common.util.concurrent.Futures.nonCancellationPropagating; public class SourceHandle implements ISourceHandle { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/DataNodeQueryContext.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/DataNodeQueryContext.java index 3e3b8beee0187..90130016ef23b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/DataNodeQueryContext.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/DataNodeQueryContext.java @@ -44,7 +44,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class DataNodeQueryContext { // Used for TreeModel, left of Pair is DataNodeSeriesScanNum, right of Pair is the last value diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceFailureInfo.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceFailureInfo.java index 84465e4ca75de..dbe39e983ce22 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceFailureInfo.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceFailureInfo.java @@ -20,8 +20,8 @@ package org.apache.iotdb.db.queryengine.execution.fragment; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.PublicBAOS; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -37,8 +37,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * This class is inspired by Trino implements SourceOperator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountMergeOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountMergeOperator.java index ab740a31ea193..0389c8ffe3086 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountMergeOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountMergeOperator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.execution.operator.process.ProcessOperator; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; @@ -36,7 +36,7 @@ import java.util.List; import java.util.NoSuchElementException; -import static com.google.common.util.concurrent.Futures.successfulAsList; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; public class CountMergeOperator implements ProcessOperator { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodeManageMemoryMergeOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodeManageMemoryMergeOperator.java index 84ae7672e913e..1b4678c4c2be5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodeManageMemoryMergeOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodeManageMemoryMergeOperator.java @@ -28,8 +28,8 @@ import org.apache.iotdb.commons.schema.node.MNodeType; import org.apache.iotdb.db.i18n.DataNodeSchemaMessages; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsConvertOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsConvertOperator.java index dbb92233d1b7e..b94ca4b33e60f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsConvertOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsConvertOperator.java @@ -28,8 +28,8 @@ import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsCountOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsCountOperator.java index ff8c4e140355f..669470e560e00 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsCountOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/NodePathsCountOperator.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.db.i18n.DataNodeSchemaMessages; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java index 4cafe40c36b1c..9c2ba3d44b912 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java @@ -34,8 +34,8 @@ import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.IDeviceSchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ISchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaFetchMergeOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaFetchMergeOperator.java index 0fc3d5647a4dc..88eb023cf27f6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaFetchMergeOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaFetchMergeOperator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.execution.operator.process.ProcessOperator; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.BinaryColumn; import org.apache.tsfile.read.common.block.column.TimeColumn; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryMergeOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryMergeOperator.java index 999db03329b40..e86d6f38e842f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryMergeOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryMergeOperator.java @@ -23,8 +23,8 @@ import org.apache.iotdb.calc.execution.operator.process.ProcessOperator; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryOrderByHeatOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryOrderByHeatOperator.java index c5cb23a0cb150..3cd874052daa0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryOrderByHeatOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryOrderByHeatOperator.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.db.i18n.DataNodeSchemaMessages; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; @@ -43,8 +43,8 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; -import static com.google.common.util.concurrent.Futures.successfulAsList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.util.concurrent.Futures.successfulAsList; public class SchemaQueryOrderByHeatOperator implements ProcessOperator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperator.java index 493e58eea6f0e..47c49306dc817 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperator.java @@ -30,9 +30,9 @@ import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ISchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; @@ -43,7 +43,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; public class SchemaQueryScanOperator implements SourceOperator { private static final long MAX_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/PathsUsingTemplateSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/PathsUsingTemplateSource.java index fb5a08fcecca7..07ac4f1f53969 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/PathsUsingTemplateSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/PathsUsingTemplateSource.java @@ -28,8 +28,8 @@ import org.apache.iotdb.db.schemaengine.schemaregion.read.req.SchemaRegionReadPlanFactory; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.IDeviceSchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java index 5492f63218d2e..a8491ea0d026c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java @@ -37,8 +37,8 @@ import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileConfig; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/IdentitySinkOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/IdentitySinkOperator.java index 48f14ce1c2df0..1d779677d9aeb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/IdentitySinkOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/IdentitySinkOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.db.queryengine.execution.exchange.sink.DownStreamChannelIndex; import org.apache.iotdb.db.queryengine.execution.exchange.sink.ISinkHandle; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/ShuffleHelperOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/ShuffleHelperOperator.java index 5e27908ad10ea..18fa5aba70326 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/ShuffleHelperOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/sink/ShuffleHelperOperator.java @@ -24,8 +24,8 @@ import org.apache.iotdb.db.queryengine.execution.exchange.sink.DownStreamChannelIndex; import org.apache.iotdb.db.queryengine.execution.exchange.sink.ISinkHandle; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ExchangeOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ExchangeOperator.java index 7f405618944ca..8ee09eca4bcca 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ExchangeOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ExchangeOperator.java @@ -23,9 +23,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.db.queryengine.execution.exchange.source.ISourceHandle; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/FileLoaderUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/FileLoaderUtils.java index 023dd69fd576d..1b8c8ff26943e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/FileLoaderUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/FileLoaderUtils.java @@ -64,7 +64,7 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class FileLoaderUtils { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/SeriesScanUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/SeriesScanUtil.java index 1b8ca2b1020c1..ee23c0481588c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/SeriesScanUtil.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/SeriesScanUtil.java @@ -95,9 +95,9 @@ import java.util.PriorityQueue; import java.util.function.ToLongFunction; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.metric.SeriesScanCostMetricSet.BUILD_TSBLOCK_FROM_MERGE_READER_ALIGNED; import static org.apache.iotdb.db.queryengine.metric.SeriesScanCostMetricSet.BUILD_TSBLOCK_FROM_MERGE_READER_NONALIGNED; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class SeriesScanUtil implements Accountable { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/DeviceIteratorScanOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/DeviceIteratorScanOperator.java index dea74163d0ddc..5f57423f3c971 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/DeviceIteratorScanOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/DeviceIteratorScanOperator.java @@ -28,8 +28,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; import org.apache.iotdb.db.storageengine.dataregion.read.IQueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.Accountable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/LastQueryAggTableScanOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/LastQueryAggTableScanOperator.java index dc95221f0a95e..111ddd298183c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/LastQueryAggTableScanOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/LastQueryAggTableScanOperator.java @@ -51,11 +51,11 @@ import java.util.OptionalLong; import java.util.concurrent.TimeUnit; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.calc.execution.operator.source.relational.aggregation.Utils.serializeTimeValueWithNull; import static org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager.getTSDataType; import static org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableDeviceLastCache.PLACEHOLDER_EMPTY_COLUMN; import static org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableDeviceLastCache.PLACEHOLDER_NO_VALUE; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** * This class is used to execute aggregation table scan when apply {@code canUseLastCacheOptimize()} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeNonAlignedDeviceViewAggregationScanOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeNonAlignedDeviceViewAggregationScanOperator.java index 0e89c2c6cd077..ebf37e3bb0b1d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeNonAlignedDeviceViewAggregationScanOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeNonAlignedDeviceViewAggregationScanOperator.java @@ -27,8 +27,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; import org.apache.iotdb.db.storageengine.dataregion.read.IQueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeToTableViewAdaptorOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeToTableViewAdaptorOperator.java index 0a2ad64477254..a9ff1df3452dc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeToTableViewAdaptorOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/TreeToTableViewAdaptorOperator.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.metadata.ColumnSchema; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverScheduler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverScheduler.java index 0485de570fe5b..da06437e103d0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverScheduler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverScheduler.java @@ -47,10 +47,10 @@ import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTaskStatus; import org.apache.iotdb.db.storageengine.rescon.quotas.DataNodeThrottleQuotaManager; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.util.concurrent.MoreExecutors; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.SettableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskThread.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskThread.java index 912bd6711e34e..a70941936132f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskThread.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskThread.java @@ -27,9 +27,9 @@ import org.apache.iotdb.db.queryengine.execution.schedule.queue.multilevelqueue.MultilevelPriorityQueue; import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTask; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.base.Ticker; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.base.Ticker; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import java.util.concurrent.Executor; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/queue/multilevelqueue/MultilevelPriorityQueue.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/queue/multilevelqueue/MultilevelPriorityQueue.java index 7cd09fd547217..1bcab2f4c805f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/queue/multilevelqueue/MultilevelPriorityQueue.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/queue/multilevelqueue/MultilevelPriorityQueue.java @@ -27,10 +27,10 @@ import java.util.PriorityQueue; import java.util.concurrent.atomic.AtomicLong; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** * This class is inspired by Trino. { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/GroupByLevelHelper.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/GroupByLevelHelper.java index 1f7465bf1b870..f5672f7dafaf2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/GroupByLevelHelper.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/GroupByLevelHelper.java @@ -36,10 +36,10 @@ import java.util.Map; import java.util.Set; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.analyze.AnalyzeVisitor.analyzeExpressionType; import static org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.normalizeExpression; import static org.apache.iotdb.db.queryengine.plan.analyze.ExpressionAnalyzer.searchAggregationExpressions; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class GroupByLevelHelper { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TypeProvider.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TypeProvider.java index d08bb6c6f3ce7..a513c06d4a94e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TypeProvider.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/TypeProvider.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.utils.TypeUtil; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -38,9 +38,9 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand.TIMESTAMP_EXPRESSION_STRING; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TypeProvider implements ITableTypeProvider { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java index 4b8ee72ffd484..afbafe3afee85 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java @@ -64,10 +64,10 @@ import org.apache.iotdb.db.schemaengine.lease.MetadataLeaseManager; import org.apache.iotdb.db.schemaengine.schemaregion.utils.MetaUtils; import org.apache.iotdb.db.service.metrics.CacheMetrics; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; import org.apache.iotdb.rpc.TSStatusCode; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.thrift.TException; import org.apache.tsfile.file.metadata.IDeviceID; import org.slf4j.Logger; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/DataNodeDevicePathCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/DataNodeDevicePathCache.java index 45b6e468b5191..e351d1da19d79 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/DataNodeDevicePathCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/schema/DataNodeDevicePathCache.java @@ -25,10 +25,9 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.conf.DataNodeMemoryConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; - -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Weigher; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; /** This cache is for reducing duplicated DeviceId PartialPath initialization in write process. */ public class DataNodeDevicePathCache { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java index 322a86acfd3b3..926cad1c690d8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileTableSchemaCache.java @@ -50,9 +50,9 @@ import org.apache.iotdb.db.storageengine.load.memory.LoadTsFileMemoryManager; import org.apache.iotdb.db.utils.ModificationUtils; import org.apache.iotdb.db.utils.datastructure.PatternTreeMapFactory; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.read.TsFileSequenceReader; import org.apache.tsfile.utils.Pair; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java index 035bac7b5d879..fc39294fa93af 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java @@ -50,12 +50,12 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeUtil; import org.apache.iotdb.db.queryengine.plan.scheduler.IScheduler; import org.apache.iotdb.db.utils.SetThreadName; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,11 +70,11 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Throwables.throwIfUnchecked; import static org.apache.iotdb.calc.metric.QueryExecutionMetricSet.WAIT_FOR_RESULT; import static org.apache.iotdb.commons.utils.ErrorHandlingCommonUtils.getRootCause; import static org.apache.iotdb.db.queryengine.common.DataNodeEndPoints.isSameNode; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; import static org.apache.iotdb.rpc.TSStatusCode.DATE_OUT_OF_RANGE; /** diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java index 148dab7de71fe..a91f33b5a4d8f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java @@ -34,14 +34,14 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.ClusterConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.google.common.util.concurrent.FutureCallback; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.TsBlockSerde; import org.slf4j.Logger; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/IConfigTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/IConfigTask.java index dd1a2aee5b236..0f8eae270fa1d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/IConfigTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/IConfigTask.java @@ -20,8 +20,7 @@ package org.apache.iotdb.db.queryengine.plan.execution.config; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public interface IConfigTask { ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 65d4b3f102d5e..87ec734608a1b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -347,6 +347,8 @@ import org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.CompactionScheduleTaskManager; import org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.CompactionTaskManager; import org.apache.iotdb.db.trigger.service.TriggerClassLoader; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.pipe.api.PipePlugin; import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters; import org.apache.iotdb.pipe.api.exception.PipeParameterNotValidException; @@ -365,8 +367,6 @@ import org.apache.iotdb.udf.api.relational.table.specification.ParameterSpecification; import org.apache.iotdb.udf.api.relational.table.specification.ScalarParameterSpecification; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.SettableFuture; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransportException; import org.apache.tsfile.common.conf.TSFileDescriptor; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index 964b1c527792b..598282b19c66f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -101,10 +101,10 @@ import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.SetThrottleQuotaStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.ShowSpaceQuotaStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.ShowThrottleQuotaStatement; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; import org.apache.iotdb.service.rpc.thrift.TPipeTransferResp; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterEncodingCompressorTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterEncodingCompressorTask.java index 21a878397bf6b..a5fd80e3118ce 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterEncodingCompressorTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterEncodingCompressorTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.AlterEncodingCompressorStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterEncodingCompressorTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterTimeSeriesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterTimeSeriesTask.java index c9b9861cf2459..313b40a7ff3f6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterTimeSeriesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/AlterTimeSeriesTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.AlterTimeSeriesStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterTimeSeriesTask implements IConfigTask { private final String queryId; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountDatabaseTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountDatabaseTask.java index 720e2511dd032..d185756b8feb6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountDatabaseTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountDatabaseTask.java @@ -26,10 +26,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountDatabaseStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountTimeSlotListTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountTimeSlotListTask.java index 3b184b8834998..86189c2c996f3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountTimeSlotListTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CountTimeSlotListTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountTimeSlotListStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateContinuousQueryTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateContinuousQueryTask.java index 619ebf55e781e..c7a32d4d2f932 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateContinuousQueryTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateContinuousQueryTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateContinuousQueryStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateContinuousQueryTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateFunctionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateFunctionTask.java index 5bd093a1fdec0..88c392ca9875e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateFunctionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateFunctionTask.java @@ -25,11 +25,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateFunctionStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.udf.api.UDF; import org.apache.iotdb.udf.api.relational.SQLFunction; -import com.google.common.util.concurrent.ListenableFuture; - import java.util.Optional; public class CreateFunctionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreatePipePluginTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreatePipePluginTask.java index 843fb031a5c4c..52a258f5c7229 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreatePipePluginTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreatePipePluginTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipePlugin; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.CreatePipePluginStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreatePipePluginTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateTriggerTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateTriggerTask.java index 6839b4adac4c8..d551b8550c2ab 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateTriggerTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/CreateTriggerTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateTriggerStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateTriggerTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DatabaseSchemaTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DatabaseSchemaTask.java index 31c96887600c5..be02abca2510a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DatabaseSchemaTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DatabaseSchemaTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DatabaseSchemaStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DatabaseSchemaTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteStorageGroupTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteStorageGroupTask.java index 94c8645f6ced2..470c5aa1cd248 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteStorageGroupTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteStorageGroupTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteDatabaseStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DeleteStorageGroupTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteTimeSeriesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteTimeSeriesTask.java index 2d8cf754ae7d6..83fb231539a99 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteTimeSeriesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DeleteTimeSeriesTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteTimeSeriesStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DeleteTimeSeriesTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropContinuousQueryTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropContinuousQueryTask.java index ea7a36fbad5b7..2382335d7636b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropContinuousQueryTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropContinuousQueryTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DropContinuousQueryStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropContinuousQueryTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropFunctionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropFunctionTask.java index 9eff17015990f..69116344f3fc0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropFunctionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropFunctionTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropFunctionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropPipePluginTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropPipePluginTask.java index 7cd106e1713de..8e29164ceb156 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropPipePluginTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropPipePluginTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipePlugin; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.DropPipePluginStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropPipePluginTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropTriggerTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropTriggerTask.java index 950d3fc53e4de..852a6915803ce 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropTriggerTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/DropTriggerTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DropTriggerStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropTriggerTask implements IConfigTask { private final String triggerName; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetRegionIdTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetRegionIdTask.java index 0f305bf6d4e26..41bb5fa8e65a5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetRegionIdTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetRegionIdTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.GetRegionIdStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetSeriesSlotListTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetSeriesSlotListTask.java index 0908e928b01b2..c58283aa8c2ea 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetSeriesSlotListTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetSeriesSlotListTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.GetSeriesSlotListStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetTimeSlotListTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetTimeSlotListTask.java index 2e0037e91e07a..3a5eb9d1025cf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetTimeSlotListTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/GetTimeSlotListTask.java @@ -31,10 +31,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.GetTimeSlotListStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveAINodeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveAINodeTask.java index 2927aa07211fc..93e658c8ec32a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveAINodeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveAINodeTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveAINodeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RemoveAINodeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveConfigNodeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveConfigNodeTask.java index aa11f7fad52f9..1a63509b76814 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveConfigNodeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveConfigNodeTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveConfigNodeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RemoveConfigNodeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveDataNodeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveDataNodeTask.java index 5a91da21f628f..beb9b9315ec72 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveDataNodeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/RemoveDataNodeTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.RemoveDataNodeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RemoveDataNodeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/SetTTLTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/SetTTLTask.java index 6caa0accae1cf..d2f10f9abff66 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/SetTTLTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/SetTTLTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.SetTTLStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetTTLTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAINodesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAINodesTask.java index d331d35c7d7c3..c8b3ce131b248 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAINodesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAINodesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAvailableUrlsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAvailableUrlsTask.java index 6d957769b4428..75f75a0acb61f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAvailableUrlsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowAvailableUrlsTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java index 17778269b7ea1..fdd572fbd9ad7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowClusterStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterIdTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterIdTask.java index c43aa7f03c564..8f83263d38d99 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterIdTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterIdTask.java @@ -26,10 +26,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java index 8837864ab1390..1971e93a51cad 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowClusterStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowConfigNodesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowConfigNodesTask.java index a666a72d81539..50d9813588f99 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowConfigNodesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowConfigNodesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowContinuousQueriesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowContinuousQueriesTask.java index 8a1140c3767d1..bb0ed302cccc4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowContinuousQueriesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowContinuousQueriesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDataNodesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDataNodesTask.java index 1adb6e68cae67..f10466799a4cb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDataNodesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDataNodesTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowDataNodesStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDatabaseTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDatabaseTask.java index 34330c28c43f0..1a897f841accf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDatabaseTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowDatabaseTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowDatabaseStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class ShowDatabaseTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowFunctionsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowFunctionsTask.java index 9866b77cf13ca..22a5a196ff270 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowFunctionsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowFunctionsTask.java @@ -38,10 +38,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.function.DataNodeTableBuiltinTableFunction; import org.apache.iotdb.db.queryengine.plan.udf.TreeUDFUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowPipePluginsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowPipePluginsTask.java index bffc9fe43d9fc..e2e7022af23da 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowPipePluginsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowPipePluginsTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowPipePlugins; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.ShowPipePluginsStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowRegionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowRegionTask.java index 83289ba8cded3..307137d0433c8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowRegionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowRegionTask.java @@ -34,10 +34,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowRegionStatement; import org.apache.iotdb.db.utils.MathUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTTLTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTTLTask.java index 148394a1d9c22..8e9ed22d0420c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTTLTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTTLTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.ShowTTLStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTriggersTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTriggersTask.java index be6ebb86875cd..ff8711216f83c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTriggersTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowTriggersTask.java @@ -27,11 +27,11 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.trigger.api.enums.TriggerType; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowVariablesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowVariablesTask.java index d4f8adf23a5af..f7c389a499a26 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowVariablesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowVariablesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java index 6f488a0e7bf51..be64e84dc1b43 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateModelTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateModelTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateTuningTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateTuningTask.java index a66b7e700b758..1994e3a01701a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateTuningTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/CreateTuningTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/DropModelTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/DropModelTask.java index 688e413cf8597..8a62cdb210593 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/DropModelTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/DropModelTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropModelTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/LoadModelTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/LoadModelTask.java index eac0c73747708..13d8c7dce37c1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/LoadModelTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/LoadModelTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowAIDevicesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowAIDevicesTask.java index 3ccad1e24d5e0..dfb025ec332cf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowAIDevicesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowAIDevicesTask.java @@ -27,10 +27,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowLoadedModelsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowLoadedModelsTask.java index c8c6f8938f759..677507c2cf595 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowLoadedModelsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowLoadedModelsTask.java @@ -27,10 +27,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowModelsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowModelsTask.java index c0d7f4ef20309..ea69684cf25b0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowModelsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/ShowModelsTask.java @@ -27,10 +27,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/UnloadModelTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/UnloadModelTask.java index 528a6c493736a..84e194a23c3d6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/UnloadModelTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ai/UnloadModelTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java index 948609333fa80..0341f5251a1d0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/CreateExternalServiceTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService; import org.apache.iotdb.db.queryengine.plan.statement.metadata.externalservice.CreateExternalServiceStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Locale; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java index 7772af7b2a3d8..15219bb9402db 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/DropExternalServiceTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Locale; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java index c2c8352b6b0ac..55338a833d333 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/ShowExternalServiceTask.java @@ -24,8 +24,8 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java index 1382269084d2c..dafcb4668a1d2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StartExternalServiceTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Locale; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java index 671cad38965d2..3a2c6a5213cfb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/externalservice/StopExternalServiceTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Locale; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ExtendRegionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ExtendRegionTask.java index 8635c085ee1a1..9be0a19d9a55a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ExtendRegionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ExtendRegionTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExtendRegion; import org.apache.iotdb.db.queryengine.plan.statement.metadata.region.ExtendRegionStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class ExtendRegionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/MigrateRegionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/MigrateRegionTask.java index d99f9a36c164e..f49f57627ee16 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/MigrateRegionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/MigrateRegionTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.MigrateRegion; import org.apache.iotdb.db.queryengine.plan.statement.metadata.region.MigrateRegionStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class MigrateRegionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ReconstructRegionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ReconstructRegionTask.java index e1d7127f749e7..8ff3cba10b261 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ReconstructRegionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/ReconstructRegionTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ReconstructRegion; import org.apache.iotdb.db.queryengine.plan.statement.metadata.region.ReconstructRegionStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class ReconstructRegionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/RemoveRegionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/RemoveRegionTask.java index e1d32bff98626..c1f7b3b98f68c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/RemoveRegionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/region/RemoveRegionTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveRegion; import org.apache.iotdb.db.queryengine.plan.statement.metadata.region.RemoveRegionStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RemoveRegionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterColumnDataTypeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterColumnDataTypeTask.java index 2f8a1983a9f9d..5642db8d0fbc7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterColumnDataTypeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterColumnDataTypeTask.java @@ -21,8 +21,8 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; public class AlterColumnDataTypeTask extends AbstractAlterOrDropTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterDBTask.java index 377d982482fde..d104d39e8c44e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterDBTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterDBTask extends AbstractDatabaseTask { public AlterDBTask(final TDatabaseSchema schema, final boolean exists) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableAddColumnTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableAddColumnTask.java index 9ead4a5a6f595..1777cca735e42 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableAddColumnTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableAddColumnTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentColumnTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentColumnTask.java index ecc1a3397a4d5..2a703863e1091 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentColumnTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentColumnTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterTableCommentColumnTask extends AbstractAlterOrDropTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentTableTask.java index 30bda6d638f37..97d4ff1e0170e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableCommentTableTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableDropColumnTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableDropColumnTask.java index daac5e4e37cc5..7c00575bb244a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableDropColumnTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableDropColumnTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterTableDropColumnTask extends AbstractAlterOrDropTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameColumnTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameColumnTask.java index 79cea6c05e08a..e268e6236cc7a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameColumnTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameColumnTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterTableRenameColumnTask extends AbstractAlterOrDropTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameTableTask.java index fd2ae6d29a9ec..bc1bbac6218c2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableRenameTableTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterTableRenameTableTask extends AbstractAlterOrDropTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableSetPropertiesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableSetPropertiesTask.java index 87e148683f415..c211f14775226 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableSetPropertiesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/AlterTableSetPropertiesTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ClearCacheTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ClearCacheTask.java index 2c0a98059059b..a7249a5a291a1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ClearCacheTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ClearCacheTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class ClearCacheTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java index d5605feac181e..772d228464960 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java @@ -26,10 +26,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateDBTask.java index 5728c4ce52c7e..0b7307eb9c662 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateDBTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateDBTask extends AbstractDatabaseTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableTask.java index daf786a15a939..ab00e43f76996 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateTableTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableViewTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableViewTask.java index da8cc577a1833..2468979f3d52f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableViewTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CreateTableViewTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.commons.schema.table.TsTable; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateTableViewTask extends CreateTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DeleteDeviceTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DeleteDeviceTask.java index b21cac77466b5..a2fac1536d415 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DeleteDeviceTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DeleteDeviceTask.java @@ -27,10 +27,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableDetailsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableDetailsTask.java index 79e9a016d5b49..b316542ae07d2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableDetailsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableDetailsTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.schemaengine.table.TableColumnMetadataUtil; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableTask.java index 8f9c3cadec05e..97bb0031c7c4d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DescribeTableTask.java @@ -27,10 +27,10 @@ import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropDBTask.java index 2248f3dc120ed..b36e06e678775 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropDBTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropDBTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropTableTask.java index 23622ff541cc8..95335e772502e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/DropTableTask.java @@ -21,8 +21,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropTableTask extends AbstractAlterOrDropTableTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java index 68541badde751..2db2210ce61b3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RelationalAuthorizerTask implements IConfigTask { private final RelationalAuthorStatement statement; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowAINodesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowAINodesTask.java index a074bae86803f..0004872719ce2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowAINodesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowAINodesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowConfigNodesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowConfigNodesTask.java index f7d0b0240fa58..5a48212e7dbc7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowConfigNodesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowConfigNodesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java index 590d6fd30a01c..c093ee622a622 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java index 80cab3aa004cb..4ec7661fde89d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java @@ -30,10 +30,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java index 229cf9aa5f45b..0c0697a061dd2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java index d46f3e7774ded..1c04fa541cb57 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java @@ -30,10 +30,10 @@ import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java index 128458888dc97..dd6968afe0e03 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java @@ -30,10 +30,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.schemaengine.table.InformationSchemaUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDataNodesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDataNodesTask.java index 60066d9bdd01b..6986c2452b603 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDataNodesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDataNodesTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesDetailsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesDetailsTask.java index 228b1dd266acb..39119d66e866d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesDetailsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesDetailsTask.java @@ -29,10 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesTask.java index 9f04ffab9d28e..069fdf25d94a0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowTablesTask.java @@ -27,10 +27,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/UseDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/UseDBTask.java index b23ccf6d643de..113c172f73f3f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/UseDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/UseDBTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Use; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class UseDBTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/AlterSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/AlterSchemaTemplateTask.java index 619c18823dee3..7286e0bedbee2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/AlterSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/AlterSchemaTemplateTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.AlterSchemaTemplateStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterSchemaTemplateTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/CreateSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/CreateSchemaTemplateTask.java index 79251a7297f9a..f2a66ffaa17f1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/CreateSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/CreateSchemaTemplateTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.CreateSchemaTemplateStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateSchemaTemplateTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DeactivateSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DeactivateSchemaTemplateTask.java index 28bf34ec2788f..3e3d08f4c1ad0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DeactivateSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DeactivateSchemaTemplateTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.DeactivateTemplateStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DeactivateSchemaTemplateTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DropSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DropSchemaTemplateTask.java index 928908f6edefb..be71c0847626e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DropSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/DropSchemaTemplateTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.DropSchemaTemplateStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropSchemaTemplateTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/SetSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/SetSchemaTemplateTask.java index 2f0ac9e7df67a..c6c02ed6c6373 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/SetSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/SetSchemaTemplateTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.SetSchemaTemplateStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetSchemaTemplateTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowNodesInSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowNodesInSchemaTemplateTask.java index e601c3533ef94..1794e8c839816 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowNodesInSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowNodesInSchemaTemplateTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowPathSetTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowPathSetTemplateTask.java index ab08a47805f3a..1894bfe60b803 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowPathSetTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowPathSetTemplateTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ShowPathSetTemplateStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowSchemaTemplateTask.java index 870b4c0c076bd..f3122b143c00a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/ShowSchemaTemplateTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ShowSchemaTemplateStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/UnsetSchemaTemplateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/UnsetSchemaTemplateTask.java index 652c70c3cb60b..d4fd1105cc4f6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/UnsetSchemaTemplateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/template/UnsetSchemaTemplateTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.UnsetSchemaTemplateStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class UnsetSchemaTemplateTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/AlterLogicalViewTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/AlterLogicalViewTask.java index 516478e580915..bd3947bd9bf02 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/AlterLogicalViewTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/AlterLogicalViewTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.view.AlterLogicalViewStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterLogicalViewTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/DeleteLogicalViewTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/DeleteLogicalViewTask.java index ba3e44f1be0b3..9550871174288 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/DeleteLogicalViewTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/DeleteLogicalViewTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.view.DeleteLogicalViewStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DeleteLogicalViewTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/RenameLogicalViewTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/RenameLogicalViewTask.java index 8d7573b05933d..b741d0fd6c253 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/RenameLogicalViewTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/view/RenameLogicalViewTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.metadata.view.RenameLogicalViewStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RenameLogicalViewTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/DeallocateTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/DeallocateTask.java index 6b83905f0a378..3d0209b416217 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/DeallocateTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/DeallocateTask.java @@ -25,11 +25,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - /** * Task for executing DEALLOCATE PREPARE statement. Removes the prepared statement from the session * and releases its allocated memory. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/PrepareTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/PrepareTask.java index 0d2cbe0a7137c..af283a681dcfb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/PrepareTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/PrepareTask.java @@ -26,11 +26,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - /** * Task for executing PREPARE statement. Stores the prepared statement AST in the session. The AST * is cached to avoid reparsing on EXECUTE (skipping Parser phase). Memory is allocated from diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/SetSqlDialectTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/SetSqlDialectTask.java index 693fc4a1c7cf5..1666c188d277d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/SetSqlDialectTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/SetSqlDialectTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetSqlDialectTask implements IConfigTask { private final SqlDialect sqlDialect; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentDatabaseTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentDatabaseTask.java index f7100593a0f99..16831ed459d48 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentDatabaseTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentDatabaseTask.java @@ -25,10 +25,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentSqlDialectTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentSqlDialectTask.java index ccfce5aed2746..9628f8b69cb1a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentSqlDialectTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentSqlDialectTask.java @@ -25,10 +25,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentTimestampTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentTimestampTask.java index 78ce06eb409a9..436d811883fed 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentTimestampTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentTimestampTask.java @@ -23,11 +23,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - import static org.apache.iotdb.db.queryengine.plan.execution.memory.StatementMemorySourceVisitor.getCurrentTimestampResult; public class ShowCurrentTimestampTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentUserTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentUserTask.java index 7408727c7c71e..b0de41a7fbd2c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentUserTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowCurrentUserTask.java @@ -25,10 +25,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowVersionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowVersionTask.java index 8f009c688835c..9231a4d05430a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowVersionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/session/ShowVersionTask.java @@ -23,11 +23,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - import static org.apache.iotdb.db.queryengine.plan.execution.memory.StatementMemorySourceVisitor.getVersionResult; public class ShowVersionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/AuthorizerTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/AuthorizerTask.java index 4102e37ed1ead..51679f6e73afc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/AuthorizerTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/AuthorizerTask.java @@ -26,8 +26,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AuthorizerTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ClearCacheTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ClearCacheTask.java index bf5e332a29c2d..98df32f5f8d96 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ClearCacheTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ClearCacheTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.ClearCacheStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class ClearCacheTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/FlushTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/FlushTask.java index b4b58224f9007..c244202cf29fe 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/FlushTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/FlushTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.FlushStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class FlushTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/KillQueryTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/KillQueryTask.java index 362e3a567c7b7..3a455a42fe4ec 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/KillQueryTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/KillQueryTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.KillQuery; import org.apache.iotdb.db.queryengine.plan.statement.sys.KillQueryStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class KillQueryTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/LoadConfigurationTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/LoadConfigurationTask.java index b7928c47cc957..1807afd401d2d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/LoadConfigurationTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/LoadConfigurationTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.LoadConfigurationStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class LoadConfigurationTask implements IConfigTask { private final LoadConfigurationStatement loadConfigurationStatement; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/MergeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/MergeTask.java index 6af87ded59623..7f543b753c11c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/MergeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/MergeTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.MergeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class MergeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/RepairDataPartitionTableTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/RepairDataPartitionTableTask.java index f3675e9a0d970..a869d58ce752b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/RepairDataPartitionTableTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/RepairDataPartitionTableTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class RepairDataPartitionTableTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetConfigurationTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetConfigurationTask.java index f03c0aed7bd54..bbc304830688a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetConfigurationTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetConfigurationTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.SetConfigurationStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetConfigurationTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetSystemStatusTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetSystemStatusTask.java index 973bcfeb84d14..2c10fe203b0f7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetSystemStatusTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/SetSystemStatusTask.java @@ -22,8 +22,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.SetSystemStatusStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetSystemStatusTask implements IConfigTask { private final SetSystemStatusStatement setSystemStatusStatement; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowConfigurationTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowConfigurationTask.java index e3f880d5ea3d3..9fb3cf898675f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowConfigurationTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowConfigurationTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.ShowConfigurationStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowRepairDataPartitionTableProgressTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowRepairDataPartitionTableProgressTask.java index d0ccbaa9f3423..8563691e919c8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowRepairDataPartitionTableProgressTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/ShowRepairDataPartitionTableProgressTask.java @@ -24,10 +24,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StartRepairDataTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StartRepairDataTask.java index 431c603c414af..ff0fa94600e3a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StartRepairDataTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StartRepairDataTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.StartRepairDataStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class StartRepairDataTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StopRepairDataTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StopRepairDataTask.java index cff60b4889dfd..8119dee56c3c4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StopRepairDataTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/StopRepairDataTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.StopRepairDataStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class StopRepairDataTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/TestConnectionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/TestConnectionTask.java index e71af67cafd10..b206ab9c6d770 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/TestConnectionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/TestConnectionTask.java @@ -32,11 +32,11 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.rpc.UrlUtils; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/AlterPipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/AlterPipeTask.java index 9b5857d263595..19e53f0d2ab1b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/AlterPipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/AlterPipeTask.java @@ -28,8 +28,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.AlterPipeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/CreatePipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/CreatePipeTask.java index fc3884cb27e21..fdde7ed4d54d2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/CreatePipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/CreatePipeTask.java @@ -28,8 +28,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.CreatePipeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/DropPipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/DropPipeTask.java index a10c6042241c2..deeb035d0f82d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/DropPipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/DropPipeTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropPipe; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.DropPipeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropPipeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java index 71a6e6ef4d327..0215d19351772 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java @@ -31,10 +31,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowPipes; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.ShowPipesStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StartPipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StartPipeTask.java index c118f0a3a58a4..0309ebbc0c52c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StartPipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StartPipeTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StartPipe; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.StartPipeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class StartPipeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StopPipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StopPipeTask.java index 91764c43263d0..8b2233059751a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StopPipeTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/StopPipeTask.java @@ -25,8 +25,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopPipe; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.metadata.pipe.StopPipeStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class StopPipeTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetSpaceQuotaTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetSpaceQuotaTask.java index 2f7eeb685886f..2959a045c1824 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetSpaceQuotaTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetSpaceQuotaTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.SetSpaceQuotaStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetSpaceQuotaTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetThrottleQuotaTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetThrottleQuotaTask.java index 4b47477ea7b5b..b761516191ff5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetThrottleQuotaTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/SetThrottleQuotaTask.java @@ -23,8 +23,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.SetThrottleQuotaStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class SetThrottleQuotaTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowSpaceQuotaTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowSpaceQuotaTask.java index 4aa59f0bed8d6..17c702e600a07 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowSpaceQuotaTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowSpaceQuotaTask.java @@ -31,10 +31,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.ShowSpaceQuotaStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowThrottleQuotaTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowThrottleQuotaTask.java index 6913c7e0e1474..a5bc89d6fe343 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowThrottleQuotaTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/quota/ShowThrottleQuotaTask.java @@ -33,10 +33,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.ShowThrottleQuotaStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.BytesUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/AlterTopicTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/AlterTopicTask.java index 33ae342267be2..80a1da1c4e794 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/AlterTopicTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/AlterTopicTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterTopic; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.AlterTopicStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class AlterTopicTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/CreateTopicTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/CreateTopicTask.java index b3bfe6dc57f47..ca2fc47e16c7f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/CreateTopicTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/CreateTopicTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTopic; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.CreateTopicStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class CreateTopicTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropSubscriptionTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropSubscriptionTask.java index bdf076a720ef9..f3cb2158b3e80 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropSubscriptionTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropSubscriptionTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropSubscription; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropSubscriptionStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropSubscriptionTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropTopicTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropTopicTask.java index d7ff17d287394..d2c002d94a37c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropTopicTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/DropTopicTask.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTopic; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropTopicStatement; - -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; public class DropTopicTask implements IConfigTask { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java index 4ab29b026dd06..454daff2be632 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java @@ -30,11 +30,11 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.rpc.subscription.config.ConsumerConstant; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowSubscriptionsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowSubscriptionsTask.java index 87e07063629e6..157cf9f329f61 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowSubscriptionsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowSubscriptionsTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowSubscriptions; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowSubscriptionsStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowTopicsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowTopicsTask.java index 1871ef66a5026..f20d99e05440a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowTopicsTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowTopicsTask.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTopics; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowTopicsStatement; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/MemorySourceHandle.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/MemorySourceHandle.java index 7a4919446cf92..e2b204dc10b33 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/MemorySourceHandle.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/MemorySourceHandle.java @@ -22,10 +22,10 @@ import org.apache.iotdb.commons.exception.IoTDBException; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.execution.exchange.source.ISourceHandle; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.TsBlockSerde; @@ -34,7 +34,7 @@ import java.io.IOException; import java.nio.ByteBuffer; -import static com.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; public class MemorySourceHandle implements ISourceHandle { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java index a2ff5ef6adbf8..cacaa9c21080d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java @@ -43,8 +43,8 @@ import org.apache.iotdb.db.queryengine.plan.expression.unary.LikeExpression; import org.apache.iotdb.db.queryengine.plan.expression.unary.LogicNotExpression; import org.apache.iotdb.db.queryengine.plan.expression.unary.RegularExpression; +import org.apache.iotdb.google.common.io.BaseEncoding; -import com.google.common.io.BaseEncoding; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.common.regexp.LikePattern; import org.apache.tsfile.enums.TSDataType; @@ -65,8 +65,8 @@ import java.util.Map; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.db.queryengine.plan.expression.binary.CompareBinaryExpression.flipType; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.enums.TSDataType.DOUBLE; import static org.apache.tsfile.enums.TSDataType.INT32; import static org.apache.tsfile.enums.TSDataType.INT64; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToTimeFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToTimeFilterVisitor.java index e573a7dcf225e..d8f6926f65db6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToTimeFilterVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToTimeFilterVisitor.java @@ -51,8 +51,8 @@ import java.util.Set; import java.util.TimeZone; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.db.queryengine.plan.expression.binary.CompareBinaryExpression.flipType; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.read.filter.operator.Not.CONTAIN_NOT_ERR_MSG; public class ConvertPredicateToTimeFilterVisitor extends PredicateVisitor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/AggregationPushDown.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/AggregationPushDown.java index f852a267d2440..bb38eac232834 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/AggregationPushDown.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/AggregationPushDown.java @@ -74,8 +74,8 @@ import java.util.Map; import java.util.Set; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.calc.utils.constant.SqlConstant.COUNT_TIME; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class AggregationPushDown implements PlanOptimizer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/PredicatePushDown.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/PredicatePushDown.java index 6c3ea558953b4..a245c59c6a481 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/PredicatePushDown.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/optimization/PredicatePushDown.java @@ -60,8 +60,8 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** Optimization phase: Logical plan planning. */ public class PredicatePushDown implements PlanOptimizer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index e1ef66774f9f1..6f888401a2386 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -263,12 +263,12 @@ import org.apache.iotdb.db.schemaengine.template.TemplateAlterOperationType; import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; import org.apache.iotdb.db.utils.DataNodeDateTimeUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.io.BaseEncoding; import org.apache.iotdb.trigger.api.enums.TriggerEvent; import org.apache.iotdb.trigger.api.enums.TriggerType; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.io.BaseEncoding; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.tsfile.common.conf.TSFileDescriptor; @@ -304,7 +304,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.stream.Collectors.toList; import static org.apache.iotdb.calc.transformation.dag.util.CommonTransformUtils.getEscapeCharacter; import static org.apache.iotdb.calc.utils.constant.SqlConstant.CAST_FUNCTION; @@ -330,6 +329,7 @@ import static org.apache.iotdb.db.queryengine.plan.optimization.LimitOffsetPushDown.canPushDownLimitOffsetToGroupByTime; import static org.apache.iotdb.db.queryengine.plan.optimization.LimitOffsetPushDown.pushDownLimitOffsetToTimeParameter; import static org.apache.iotdb.db.queryengine.plan.relational.sql.parser.AstBuilder.lowerIdentifier; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** Parse AST to Statement. */ public class ASTVisitor extends IoTDBSqlParserBaseVisitor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/DataNodeTableOperatorGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/DataNodeTableOperatorGenerator.java index 2e5e8cba292b9..091ca44c67bf8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/DataNodeTableOperatorGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/DataNodeTableOperatorGenerator.java @@ -144,9 +144,9 @@ import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils; import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSourceType; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; @@ -185,7 +185,6 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isTimeColumn; import static org.apache.iotdb.calc.utils.constant.SqlConstant.AVG; @@ -211,6 +210,7 @@ import static org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.SeriesScanOptions.updateFilterUsingTTL; import static org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableDeviceLastCache.PLACEHOLDER_NO_VALUE; import static org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableDeviceLastCache.PLACEHOLDER_STALE_VALUE; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.read.common.type.TimestampType.TIMESTAMP; public class DataNodeTableOperatorGenerator diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanContext.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanContext.java index 144fa0fec8547..258bd8ae6ed20 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanContext.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanContext.java @@ -64,8 +64,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; // Attention: We should use thread-safe data structure for members that are shared by all pipelines public class LocalExecutionPlanContext implements ITableOperatorGeneratorContext { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java index aad7b56cddb66..e5401cd6339ee 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java @@ -252,9 +252,9 @@ import org.apache.iotdb.db.utils.columngenerator.SlidingTimeColumnGenerator; import org.apache.iotdb.db.utils.columngenerator.parameter.ColumnGeneratorParameter; import org.apache.iotdb.db.utils.columngenerator.parameter.SlidingTimeColumnGeneratorParameter; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.external.commons.lang3.StringUtils; @@ -290,8 +290,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.common.DataNodeEndPoints.isSameNode; import static org.apache.iotdb.db.queryengine.execution.operator.AggregationUtil.calculateMaxAggregationResultSize; import static org.apache.iotdb.db.queryengine.execution.operator.AggregationUtil.calculateMaxAggregationResultSizeForLastQuery; @@ -305,6 +303,8 @@ import static org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.AggregationDescriptor.getAggregationTypeByFuncName; import static org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.SeriesScanOptions.updateFilterUsingTTL; import static org.apache.iotdb.db.queryengine.plan.statement.component.Ordering.ASC; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** This Visitor is responsible for transferring PlanNode Tree to Operator Tree. */ public class OperatorTreeGenerator implements PlanVisitor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java index 2e3714b71a6c5..01961a4b3267f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java @@ -87,8 +87,8 @@ import org.apache.iotdb.db.queryengine.plan.statement.component.OrderByKey; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; import org.apache.iotdb.db.queryengine.plan.statement.component.SortItem; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java index 39e987a4b2f8d..b154bc213ed99 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java @@ -28,7 +28,7 @@ import javax.annotation.concurrent.NotThreadSafe; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; @NotThreadSafe public class NotThreadSafeMemoryReservationManager implements MemoryReservationManager { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/PlanFragment.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/PlanFragment.java index d39281f286432..36d11ea185cb2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/PlanFragment.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/PlanFragment.java @@ -47,7 +47,7 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** PlanFragment contains a sub-query of distributed query. */ public class PlanFragment { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/BaseSourceRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/BaseSourceRewriter.java index b2424bfca26ab..a83404ad02423 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/BaseSourceRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/BaseSourceRewriter.java @@ -24,7 +24,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class BaseSourceRewriter implements PlanVisitor, C> { @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java index f0d2bc134d7b0..a8b22378bdd30 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java @@ -102,8 +102,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExternalTsFileScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableDiskUsageInformationSchemaTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeDeviceViewScanNode; +import org.apache.iotdb.google.common.base.Joiner; -import com.google.common.base.Joiner; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.utils.Pair; @@ -114,7 +114,7 @@ import java.util.Map.Entry; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PlanGraphPrinter implements PlanVisitor, PlanGraphPrinter.GraphContext> { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodeManagementMemoryMergeNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodeManagementMemoryMergeNode.java index 42be7dc9febe5..8174098ed0a14 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodeManagementMemoryMergeNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodeManagementMemoryMergeNode.java @@ -27,8 +27,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.ProcessNode; import org.apache.iotdb.commons.utils.ThriftCommonsSerDeUtils; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsConvertNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsConvertNode.java index 315f71cfacb75..f52f557ca443f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsConvertNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsConvertNode.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.ProcessNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsCountNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsCountNode.java index 84a8024f05b36..2c3e508dd82b9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsCountNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/NodePathsCountNode.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.ProcessNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/SchemaFetchScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/SchemaFetchScanNode.java index 0fe944f78bfb9..4545180e83f98 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/SchemaFetchScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/SchemaFetchScanNode.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.source.SourceNode; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Collections; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/ActivateTemplateNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/ActivateTemplateNode.java index 16473e7936ee5..fa609acae17c5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/ActivateTemplateNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/ActivateTemplateNode.java @@ -30,8 +30,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.db.schemaengine.schemaregion.write.req.IActivateTemplateInClusterPlan; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/AlterTimeSeriesNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/AlterTimeSeriesNode.java index e262eadaf8873..471ad4e45a3ff 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/AlterTimeSeriesNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/AlterTimeSeriesNode.java @@ -31,8 +31,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.db.queryengine.plan.statement.metadata.AlterTimeSeriesStatement.AlterType; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.NotImplementedException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateAlignedTimeSeriesNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateAlignedTimeSeriesNode.java index e9c5702464735..a08507287628c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateAlignedTimeSeriesNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateAlignedTimeSeriesNode.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.db.schemaengine.schemaregion.write.req.ICreateAlignedTimeSeriesPlan; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.NotImplementedException; import org.apache.tsfile.file.metadata.enums.CompressionType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateTimeSeriesNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateTimeSeriesNode.java index 6cbd9fb79fdca..4e26e76756532 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateTimeSeriesNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/CreateTimeSeriesNode.java @@ -31,8 +31,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode; import org.apache.iotdb.db.schemaengine.schemaregion.write.req.ICreateTimeSeriesPlan; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.NotImplementedException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/InternalCreateTimeSeriesNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/InternalCreateTimeSeriesNode.java index 2fe9855f827d5..68670f1b64e80 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/InternalCreateTimeSeriesNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/InternalCreateTimeSeriesNode.java @@ -30,8 +30,8 @@ import org.apache.iotdb.db.queryengine.plan.analyze.IAnalysis; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.exception.NotImplementedException; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/CollectNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/CollectNode.java index 5e59108a83d74..3cae5675aeb38 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/CollectNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/CollectNode.java @@ -35,7 +35,7 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CollectNode extends MultiChildProcessNode { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/SortNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/SortNode.java index 79f277ee9c19a..3e08b8fb8fda2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/SortNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/SortNode.java @@ -26,8 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.OrderByParameter; - -import com.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Objects; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java index 5c783ec88e9c0..58985fa7f657c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/AlignedSeriesAggregationScanNode.java @@ -38,8 +38,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.AggregationStep; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/DeviceRegionScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/DeviceRegionScanNode.java index af96dcc08783c..d540620f8ce49 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/DeviceRegionScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/DeviceRegionScanNode.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.common.DeviceContext; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeUtil; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java index 71fd760fc0553..1b9f2ba9b7446 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/LastQueryScanNode.java @@ -31,8 +31,8 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeUtil; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesAggregationScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesAggregationScanNode.java index dd0fd381806dd..b61179ce9c9fc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesAggregationScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesAggregationScanNode.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.AggregationDescriptor; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanNode.java index 31fa9d217d89f..fce37b54d3e77 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanNode.java @@ -31,8 +31,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeUtil; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanSourceNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanSourceNode.java index fc5ec9267c57c..849b19fed8dd6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanSourceNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/SeriesScanSourceNode.java @@ -27,8 +27,7 @@ import org.apache.iotdb.db.queryengine.plan.expression.Expression; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowDiskUsageNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowDiskUsageNode.java index 66c93c9d898aa..032c92ca8413f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowDiskUsageNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowDiskUsageNode.java @@ -30,8 +30,8 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.expression.Expression; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowQueriesNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowQueriesNode.java index 20b3a7e843f32..8a9c49a05d914 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowQueriesNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/ShowQueriesNode.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.schema.column.ColumnHeader; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java index 24bcf28840334..0e0ed8fa28573 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java @@ -34,8 +34,8 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.TimeseriesContext; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import org.apache.tsfile.write.schema.IMeasurementSchema; import org.apache.tsfile.write.schema.MeasurementSchema; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/parameter/OutputColumn.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/parameter/OutputColumn.java index 6ebe4d2e32225..8e44eedd954f2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/parameter/OutputColumn.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/parameter/OutputColumn.java @@ -20,14 +20,14 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.nio.ByteBuffer; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class OutputColumn { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java index d88efc0c41feb..dd90a2bf3ab0b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java @@ -61,8 +61,7 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import javax.annotation.Nullable; @@ -71,9 +70,6 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.extractAggregateFunctions; @@ -84,6 +80,9 @@ import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.ScopeReferenceExtractor.isFieldFromScope; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware.scopeAwareKey; import static org.apache.iotdb.db.queryengine.plan.relational.utils.NodeUtils.getSortItemsFromOrderBy; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; /** Checks whether an expression is constant with respect to the group */ class AggregationAnalyzer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java index 884d5cbd4e74a..e225e5fc82a06 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java @@ -73,13 +73,13 @@ import org.apache.iotdb.db.queryengine.plan.relational.analyzer.tablefunction.TableFunctionInvocationAnalysis; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; +import org.apache.iotdb.google.common.collect.ArrayListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Multimap; +import org.apache.iotdb.google.common.collect.Streams; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; -import com.google.common.collect.Streams; import com.google.errorprone.annotations.Immutable; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.type.Type; @@ -101,17 +101,17 @@ import java.util.OptionalLong; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.lang.String.format; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class Analysis implements IAnalysis { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalysis.java index 91c544922b963..4a9fa5a1a6d07 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalysis.java @@ -27,9 +27,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QuantifiedComparisonExpression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SubqueryExpression; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.read.common.type.Type; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalyzer.java index 943965b5d2075..d032001a629c8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionAnalyzer.java @@ -103,13 +103,13 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.OperatorNotFoundException; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StackableAstVisitor; +import org.apache.iotdb.google.common.collect.HashMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.LinkedHashMultimap; +import org.apache.iotdb.google.common.collect.Multimap; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; import org.apache.tsfile.read.common.type.RowType; import org.apache.tsfile.read.common.type.TimestampType; import org.apache.tsfile.read.common.type.Type; @@ -133,12 +133,6 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.collect.Iterators.getOnlyElement; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; import static java.util.Locale.ENGLISH; @@ -162,6 +156,12 @@ import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toTypeSignature; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.extractExpressions; import static org.apache.iotdb.db.queryengine.plan.relational.utils.NodeUtils.getSortItemsFromOrderBy; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; +import static org.apache.iotdb.google.common.collect.Iterators.getOnlyElement; import static org.apache.tsfile.read.common.type.BlobType.BLOB; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionTreeUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionTreeUtils.java index ee1343b35ab72..b9ecc9d39dac8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionTreeUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExpressionTreeUtils.java @@ -29,16 +29,15 @@ import org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DefaultExpressionTraversalVisitor; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.function.Predicate; -import static com.google.common.base.Predicates.alwaysTrue; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Streams.stream; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Predicates.alwaysTrue; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Streams.stream; public final class ExpressionTreeUtils { private ExpressionTreeUtils() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/FieldId.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/FieldId.java index 6b19e6b4abc36..60a83a3bc3276 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/FieldId.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/FieldId.java @@ -23,9 +23,9 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public final class FieldId { public static FieldId from(ResolvedField field) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalysis.java index e539607413e0b..1b7c47dff52f7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalysis.java @@ -25,9 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.FunctionCall; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.RangeQuantifier; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalyzer.java index 0cd76b2cd2e0f..28f7a409d70c4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PatternRecognitionAnalyzer.java @@ -36,23 +36,22 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.VariableDefinition; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis.Range; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.Streams; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ProcessingMode.Mode.FINAL; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.extractExpressions; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil.preOrder; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PatternRecognitionAnalyzer { private PatternRecognitionAnalyzer() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationId.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationId.java index f6b6778865271..eb894c82f1c09 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationId.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationId.java @@ -25,9 +25,9 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.lang.String.format; import static java.lang.System.identityHashCode; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class RelationId { /** diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationType.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationType.java index f346b429a986e..cbb0e5524dcc3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationType.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RelationType.java @@ -21,19 +21,18 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class RelationType { private final List visibleFields; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java index 716d8ab8e48ed..d1fd28077ec0d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java @@ -28,10 +28,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Table; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WithQuery; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableMap; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -39,13 +38,13 @@ import java.util.Optional; import java.util.function.Predicate; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.asQualifiedName; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope.BasisType.FIELD; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope.BasisType.TABLE; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class Scope { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index 741116c844c53..1734e342ca39f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -211,6 +211,16 @@ import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.VerifyException; +import org.apache.iotdb.google.common.collect.ArrayListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMultimap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.Streams; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.udf.api.exception.UDFException; @@ -224,16 +234,6 @@ import org.apache.iotdb.udf.api.relational.table.specification.ScalarParameterSpecification; import org.apache.iotdb.udf.api.relational.table.specification.TableParameterSpecification; -import com.google.common.base.Joiner; -import com.google.common.base.VerifyException; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Streams; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.type.BinaryType; import org.apache.tsfile.read.common.type.ObjectType; @@ -270,14 +270,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; -import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.common.collect.Iterables.getLast; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.lang.Math.toIntExact; import static java.util.Collections.emptyList; import static java.util.Locale.ENGLISH; @@ -306,6 +298,14 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PushPredicateIntoTableScan.containsDiffFunction; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil.preOrder; import static org.apache.iotdb.db.queryengine.plan.relational.utils.NodeUtils.getSortItemsFromOrderBy; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; +import static org.apache.iotdb.google.common.collect.Iterables.getLast; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; public class StatementAnalyzer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java index 80ddbb8cddf2c..f647d499958fb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java @@ -49,9 +49,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.type.InternalTypeManager; import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.math.DoubleMath; -import com.google.common.collect.ImmutableList; -import com.google.common.math.DoubleMath; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.common.regexp.LikePattern; import org.apache.tsfile.enums.TSDataType; @@ -76,13 +76,13 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isExtractTimeColumn; import static org.apache.iotdb.calc.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isTimeColumn; import static org.apache.iotdb.calc.transformation.dag.util.CommonTransformUtils.getEscapeCharacter; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.ConvertPredicateToTimeFilterVisitor.getLongValue; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isLiteral; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isSymbolReference; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.enums.TSDataType.INT32; import static org.apache.tsfile.enums.TSDataType.INT64; import static org.apache.tsfile.read.common.type.TimestampType.TIMESTAMP; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java index 18a1aa33f50eb..1517671dc8b48 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToTimeFilterVisitor.java @@ -37,8 +37,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SimpleCaseExpression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.read.filter.factory.FilterFactory; import org.apache.tsfile.read.filter.factory.TimeFilterApi; @@ -52,7 +52,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** The caller must make sure that the Expression only contains valid time predicate */ public class ConvertPredicateToTimeFilterVisitor extends PredicateVisitor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java index a7781bc1e64d9..55f50271474f3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/schema/ConvertSchemaPredicateToFilterVisitor.java @@ -63,9 +63,9 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.calc.transformation.dag.util.CommonTransformUtils.getEscapeCharacter; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isSymbolReference; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * The {@link ConvertSchemaPredicateToFilterVisitor} will convert a predicate to {@link diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/ArgumentsAnalysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/ArgumentsAnalysis.java index d07e05c565e3b..1435d5e7f11df 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/ArgumentsAnalysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/ArgumentsAnalysis.java @@ -20,11 +20,10 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer.tablefunction; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.udf.api.relational.table.argument.Argument; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableArgumentAnalysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableArgumentAnalysis.java index a909becbb6b96..9e03460d2e53f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableArgumentAnalysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableArgumentAnalysis.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Relation; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableFunctionInvocationAnalysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableFunctionInvocationAnalysis.java index 1740ac2120a83..6d44ec29931d2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableFunctionInvocationAnalysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/tablefunction/TableFunctionInvocationAnalysis.java @@ -19,11 +19,10 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer.tablefunction; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.udf.api.relational.table.TableFunctionHandle; import org.apache.iotdb.udf.api.relational.table.argument.Argument; -import com.google.common.collect.ImmutableList; - import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/PlanOptimizersStatsCollector.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/PlanOptimizersStatsCollector.java index c3adf8fc8811d..a083e314be65a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/PlanOptimizersStatsCollector.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/PlanOptimizersStatsCollector.java @@ -27,7 +27,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class PlanOptimizersStatsCollector { private final Map, QueryPlanOptimizerStats> stats = new ConcurrentHashMap<>(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/QueryPlanOptimizerStats.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/QueryPlanOptimizerStats.java index 207cc0d45e2e6..7ec9ba502485a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/QueryPlanOptimizerStats.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/execution/querystats/QueryPlanOptimizerStats.java @@ -23,8 +23,8 @@ import java.util.concurrent.atomic.AtomicLong; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class QueryPlanOptimizerStats { private final String rule; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/OperatorNotFoundException.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/OperatorNotFoundException.java index ca4f97ab096a8..c42b2f21c4fb3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/OperatorNotFoundException.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/OperatorNotFoundException.java @@ -23,9 +23,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.function.OperatorType; import org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignature; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.Type; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java index 6a08427d82b25..ca88fa89b4f01 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java @@ -52,9 +52,9 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateTableTask; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.StringType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/DeviceSchemaRequestCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/DeviceSchemaRequestCache.java index e612d6336531a..ca84cf28fc02b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/DeviceSchemaRequestCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/DeviceSchemaRequestCache.java @@ -21,9 +21,9 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.FetchDevice; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializer.java index 322e0777b22b1..bbcabca78fba9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializer.java @@ -52,10 +52,10 @@ import org.apache.iotdb.db.queryengine.statistics.FragmentInstanceStatisticsDrawer; import org.apache.iotdb.db.queryengine.statistics.QueryStatisticsFetcher; import org.apache.iotdb.db.queryengine.statistics.StatisticLine; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.mpp.rpc.thrift.TFetchFragmentInstanceStatisticsResp; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.type.TypeFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/EqualityInference.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/EqualityInference.java index 1f86b6b9389a1..0d9cb30fa4835 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/EqualityInference.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/EqualityInference.java @@ -26,13 +26,12 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.SubExpressionExtractor; import org.apache.iotdb.db.queryengine.plan.relational.utils.DisjointSet; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Multimap; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSetMultimap; +import org.apache.iotdb.google.common.collect.Multimap; import java.util.ArrayList; import java.util.Arrays; @@ -49,11 +48,11 @@ import java.util.function.ToIntFunction; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.DeterminismEvaluator.isDeterministic; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionNodeInliner.replaceExpression; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.extractConjuncts; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * Makes equality based inferences to rewrite Expressions and generate equality sets in terms of diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionExtractor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionExtractor.java index 6fae6f8cd6bd8..684dde6856efc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionExtractor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionExtractor.java @@ -29,8 +29,7 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.SimplePlanVisitor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.function.Consumer; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionSymbolInliner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionSymbolInliner.java index 57ee185183136..a3d54fbe7e241 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionSymbolInliner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExpressionSymbolInliner.java @@ -25,14 +25,13 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter; - -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; +import org.apache.iotdb.google.common.collect.HashMultiset; +import org.apache.iotdb.google.common.collect.Multiset; import java.util.Map; import java.util.function.Function; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public final class ExpressionSymbolInliner { public static Expression inlineSymbols( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrExpressionInterpreter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrExpressionInterpreter.java index 464c18019bdd0..12b0331dd5b6a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrExpressionInterpreter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrExpressionInterpreter.java @@ -55,9 +55,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.DeterminismEvaluator; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor; import org.apache.iotdb.db.queryengine.plan.relational.type.TypeCoercion; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.Type; import java.util.ArrayList; @@ -71,12 +71,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Predicates.instanceOf; -import static com.google.common.base.Throwables.throwIfInstanceOf; -import static com.google.common.base.Verify.verify; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.transformation.dag.column.unary.scalar.ExtractTransformer.constructEvaluateFunction; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ArithmeticUnaryExpression.Sign.MINUS; @@ -84,6 +78,12 @@ import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toTypeSignature; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.DeterminismEvaluator.isDeterministic; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.isEffectivelyLiteral; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Predicates.instanceOf; +import static org.apache.iotdb.google.common.base.Throwables.throwIfInstanceOf; +import static org.apache.iotdb.google.common.base.Verify.verify; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class IrExpressionInterpreter { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrTypeAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrTypeAnalyzer.java index d7ffc1c7693a4..7dddfb23be468 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrTypeAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/IrTypeAnalyzer.java @@ -66,10 +66,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.security.AllowAllAccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor; import org.apache.iotdb.db.queryengine.plan.relational.type.TypeCoercionUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.LinkedHashMultimap; import org.apache.tsfile.read.common.type.BlobType; import org.apache.tsfile.read.common.type.DateType; import org.apache.tsfile.read.common.type.RowType; @@ -88,11 +88,11 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toTypeSignature; import static org.apache.iotdb.commons.queryengine.utils.TimestampPrecisionUtils.currPrecision; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE; import static org.apache.tsfile.read.common.type.FloatType.FLOAT; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LiteralEncoder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LiteralEncoder.java index 2851e4659c34e..3e005e5061e07 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LiteralEncoder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LiteralEncoder.java @@ -30,8 +30,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NullLiteral; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.type.DateType; import org.apache.tsfile.read.common.type.TimestampType; @@ -43,12 +43,12 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.metadata.CommonMetadataUtils.isBlobType; import static org.apache.iotdb.calc.plan.relational.metadata.CommonMetadataUtils.isBool; import static org.apache.iotdb.calc.plan.relational.metadata.CommonMetadataUtils.isCharType; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toSqlType; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.read.common.type.DoubleType.DOUBLE; import static org.apache.tsfile.read.common.type.FloatType.FLOAT; import static org.apache.tsfile.read.common.type.IntType.INT32; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java index b7937b1b584f7..73382d6f60316 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanBuilder.java @@ -31,8 +31,7 @@ import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope; - -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.List; @@ -40,9 +39,9 @@ import java.util.function.BiFunction; import java.util.function.BiPredicate; -import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware.scopeAwareKey; +import static org.apache.iotdb.google.common.base.Verify.verify; public class PlanBuilder { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanNodeSearcher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanNodeSearcher.java index e5a6a81067185..c03d240405815 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanNodeSearcher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanNodeSearcher.java @@ -22,22 +22,21 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Optional; import java.util.function.Predicate; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Predicates.alwaysFalse; -import static com.google.common.base.Predicates.alwaysTrue; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup.noLookup; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.ChildReplacer.replaceChildren; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Predicates.alwaysFalse; +import static org.apache.iotdb.google.common.base.Predicates.alwaysTrue; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class PlanNodeSearcher { public static PlanNodeSearcher searchFrom(PlanNode node) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java index f8dd5548595fd..3430ef1bde91f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/QueryPlanner.java @@ -80,12 +80,12 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.GapFillStartAndEndTimeExtractVisitor; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.PredicateWithUncorrelatedScalarSubqueryReconstructor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; import org.apache.tsfile.read.common.type.Type; import javax.annotation.Nonnull; @@ -106,10 +106,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.metadata.CommonMetadataUtils.isNumericType; @@ -130,6 +126,10 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.GapFillStartAndEndTimeExtractVisitor.CAN_NOT_INFER_TIME_RANGE; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil.preOrder; import static org.apache.iotdb.db.queryengine.plan.relational.utils.NodeUtils.getSortItemsFromOrderBy; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; public class QueryPlanner { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java index 266fdce85b095..8dabe16cc4137 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlan.java @@ -24,14 +24,13 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * The purpose of this class is to hold the current plan built so far for a relation (query, table, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java index 117d5db4e5c1d..2625439da1b7a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java @@ -138,13 +138,13 @@ import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ListMultimap; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ListMultimap; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.write.schema.MeasurementSchema; @@ -161,9 +161,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleAggregation; @@ -187,6 +184,9 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.QueryPlanner.pruneInvisibleFields; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.extractPredicates; import static org.apache.iotdb.db.queryengine.plan.relational.utils.NodeUtils.getSortItemsFromOrderBy; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.tsfile.read.common.type.LongType.INT64; import static org.apache.tsfile.read.common.type.StringType.STRING; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ScopeAware.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ScopeAware.java index dc8eb78bb00ce..019da0265b96e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ScopeAware.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ScopeAware.java @@ -30,10 +30,10 @@ import java.util.OptionalInt; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil.treeEqual; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil.treeHash; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * A wrapper for Expressions that can be used as a key in maps and sets. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SimplePlanRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SimplePlanRewriter.java index 43b056372b3fc..af81903dbb1eb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SimplePlanRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SimplePlanRewriter.java @@ -21,11 +21,10 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; - -import static com.google.common.base.Verify.verifyNotNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.ChildReplacer.replaceChildren; +import static org.apache.iotdb.google.common.base.Verify.verifyNotNull; public abstract class SimplePlanRewriter implements PlanVisitor> { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SubqueryPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SubqueryPlanner.java index 20501e9d44258..342c90c6cf8a7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SubqueryPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SubqueryPlanner.java @@ -50,11 +50,11 @@ import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Scope; import org.apache.iotdb.db.queryengine.plan.relational.planner.QueryPlanner.PlanAndMappings; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.PredicateWithUncorrelatedScalarSubqueryReconstructor; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.graph.SuccessorsFunction; +import org.apache.iotdb.google.common.graph.Traverser; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.graph.SuccessorsFunction; -import com.google.common.graph.Traverser; import org.apache.tsfile.read.common.type.Type; import java.util.ArrayList; @@ -64,17 +64,17 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; -import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.collect.Streams.stream; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QuantifiedComparisonExpression.Quantifier.ALL; import static org.apache.iotdb.db.queryengine.plan.relational.planner.PlanBuilder.newPlanBuilder; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware.scopeAwareKey; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; +import static org.apache.iotdb.google.common.collect.Streams.stream; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; class SubqueryPlanner { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java index 0622398c2a04d..f00207875ca46 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolAllocator.java @@ -34,8 +34,8 @@ import java.util.HashMap; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class SymbolAllocator { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolsExtractor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolsExtractor.java index 87676bcf75a00..bfa3e732af7d4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolsExtractor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/SymbolsExtractor.java @@ -34,19 +34,18 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DefaultExpressionTraversalVisitor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DefaultTraversalVisitor; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ExpressionExtractor.extractExpressions; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ExpressionExtractor.extractExpressionsNonRecursive; import static org.apache.iotdb.db.queryengine.plan.relational.planner.PlanNodeSearcher.searchFrom; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup.noLookup; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public final class SymbolsExtractor { private SymbolsExtractor() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java index 8f9928b7ad2d9..e49ca6d83ab93 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java @@ -89,9 +89,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Update; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.WrappedStatement; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.LongType; import org.apache.tsfile.read.common.type.StringType; @@ -102,7 +102,6 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.metadata.MetadataUtil.createQualifiedObjectName; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.OutputNode.COLUMN_NAME_PREFIX; @@ -112,6 +111,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.QueryPlanner.visibleFields; import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice.COUNT_DEVICE_HEADER_STRING; import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice.getDeviceColumnHeaderList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class TableLogicalPlanner { private final MPPQueryContext queryContext; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TranslationMap.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TranslationMap.java index 29675036828f0..474a0d5fb7c79 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TranslationMap.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TranslationMap.java @@ -39,8 +39,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter; import org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil; - -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.Arrays; @@ -51,11 +50,11 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verify; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware.scopeAwareKey; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Verify.verify; /** * Keeps mappings of fields and AST expressions to symbols in the current plan within query diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java index 25845758a081b..62084e3d57baf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java @@ -115,12 +115,12 @@ import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ListMultimap; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ListMultimap; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.read.common.type.LongType; @@ -145,7 +145,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.calc.utils.constant.SqlConstant.COUNT; import static org.apache.iotdb.calc.utils.constant.SqlConstant.DELTA; import static org.apache.iotdb.calc.utils.constant.SqlConstant.INCREASE; @@ -159,6 +158,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PushPredicateIntoTableScan.containsDiffFunction; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.TransformSortToStreamSort.isOrderByAllIdsAndTime; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.Util.split; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; import static org.apache.tsfile.utils.Preconditions.checkArgument; /** This class is used to generate distributed plan for table model. */ diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanner.java index 867fe4ecf6761..add23bcb02abd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanner.java @@ -54,9 +54,9 @@ import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.execution.warnings.WarningCollector.NOOP; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableDistributedPlanner { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java index 2076faedcebbb..532271f8a69d0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExpressionTreeRewriter.java @@ -62,9 +62,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WhenClause; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; import java.util.Iterator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExtractCommonPredicatesExpressionRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExtractCommonPredicatesExpressionRewriter.java index e72509e252a4f..76ba8de3c74be 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExtractCommonPredicatesExpressionRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/ExtractCommonPredicatesExpressionRewriter.java @@ -21,16 +21,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LogicalExpression; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Sets; import java.util.Collection; import java.util.List; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Collections.emptySet; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -38,6 +36,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.DeterminismEvaluator.isDeterministic; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.combinePredicates; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.extractPredicates; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public final class ExtractCommonPredicatesExpressionRewriter { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java index 7b19a444f97f0..05ad8f24de975 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/IrUtils.java @@ -35,9 +35,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.IrTypeAnalyzer; import org.apache.iotdb.db.queryengine.plan.relational.planner.NoOpSymbolResolver; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import org.apache.tsfile.read.common.type.Type; import java.util.Arrays; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java index 3edacf901d4b7..d34a240d0bb79 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/NormalizeOrExpressionRewriter.java @@ -25,10 +25,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.InPredicate; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LogicalExpression; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMultimap; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.LinkedHashSet; @@ -36,11 +35,11 @@ import java.util.Map; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression.Operator.EQUAL; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LogicalExpression.Operator.AND; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.and; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.or; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public final class NormalizeOrExpressionRewriter { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/PredicateWithUncorrelatedScalarSubqueryReconstructor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/PredicateWithUncorrelatedScalarSubqueryReconstructor.java index 7789de35f3a4e..67013afb1cb66 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/PredicateWithUncorrelatedScalarSubqueryReconstructor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/PredicateWithUncorrelatedScalarSubqueryReconstructor.java @@ -59,7 +59,7 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PredicateWithUncorrelatedScalarSubqueryReconstructor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/SubExpressionExtractor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/SubExpressionExtractor.java index 3182b006a6e9f..776c3a0bd06e6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/SubExpressionExtractor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ir/SubExpressionExtractor.java @@ -21,14 +21,13 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.graph.SuccessorsFunction; -import com.google.common.graph.Traverser; +import org.apache.iotdb.google.common.graph.SuccessorsFunction; +import org.apache.iotdb.google.common.graph.Traverser; import java.util.stream.Stream; -import static com.google.common.collect.Streams.stream; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.collect.Streams.stream; /** * Extracts and returns the stream of all expression subtrees within an Expression, including diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/IterativeOptimizer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/IterativeOptimizer.java index 5f771e5ab1449..9af5170945253 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/IterativeOptimizer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/IterativeOptimizer.java @@ -35,10 +35,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.AdaptivePlanOptimizer; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanOptimizer; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.session.Session; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import io.airlift.units.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +50,6 @@ import java.util.function.Predicate; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static java.lang.System.nanoTime; import static java.util.Objects.requireNonNull; @@ -59,6 +58,7 @@ import static java.util.stream.Collectors.joining; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.db.queryengine.plan.relational.execution.querystats.PlanOptimizersStatsCollector.createPlanOptimizersStatsCollector; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.rpc.TSStatusCode.OPTIMIZER_TIMEOUT; public class IterativeOptimizer implements AdaptivePlanOptimizer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Lookup.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Lookup.java index 8d265ee1aff72..b5aa72f933de4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Lookup.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Lookup.java @@ -26,8 +26,8 @@ import java.util.function.Function; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.MoreCollectors.toOptional; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.MoreCollectors.toOptional; public interface Lookup { /** diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Memo.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Memo.java index 5864e9903c0aa..d28cafdd1d93c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Memo.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/Memo.java @@ -23,9 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.iterative.GroupReference; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.QueryId; - -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; +import org.apache.iotdb.google.common.collect.HashMultiset; +import org.apache.iotdb.google.common.collect.Multiset; import java.util.HashMap; import java.util.HashSet; @@ -34,10 +33,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Plans.resolveGroupReferences; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** * Stores a plan in a form that's efficient to mutate locally (i.e. without having to do full diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleIndex.java index 4bb2e0ecd474e..c9f4574c3f945 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleIndex.java @@ -22,10 +22,9 @@ import org.apache.iotdb.calc.plan.relational.utils.matching.Pattern; import org.apache.iotdb.calc.plan.relational.utils.matching.pattern.TypeOfPattern; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; -import com.google.common.reflect.TypeToken; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.reflect.TypeToken; import java.util.Set; import java.util.stream.Stream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleStatsRecorder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleStatsRecorder.java index b36fdb8cb99c8..961a9d5eb12d4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleStatsRecorder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/RuleStatsRecorder.java @@ -19,13 +19,13 @@ package org.apache.iotdb.db.queryengine.plan.relational.planner.iterative; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class RuleStatsRecorder { private final Map, RuleStats> stats = new HashMap<>(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/AggregationDecorrelation.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/AggregationDecorrelation.java index de1bfdb943ef2..80989d05ccab6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/AggregationDecorrelation.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/AggregationDecorrelation.java @@ -22,15 +22,14 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.List; import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; class AggregationDecorrelation { private AggregationDecorrelation() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/CanonicalizeExpressionRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/CanonicalizeExpressionRewriter.java index 8ad62b9713e15..b8730800e2c81 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/CanonicalizeExpressionRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/CanonicalizeExpressionRewriter.java @@ -37,8 +37,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ExpressionRewriteRuleSet.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ExpressionRewriteRuleSet.java index dfd84d1716880..edc0ba709d4a6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ExpressionRewriteRuleSet.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ExpressionRewriteRuleSet.java @@ -33,10 +33,9 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Map; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/GatherAndMergeWindows.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/GatherAndMergeWindows.java index 2e8424372d0ca..e62303426382d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/GatherAndMergeWindows.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/GatherAndMergeWindows.java @@ -35,11 +35,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Maps; import java.util.ArrayList; import java.util.Collection; @@ -52,8 +51,6 @@ import java.util.stream.IntStream; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder.ASC_NULLS_LAST; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictOutputs; @@ -62,6 +59,8 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.project; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.window; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class GatherAndMergeWindows { private GatherAndMergeWindows() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptAll.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptAll.java index 63dec0a1e78b6..e666f8960774f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptAll.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptAll.java @@ -36,14 +36,14 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.LongType; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ArithmeticBinaryExpression.Operator.SUBTRACT; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.Except.distinct; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class ImplementExceptAll implements Rule { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptDistinctAsUnion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptDistinctAsUnion.java index 9b7ee07376c1e..da0273d205a01 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptDistinctAsUnion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementExceptDistinctAsUnion.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.LongType; import static java.util.Objects.requireNonNull; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectAll.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectAll.java index 2b94174ef3d79..35097613b8019 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectAll.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectAll.java @@ -33,8 +33,7 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.Intersect.distinct; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectDistinctAsUnion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectDistinctAsUnion.java index d286e40bf0155..a596a3ed099ef 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectDistinctAsUnion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementIntersectDistinctAsUnion.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.LongType; import static java.util.Objects.requireNonNull; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementPatternRecognition.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementPatternRecognition.java index 656b10b33a533..415270d3c6770 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementPatternRecognition.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementPatternRecognition.java @@ -34,8 +34,8 @@ import java.util.List; import java.util.Map; -import static com.google.common.collect.Iterables.getOnlyElement; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.patternRecognition; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class ImplementPatternRecognition implements Rule { private static final Pattern PATTERN = patternRecognition(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementTableFunctionSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementTableFunctionSource.java index 47a8b945f7afe..98bad124f1d4e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementTableFunctionSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ImplementTableFunctionSource.java @@ -31,8 +31,7 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.HashMap; @@ -41,8 +40,8 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -import static com.google.common.collect.Iterables.getOnlyElement; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder.ASC_NULLS_LAST; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; /** * This rule prepares cartesian product of partitions from all inputs of table function. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/InlineProjections.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/InlineProjections.java index 52144a519bed2..44ec204ee20be 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/InlineProjections.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/InlineProjections.java @@ -34,10 +34,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Sets; import java.util.LinkedHashMap; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithMergeSort.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithMergeSort.java index 87b8075599aed..f097f5bc64d40 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithMergeSort.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithMergeSort.java @@ -28,8 +28,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.MergeLimitWithMergeSort.transformByMergeSortNode; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithSort.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithSort.java index fbc4e790c2876..7b57f8cda1fb3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithSort.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MergeLimitOverProjectWithSort.java @@ -28,8 +28,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.StreamSortNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.limit; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MultipleDistinctAggregationToMarkDistinct.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MultipleDistinctAggregationToMarkDistinct.java index 53682050a7ace..3bd63c5778d3b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MultipleDistinctAggregationToMarkDistinct.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/MultipleDistinctAggregationToMarkDistinct.java @@ -26,11 +26,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.MarkDistinctNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; import java.util.HashMap; import java.util.HashSet; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ProjectOffPushDownRule.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ProjectOffPushDownRule.java index e6976185d244c..fc9f245e6d3ed 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ProjectOffPushDownRule.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ProjectOffPushDownRule.java @@ -26,8 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationColumns.java index c0ee4cd795a3f..460894103deb9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationColumns.java @@ -22,8 +22,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; - -import com.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Maps; import java.util.Map; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationSourceColumns.java index a5d06b8788615..60f72cdbdf55a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneAggregationSourceColumns.java @@ -25,15 +25,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.Streams; import java.util.Set; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.aggregation; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PruneAggregationSourceColumns implements Rule { private static final Pattern PATTERN = aggregation(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyColumns.java index 74901facbd487..76ac32d20fff3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyColumns.java @@ -22,17 +22,16 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ApplyNode; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Map; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.Sets.intersection; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.applyNode; +import static org.apache.iotdb.google.common.collect.Sets.intersection; /** * This rule restricts the outputs of ApplyNode's input and subquery based on which ApplyNode's diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyCorrelation.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyCorrelation.java index 3d1b93b7aabe1..9db00cef67b06 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyCorrelation.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplyCorrelation.java @@ -28,9 +28,9 @@ import java.util.List; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor.extractUnique; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.applyNode; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * This rule updates ApplyNode's correlation list. A symbol can be removed from the correlation list diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplySourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplySourceColumns.java index d3cc7382b52f3..02b26702c0b1c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplySourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneApplySourceColumns.java @@ -25,15 +25,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ApplyNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.applyNode; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; /** * This rule restricts outputs of ApplyNode's subquery to include only the symbols needed for diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCopyToColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCopyToColumns.java index 4ef3b95185855..cfa1ddb946c66 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCopyToColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCopyToColumns.java @@ -23,8 +23,7 @@ import org.apache.iotdb.calc.plan.relational.utils.matching.Pattern; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.CopyToNode; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.copyTo; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinColumns.java index 5364d492791fd..e9d9fa7f4cf21 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinColumns.java @@ -23,19 +23,18 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.CorrelatedJoinNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.JoinNode; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.Sets.intersection; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor.extractUnique; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.isAtMostScalar; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.isScalar; +import static org.apache.iotdb.google.common.collect.Sets.intersection; public class PruneCorrelatedJoinColumns extends ProjectOffPushDownRule { public PruneCorrelatedJoinColumns() { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinCorrelation.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinCorrelation.java index 887bce83cd584..39e8899e6ef5d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinCorrelation.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneCorrelatedJoinCorrelation.java @@ -28,9 +28,9 @@ import java.util.List; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor.extractUnique; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class PruneCorrelatedJoinCorrelation implements Rule { private static final Pattern PATTERN = correlatedJoin(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneDistinctAggregation.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneDistinctAggregation.java index a00b131efa895..6770e9f66d9b8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneDistinctAggregation.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneDistinctAggregation.java @@ -29,14 +29,13 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.ChildReplacer.replaceChildren; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.aggregation; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class PruneDistinctAggregation implements Rule { private static final Pattern PATTERN = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExceptSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExceptSourceColumns.java index 6606e7f719664..3b570c98ba9e1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExceptSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExceptSourceColumns.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ExceptNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java index db5291a9d3873..4cc4749229d24 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java @@ -23,8 +23,7 @@ import org.apache.iotdb.calc.plan.relational.utils.matching.Pattern; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExplainAnalyzeNode; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.explainAnalyze; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFillColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFillColumns.java index d1a061ea90e8b..a7c90ba0d1194 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFillColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFillColumns.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.LinearFillNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.NextFillNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.PreviousFillNode; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Optional; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFilterColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFilterColumns.java index 767c57ab913f3..98199e981076f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFilterColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneFilterColumns.java @@ -23,15 +23,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.FilterNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; - -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.Streams; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.filter; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PruneFilterColumns extends ProjectOffPushDownRule { public PruneFilterColumns() { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneGapFillColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneGapFillColumns.java index 4d2ed49335cf3..e414afeaa7ef9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneGapFillColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneGapFillColumns.java @@ -22,16 +22,15 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.GapFillNode; - -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.Streams; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.gapFill; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PruneGapFillColumns extends ProjectOffPushDownRule { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneIntersectSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneIntersectSourceColumns.java index 5186b07ebf9bf..bc53cfcf3f22a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneIntersectSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneIntersectSourceColumns.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.IntersectNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneJoinChildrenColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneJoinChildrenColumns.java index 8562b70997263..9e15fc988c677 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneJoinChildrenColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneJoinChildrenColumns.java @@ -24,8 +24,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.JoinNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneMarkDistinctColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneMarkDistinctColumns.java index db3b5cb9c4048..1452aead92cfa 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneMarkDistinctColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneMarkDistinctColumns.java @@ -22,16 +22,15 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.MarkDistinctNode; - -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.Streams; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.markDistinct; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PruneMarkDistinctColumns extends ProjectOffPushDownRule { public PruneMarkDistinctColumns() { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneOutputSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneOutputSourceColumns.java index c76b0ae2f4706..b5a0c6bd7dfa3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneOutputSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneOutputSourceColumns.java @@ -23,8 +23,7 @@ import org.apache.iotdb.calc.plan.relational.utils.matching.Pattern; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.OutputNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.output; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PrunePatternRecognitionSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PrunePatternRecognitionSourceColumns.java index 0c964b6b42ca9..26c4f9a3a328f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PrunePatternRecognitionSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PrunePatternRecognitionSourceColumns.java @@ -26,8 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.PatternRecognitionNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.ExpressionAndValuePointers; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.RowsPerMatch.ONE; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneSortColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneSortColumns.java index 157bf2712750d..48568c935468b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneSortColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneSortColumns.java @@ -22,15 +22,14 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SortNode; - -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.Streams; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.sort; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PruneSortColumns extends ProjectOffPushDownRule { public PruneSortColumns() { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorColumns.java index 5930f59a566de..58d3bf851751c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorColumns.java @@ -28,8 +28,8 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.tableFunctionProcessor; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * TableFunctionProcessorNode has two kinds of outputs: diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorSourceColumns.java index 3922f4a25de94..efaef34cc47df 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTableFunctionProcessorSourceColumns.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TableFunctionNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TableFunctionProcessorNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTopKColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTopKColumns.java index 2ec929fb23b45..4a338f3929007 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTopKColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneTopKColumns.java @@ -21,15 +21,14 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKNode; - -import com.google.common.collect.Streams; +import org.apache.iotdb.google.common.collect.Streams; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.topK; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class PruneTopKColumns extends ProjectOffPushDownRule { public PruneTopKColumns() { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionColumns.java index 1c4bd76b7abd8..92aae2e135d87 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionColumns.java @@ -22,16 +22,15 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.UnionNode; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; import java.util.Map; import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableListMultimap.toImmutableListMultimap; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.union; +import static org.apache.iotdb.google.common.collect.ImmutableListMultimap.toImmutableListMultimap; /** * Transforms diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionSourceColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionSourceColumns.java index eaf3c91f7aba2..06396a660c10a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionSourceColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneUnionSourceColumns.java @@ -24,8 +24,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.UnionNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneWindowColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneWindowColumns.java index b5c5e5a303f91..c7866b2b2f3ee 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneWindowColumns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneWindowColumns.java @@ -23,9 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.WindowNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Maps; import java.util.Map; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownFilterIntoWindow.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownFilterIntoWindow.java index 03a60885872ba..5e64f99bf217b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownFilterIntoWindow.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownFilterIntoWindow.java @@ -34,13 +34,11 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; import java.util.OptionalInt; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; @@ -48,6 +46,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.filter; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.window; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class PushDownFilterIntoWindow implements Rule { private static final Capture childCapture = newCapture(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownLimitIntoWindow.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownLimitIntoWindow.java index 0bde084e5131f..c7a03f5c3a06f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownLimitIntoWindow.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownLimitIntoWindow.java @@ -27,12 +27,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKRankingNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.WindowNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.lang.Math.toIntExact; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKRankingNode.RankingType.ROW_NUMBER; @@ -41,6 +39,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.limit; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.window; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class PushDownLimitIntoWindow implements Rule { private static final Capture childCapture = newCapture(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownNegationsExpressionRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownNegationsExpressionRewriter.java index 902a946b5af7b..2600f5816b4a6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownNegationsExpressionRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushDownNegationsExpressionRewriter.java @@ -28,8 +28,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.DoubleType; import org.apache.tsfile.read.common.type.FloatType; import org.apache.tsfile.read.common.type.Type; @@ -37,8 +37,6 @@ import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression.Operator.GREATER_THAN; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression.Operator.GREATER_THAN_OR_EQUAL; @@ -47,6 +45,8 @@ import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression.Operator.LESS_THAN_OR_EQUAL; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.combinePredicates; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.extractPredicates; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public final class PushDownNegationsExpressionRewriter { public static Expression pushDownNegations( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughOffset.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughOffset.java index fc7e35dbf0898..9c04a704951e7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughOffset.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughOffset.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.LimitNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.OffsetNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import static java.lang.Math.addExact; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughUnion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughUnion.java index 2b540cdd1126f..79574caa912cf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughUnion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushLimitThroughUnion.java @@ -26,8 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.LimitNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.UnionNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushProjectionThroughUnion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushProjectionThroughUnion.java index 6f221fe968c12..825ee881040b8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushProjectionThroughUnion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushProjectionThroughUnion.java @@ -30,9 +30,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; import org.apache.tsfile.read.common.type.Type; import java.util.HashMap; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushTopKThroughUnion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushTopKThroughUnion.java index 432cbadfbd2b0..dec228b6a77be 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushTopKThroughUnion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PushTopKThroughUnion.java @@ -28,20 +28,19 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.UnionNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.SymbolMapper; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collections; import java.util.Set; -import static com.google.common.collect.Iterables.getLast; -import static com.google.common.collect.Sets.intersection; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.topK; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.union; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.isAtMost; +import static org.apache.iotdb.google.common.collect.Iterables.getLast; +import static org.apache.iotdb.google.common.collect.Sets.intersection; public class PushTopKThroughUnion implements Rule { private static final Capture CHILD = newCapture(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyExceptBranches.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyExceptBranches.java index 405fd5ab7d8a0..5b0de93a2828c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyExceptBranches.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyExceptBranches.java @@ -29,11 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ListMultimap; import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyUnionBranches.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyUnionBranches.java index 10122129f4de1..a4b32752b6975 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyUnionBranches.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveEmptyUnionBranches.java @@ -27,10 +27,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.UnionNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ListMultimap; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantIdentityProjections.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantIdentityProjections.java index e89881e397b33..c71734c929321 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantIdentityProjections.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantIdentityProjections.java @@ -23,8 +23,7 @@ import org.apache.iotdb.calc.plan.relational.utils.matching.Pattern; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.project; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantWindow.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantWindow.java index e093498b25b94..ebf35fd1de026 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantWindow.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveRedundantWindow.java @@ -24,8 +24,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ValuesNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.WindowNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.window; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.isEmpty; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveTrivialFilters.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveTrivialFilters.java index 5dff1f20cea0d..fe13159818953 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveTrivialFilters.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/RemoveTrivialFilters.java @@ -27,9 +27,9 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.filter; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class RemoveTrivialFilters implements Rule { private static final Pattern PATTERN = filter(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ReplaceWindowWithRowNumber.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ReplaceWindowWithRowNumber.java index 354e1ad13eb96..c2897c62e3876 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ReplaceWindowWithRowNumber.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/ReplaceWindowWithRowNumber.java @@ -29,8 +29,8 @@ import java.util.Optional; -import static com.google.common.collect.Iterables.getOnlyElement; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.window; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class ReplaceWindowWithRowNumber implements Rule { private final Pattern pattern; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationMerge.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationMerge.java index 5c1c283ea7a98..e73c148a1a294 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationMerge.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationMerge.java @@ -28,9 +28,8 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Collection; @@ -38,8 +37,8 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class SetOperationMerge { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationNodeTranslator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationNodeTranslator.java index 1f53fe3751a0b..c48b9a2939dd7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationNodeTranslator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SetOperationNodeTranslator.java @@ -46,12 +46,12 @@ import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import org.apache.tsfile.read.common.type.Type; import java.util.Collection; @@ -60,15 +60,15 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.Iterables.concat; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.utils.constant.SqlConstant.COUNT; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleGroupingSet; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toSqlType; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.Util.getResolvedBuiltInAggregateFunction; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.Iterables.concat; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; import static org.apache.tsfile.read.common.type.LongType.INT64; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyCountOverConstant.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyCountOverConstant.java index c500ca1fd3550..9990500c19992 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyCountOverConstant.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyCountOverConstant.java @@ -34,8 +34,8 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.LongType; import java.util.Collections; @@ -43,7 +43,6 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.calc.utils.constant.SqlConstant.COUNT; @@ -54,6 +53,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.aggregation; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.project; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; +import static org.apache.iotdb.google.common.base.Verify.verify; public class SimplifyCountOverConstant implements Rule { private static final Capture CHILD = newCapture(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyExpressions.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyExpressions.java index b381ca69eae78..b5c9acd0be0c1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyExpressions.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SimplifyExpressions.java @@ -31,8 +31,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.read.common.type.Type; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SingleDistinctAggregationToGroupBy.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SingleDistinctAggregationToGroupBy.java index 64ecfb346aa4f..bae2fa643eeff 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SingleDistinctAggregationToGroupBy.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/SingleDistinctAggregationToGroupBy.java @@ -26,10 +26,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Iterables; import java.util.HashSet; import java.util.List; @@ -38,10 +37,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleAggregation; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleGroupingSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.aggregation; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * Implements distinct aggregations with similar inputs by transforming plans of the following diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithProjection.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithProjection.java index 1409a6cda8e96..be816018f1373 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithProjection.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithProjection.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.LongType; import java.util.HashSet; @@ -44,7 +44,6 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.nonEmpty; @@ -58,6 +57,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.project; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * This rule decorrelates a correlated subquery of LEFT correlated join with distinct operator diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithoutProjection.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithoutProjection.java index 07a699c357600..aab39a4c026d4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithoutProjection.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedDistinctAggregationWithoutProjection.java @@ -33,9 +33,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.read.common.type.BooleanType; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithProjection.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithProjection.java index 49f7332498040..3d7f26fb004b4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithProjection.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithProjection.java @@ -36,9 +36,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.BooleanType; import org.apache.tsfile.read.common.type.LongType; @@ -48,8 +48,6 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.empty; @@ -69,6 +67,8 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.project; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * This rule decorrelates a correlated subquery of LEFT or INNER correlated join with: - single diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithoutProjection.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithoutProjection.java index 9f21121dc4527..d90ac9f9b22ee 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithoutProjection.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGlobalAggregationWithoutProjection.java @@ -36,17 +36,16 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.read.common.type.BooleanType; import org.apache.tsfile.read.common.type.LongType; import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.empty; @@ -66,6 +65,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.aggregation; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * This rule decorrelates a correlated subquery with: - single global aggregation, or - global diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithProjection.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithProjection.java index 2b85826eb3404..755b688530ca3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithProjection.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithProjection.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.BooleanType; import java.util.HashSet; @@ -44,7 +44,6 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.nonEmpty; @@ -61,6 +60,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.project; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * This rule decorrelates a correlated subquery of INNER correlated join with: - single grouped diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithoutProjection.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithoutProjection.java index 716faf5ae713f..1cfe58ed8ac9d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithoutProjection.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedGroupedAggregationWithoutProjection.java @@ -33,9 +33,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.read.common.type.BooleanType; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedJoinToJoin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedJoinToJoin.java index a42df1389db9e..d712fd420cbb4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedJoinToJoin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedJoinToJoin.java @@ -29,12 +29,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.nonEmpty; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.JoinNode.JoinType.INNER; @@ -43,6 +41,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.combineConjuncts; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.CorrelatedJoin.correlation; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * Tries to decorrelate subquery and rewrite it using normal join. Decorrelated predicates are part diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedScalarSubquery.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedScalarSubquery.java index 283f76795780f..355781c7fae7f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedScalarSubquery.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformCorrelatedScalarSubquery.java @@ -40,12 +40,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.Cardinality; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.nonEmpty; import static org.apache.iotdb.calc.transformation.dag.column.FailFunctionColumnTransformer.FAIL_FUNCTION_NAME; @@ -58,6 +56,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.CorrelatedJoin.filter; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.extractCardinality; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; import static org.apache.tsfile.read.common.type.LongType.INT64; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformExistsApplyToCorrelatedJoin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformExistsApplyToCorrelatedJoin.java index db72625917b6f..6f9f70ead6aea 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformExistsApplyToCorrelatedJoin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformExistsApplyToCorrelatedJoin.java @@ -39,15 +39,13 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlannerContext; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanNodeDecorrelator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.LongType; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.globalAggregation; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleAggregation; @@ -58,6 +56,8 @@ import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toSqlType; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.applyNode; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.Util.getResolvedBuiltInAggregateFunction; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; /** diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformFilteringSemiJoinToInnerJoin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformFilteringSemiJoinToInnerJoin.java index 9679961291f20..8e36d01e21076 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformFilteringSemiJoinToInnerJoin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformFilteringSemiJoinToInnerJoin.java @@ -31,15 +31,13 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SemiJoinNode; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.List; import java.util.Optional; import java.util.function.Predicate; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.calc.plan.relational.utils.matching.Capture.newCapture; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleAggregation; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleGroupingSet; @@ -51,6 +49,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.filter; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.semiJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * Rewrite filtering semi-join to inner join. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedInPredicateSubqueryToSemiJoin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedInPredicateSubqueryToSemiJoin.java index 03b426616a409..522dcf01ee19e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedInPredicateSubqueryToSemiJoin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedInPredicateSubqueryToSemiJoin.java @@ -26,10 +26,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SemiJoinNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; -import static com.google.common.collect.Iterables.getOnlyElement; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.empty; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.Apply.correlation; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.applyNode; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; /** * This optimizers looks for InPredicate expressions in ApplyNodes and replaces the nodes with diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedSubqueryToJoin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedSubqueryToJoin.java index 7843e52ebf06b..318d4058268c9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedSubqueryToJoin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/TransformUncorrelatedSubqueryToJoin.java @@ -33,21 +33,21 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import org.apache.tsfile.read.common.type.Type; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.empty; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toSqlType; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.CorrelatedJoin.correlation; import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.correlatedJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.extractCardinality; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class TransformUncorrelatedSubqueryToJoin implements Rule { private static final Pattern PATTERN = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/Util.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/Util.java index 6d8a639c7b468..92b4493764041 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/Util.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/Util.java @@ -29,21 +29,20 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Sets; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKRankingNode.RankingType.RANK; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TopKRankingNode.RankingType.ROW_NUMBER; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; final class Util { private Util() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/AggregationTableScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/AggregationTableScanNode.java index 70a4198fff930..6f8b0f8dc5708 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/AggregationTableScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/AggregationTableScanNode.java @@ -37,9 +37,9 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.LongType; import org.apache.tsfile.read.common.type.TimestampType; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -57,13 +57,13 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.utils.constant.SqlConstant.COUNT; import static org.apache.iotdb.calc.utils.constant.SqlConstant.TABLE_TIME_COLUMN_NAME; import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction.LAST; import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinAggregationFunction.LAST_BY; import static org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator.DATE_BIN_PREFIX; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class AggregationTableScanNode extends DeviceTableScanNode { // if there is date_bin function of time, we should use this field to transform time input diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/CteScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/CteScanNode.java index aee78e4330b8a..d880193668a2a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/CteScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/CteScanNode.java @@ -32,8 +32,7 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.DataNodeEndPoints; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; @@ -41,7 +40,7 @@ import java.util.List; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CteScanNode extends SourceNode { private final QualifiedName qualifiedName; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileAggregationScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileAggregationScanNode.java index 4934b7f3b3c23..1a2df119e8d23 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileAggregationScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileAggregationScanNode.java @@ -33,8 +33,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.function.tvf.read_tsfile.ExternalTsFileQueryResource; import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; - -import com.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Lists; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileScanNode.java index 160d54351e651..c1b6230ed4d9c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExternalTsFileScanNode.java @@ -31,8 +31,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.function.tvf.read_tsfile.ExternalTsFileQueryResource; import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; - -import com.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Lists; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/IntoNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/IntoNode.java index 02e609a2ebb09..7ceb11f23e259 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/IntoNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/IntoNode.java @@ -29,9 +29,9 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Insert; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java index 1728d182c4177..d858a60a43030 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java @@ -59,11 +59,11 @@ import java.util.List; import java.util.Optional; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static org.apache.iotdb.calc.plan.relational.utils.matching.Pattern.typeOf; import static org.apache.iotdb.calc.plan.relational.utils.matching.Property.optionalProperty; import static org.apache.iotdb.calc.plan.relational.utils.matching.Property.property; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public final class Patterns { private Patterns() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Cardinality.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Cardinality.java index 62e87f39fe560..094d26d0cd2b0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Cardinality.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Cardinality.java @@ -20,8 +20,7 @@ package org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.collect.Range; +import org.apache.iotdb.google.common.collect.Range; import static java.util.Objects.requireNonNull; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/CheckSubqueryNodesAreRewritten.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/CheckSubqueryNodesAreRewritten.java index 9ded3a9c87f4d..92b18094da4e0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/CheckSubqueryNodesAreRewritten.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/CheckSubqueryNodesAreRewritten.java @@ -28,8 +28,8 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.relational.planner.PlanNodeSearcher.searchFrom; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; import static org.apache.iotdb.rpc.TSStatusCode.SEMANTIC_ERROR; public class CheckSubqueryNodesAreRewritten implements PlanOptimizer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DistributedOptimizeFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DistributedOptimizeFactory.java index 3d688610f0111..2af3530552467 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DistributedOptimizeFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/DistributedOptimizeFactory.java @@ -27,9 +27,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.MergeLimitOverProjectWithMergeSort; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.MergeLimitWithMergeSort; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PushDownOffsetIntoTableScan; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/JoinUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/JoinUtils.java index b6beb9b83fc46..092d9973e2264 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/JoinUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/JoinUtils.java @@ -26,21 +26,20 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.EqualityInference; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.Objects; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; import static org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor.extractUnique; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.DeterminismEvaluator.isDeterministic; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.combineConjuncts; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.extractConjuncts; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.filterDeterministicConjuncts; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class JoinUtils { public static final String UNSUPPORTED_JOIN_CRITERIA = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java index 78bb5f984a15c..fa9014f777aed 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java @@ -109,9 +109,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.TransformExistsApplyToCorrelatedJoin; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.TransformUncorrelatedInPredicateSubqueryToSemiJoin; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.TransformUncorrelatedSubqueryToJoin; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Set; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PlanNodeDecorrelator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PlanNodeDecorrelator.java index fc73837d8703d..a7535d28ebce1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PlanNodeDecorrelator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PlanNodeDecorrelator.java @@ -42,14 +42,13 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolsExtractor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.type.TypeCoercion; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMultimap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Multimap; +import org.apache.iotdb.google.common.collect.Sets; import java.util.List; import java.util.Map; @@ -57,9 +56,6 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleAggregation; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleGroupingSet; @@ -69,6 +65,9 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.combineConjuncts; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.extractConjuncts; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.SymbolMapper.symbolMapper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; public class PlanNodeDecorrelator { private final PlannerContext plannerContext; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java index 9f3c5c32e25a1..f0588ca464011 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushAggregationIntoTableScan.java @@ -38,8 +38,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExternalTsFileAggregationScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExternalTsFileScanNode; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.utils.Pair; import java.util.ArrayList; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java index 1ceb4c86f6c17..9528418e2f774 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java @@ -81,10 +81,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExternalTsFileScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.InformationSchemaTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeDeviceViewScanNode; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.filter.basic.Filter; import org.apache.tsfile.utils.Pair; @@ -106,9 +106,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.calc.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.extractGlobalTimeFilter; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder.ASC_NULLS_FIRST; @@ -144,6 +141,9 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.JoinUtils.processInnerJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.JoinUtils.processLimitedOuterJoin; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.QueryCardinalityUtil.extractCardinality; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; /** * Optimization phase: Logical plan planning. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/QueryCardinalityUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/QueryCardinalityUtil.java index 0d8427c985f98..a91709f83003f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/QueryCardinalityUtil.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/QueryCardinalityUtil.java @@ -33,17 +33,16 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.ExchangeNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode; - -import com.google.common.collect.Range; +import org.apache.iotdb.google.common.collect.Range; import java.util.List; import java.util.stream.Collectors; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.lang.Math.max; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Lookup.noLookup; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public final class QueryCardinalityUtil { private QueryCardinalityUtil() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SortElimination.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SortElimination.java index b5ed662cdae42..335232bec4658 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SortElimination.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SortElimination.java @@ -33,8 +33,7 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneTableScanColumns; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collections; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SymbolMapper.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SymbolMapper.java index 96a377d1a8976..e2bb55e137576 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SymbolMapper.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SymbolMapper.java @@ -47,9 +47,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.HashSet; import java.util.List; @@ -59,10 +58,10 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.groupingSets; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class SymbolMapper { private final Function mappingFunction; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java index 77470247488e7..91abd627a0a0d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformAggregationToStreamable.java @@ -36,9 +36,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformQuantifiedComparisonApplyToCorrelatedJoin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformQuantifiedComparisonApplyToCorrelatedJoin.java index ca6f7b9c9f835..d169314010c3c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformQuantifiedComparisonApplyToCorrelatedJoin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/TransformQuantifiedComparisonApplyToCorrelatedJoin.java @@ -43,9 +43,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.SimplePlanRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.LongType; import org.apache.tsfile.read.common.type.Type; @@ -54,8 +54,6 @@ import java.util.Optional; import java.util.function.Function; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.globalAggregation; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.singleAggregation; @@ -71,6 +69,8 @@ import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureTranslator.toSqlType; import static org.apache.iotdb.db.queryengine.plan.relational.planner.SimplePlanRewriter.rewriteWith; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.combineConjuncts; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; import static org.apache.tsfile.read.common.type.BooleanType.BOOLEAN; public class TransformQuantifiedComparisonApplyToCorrelatedJoin implements PlanOptimizer { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java index a938793cd2a5b..b11a3d7d57e3a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java @@ -73,13 +73,12 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.InformationSchemaTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.IntoNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeDeviceViewScanNode; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.Sets; import java.util.AbstractMap.SimpleEntry; import java.util.Collection; @@ -93,12 +92,12 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.JoinNode.JoinType.INNER; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.SymbolMapper.symbolMapper; import static org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.SymbolMapper.symbolReallocator; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; /** * Re-maps symbol references that are just aliases of each other (e.g., due to projections like diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Util.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Util.java index b18af7913851a..79cb0ae123633 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Util.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/Util.java @@ -33,8 +33,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTreeDeviceViewScanNode; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.Pair; @@ -44,11 +44,11 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.Step.FINAL; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.Step.INTERMEDIATE; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.Step.PARTIAL; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode.Step.SINGLE; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class Util { private Util() {} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/rowpattern/rowpattern/RowPatternToIrRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/rowpattern/rowpattern/RowPatternToIrRewriter.java index 10cc8a5921c3b..9922736805f5f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/rowpattern/rowpattern/RowPatternToIrRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/rowpattern/rowpattern/RowPatternToIrRewriter.java @@ -51,12 +51,12 @@ import java.util.List; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrQuantifier.oneOrMore; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrQuantifier.range; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrQuantifier.zeroOrMore; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrQuantifier.zeroOrOne; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class RowPatternToIrRewriter implements AstVisitor { private final Analysis analysis; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java index 421b16236a808..5be8465929bad 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java @@ -170,11 +170,10 @@ import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.SetThrottleQuotaStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.ShowSpaceQuotaStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.quota.ShowThrottleQuotaStatement; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.collect.ImmutableList; - import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ParameterExtractor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ParameterExtractor.java index 49f836872627d..b6c012b7cbfbb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ParameterExtractor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ParameterExtractor.java @@ -27,8 +27,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DefaultTraversalVisitor; - -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.Comparator; @@ -36,7 +35,7 @@ import java.util.List; import java.util.Map; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** Utility class for extracting and binding parameters in prepared statements. */ public final class ParameterExtractor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractTraverseDevice.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractTraverseDevice.java index 310da43306004..01546580ec393 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractTraverseDevice.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractTraverseDevice.java @@ -36,8 +36,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry; import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableDeviceSchemaFetcher; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExtractCommonPredicatesExpressionRewriter; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AddColumn.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AddColumn.java index 79c43a55037e0..e8d119f8f9834 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AddColumn.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AddColumn.java @@ -33,8 +33,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AddColumn extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterDB.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterDB.java index 04a214af719fa..959c95221d922 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterDB.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterDB.java @@ -28,8 +28,8 @@ import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AlterDB extends DatabaseStatement { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(AlterDB.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterPipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterPipe.java index 089e9dcbe877f..b3df11ba624ad 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterPipe.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterPipe.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AlterPipe extends PipeStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterTopic.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterTopic.java index 8598c1525266d..6d1da2baf5a57 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterTopic.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AlterTopic.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AlterTopic extends SubscriptionStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AsofJoinOn.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AsofJoinOn.java index 915d1c5c1f2b6..30fdd9c577619 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AsofJoinOn.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AsofJoinOn.java @@ -29,18 +29,18 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LogicalExpression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TimeDuration; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression.Operator.LESS_THAN_OR_EQUAL; import static org.apache.iotdb.db.queryengine.plan.relational.planner.ir.IrUtils.and; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AsofJoinOn extends JoinOn { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ClearCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ClearCache.java index 844d2eaaa7be8..5241155b42935 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ClearCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ClearCache.java @@ -24,15 +24,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.commons.schema.cache.CacheClearOptions; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ClearCache extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ColumnDefinition.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ColumnDefinition.java index dad80ed104d4e..e86e4e5be4012 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ColumnDefinition.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ColumnDefinition.java @@ -28,8 +28,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -39,8 +39,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ColumnDefinition extends Node { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CopyTo.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CopyTo.java index 49ff997998cb3..c12f357e02984 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CopyTo.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CopyTo.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.queryengine.execution.operator.process.copyto.CopyToOptions; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java index bbdbbb63e3751..b3fe059c7995e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountStatement.java index 7c4501dd4043e..ac4dec715b70a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountStatement.java @@ -26,16 +26,16 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CountStatement extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateDB.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateDB.java index 143e557eb246c..14311cb457d54 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateDB.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateDB.java @@ -28,8 +28,8 @@ import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateDB extends DatabaseStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java index 57b1968dda900..8366baef4e82a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateExternalService.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateExternalService extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateFunction.java index bd6124d0eb518..21605fefb80ac 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateFunction.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateFunction.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -35,8 +35,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateFunction extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateIndex.java index 8d188cfc6ccd3..d027c88fc0bfc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateIndex.java @@ -27,16 +27,16 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class CreateIndex extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipe.java index 2592ee9aefac4..bffa0da060945 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipe.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipe.java @@ -28,8 +28,8 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreatePipe extends PipeStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipePlugin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipePlugin.java index 204aa3b1bbc61..eb2dfac24827e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipePlugin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreatePipePlugin.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreatePipePlugin extends PipeStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTable.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTable.java index 5331ffbc06b97..d012808f3589a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTable.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTable.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -36,8 +36,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateTable extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTopic.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTopic.java index d660a3977af70..2d487f871388e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTopic.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateTopic.java @@ -28,8 +28,8 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateTopic extends SubscriptionStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateView.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateView.java index a0c2cc94fdf61..eac54bd97c377 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateView.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CreateView.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateView extends CreateTable { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DatabaseStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DatabaseStatement.java index f343787df19d9..2157b719da4c6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DatabaseStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DatabaseStatement.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DatabaseSchemaStatement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Deallocate.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Deallocate.java index b70af8727d1fb..a3c92b70909eb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Deallocate.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Deallocate.java @@ -26,15 +26,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; /** DEALLOCATE PREPARE statement AST node. Example: DEALLOCATE PREPARE stmt1 */ public final class Deallocate extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Delete.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Delete.java index e5e4f2396cc3e..10c23b7ff09ed 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Delete.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Delete.java @@ -29,8 +29,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Table; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -40,8 +40,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Delete extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java index 39bc2ecfed5dd..31f5a4e4d495f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DeleteDevice.java @@ -46,9 +46,9 @@ import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.storageengine.dataregion.modification.ModEntry; import org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.read.common.type.TypeFactory; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; @@ -67,7 +67,7 @@ import java.util.function.BiFunction; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DeleteDevice extends AbstractTraverseDevice { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeTable.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeTable.java index a7c05cb44be2e..e92ae58cc2148 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeTable.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeTable.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; @@ -35,8 +35,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DescribeTable extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropColumn.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropColumn.java index 1878ac9cbd484..dfa435dd71773 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropColumn.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropColumn.java @@ -27,15 +27,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropColumn extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropDB.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropDB.java index 9cd37ee5ea5b0..b1d601da506c4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropDB.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropDB.java @@ -26,15 +26,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropDB extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java index c9d0443c5b0c9..f9b102329ce9a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropExternalService.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropExternalService extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropFunction.java index 768149848b12d..fb390de66dcbd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropFunction.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropFunction.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropFunction extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropIndex.java index 61e530e626926..d85eaccd48a83 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropIndex.java @@ -27,15 +27,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropIndex extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipe.java index e8e37a2b7929c..a25f874b8d40d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipe.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipe.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropPipe extends PipeStatement { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(DropPipe.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipePlugin.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipePlugin.java index 497c02e8e9327..db203c1ac5ca9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipePlugin.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropPipePlugin.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropPipePlugin extends PipeStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropSubscription.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropSubscription.java index 5001453bf3b2b..89d52ce847e10 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropSubscription.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropSubscription.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropSubscription extends SubscriptionStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTable.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTable.java index 9879e4cc6865c..0b09f7546e66f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTable.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTable.java @@ -26,15 +26,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropTable extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTopic.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTopic.java index 82abba4191a48..df4bd4054abc2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTopic.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DropTopic.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropTopic extends SubscriptionStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Execute.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Execute.java index 98a460005c212..c5c7cf996165a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Execute.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Execute.java @@ -27,15 +27,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; /** EXECUTE statement AST node. Example: EXECUTE stmt1 USING 100, 'test' */ public final class Execute extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExecuteImmediate.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExecuteImmediate.java index f2252fa591af9..a1bd6a5b39e5f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExecuteImmediate.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExecuteImmediate.java @@ -27,15 +27,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; /** * EXECUTE IMMEDIATE statement AST node. Example: EXECUTE IMMEDIATE 'SELECT * FROM table WHERE id = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Explain.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Explain.java index 8bcf893b7e931..79ccdc72b6fe1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Explain.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Explain.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Explain extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExplainAnalyze.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExplainAnalyze.java index bbcdec639b9e9..fc3a7c94f9cbb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExplainAnalyze.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExplainAnalyze.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ExplainAnalyze extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExtendRegion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExtendRegion.java index e43c4bee39cc1..22e2832591932 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExtendRegion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ExtendRegion.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Insert.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Insert.java index be3a06c0c67f0..a9703bd3708ab 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Insert.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Insert.java @@ -29,8 +29,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Table; import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.LongType; import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.utils.RamUsageEstimator; @@ -41,8 +41,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class Insert extends Statement { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(Insert.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/KillQuery.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/KillQuery.java index 653a41a72360e..63114ea97a142 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/KillQuery.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/KillQuery.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java index 2e9adadb5766e..0f0f5512f0973 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/LoadTsFile.java @@ -41,8 +41,8 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class LoadTsFile extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/MigrateRegion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/MigrateRegion.java index a24f99b407696..5d20e3c67c7df 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/MigrateRegion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/MigrateRegion.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/PipeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/PipeStatement.java index 74737f28ece35..dd9752e1b69e2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/PipeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/PipeStatement.java @@ -21,8 +21,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Prepare.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Prepare.java index 8b5979e480a0e..c86925a0a4d0f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Prepare.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Prepare.java @@ -26,15 +26,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; /** PREPARE statement AST node. Example: PREPARE stmt1 FROM SELECT * FROM table WHERE id = ? */ public final class Prepare extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Property.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Property.java index 677b979f148ab..63a27b151e851 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Property.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Property.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; @@ -36,9 +36,9 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class Property extends Node { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(Property.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ReconstructRegion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ReconstructRegion.java index 3ac17c5f15c35..f88078be327e6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ReconstructRegion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ReconstructRegion.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java index 1747465be66e1..95653cc229eb1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java @@ -30,9 +30,9 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.rpc.RpcUtils; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.HashSet; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveAINode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveAINode.java index 1d9213b47019f..626baffa5ee0a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveAINode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveAINode.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveConfigNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveConfigNode.java index 7113ce52540d1..e7725e6567e63 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveConfigNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveConfigNode.java @@ -23,14 +23,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class RemoveConfigNode extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveDataNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveDataNode.java index 906dd4d4227b5..b772412e5635c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveDataNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveDataNode.java @@ -23,14 +23,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class RemoveDataNode extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveRegion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveRegion.java index 703c3174eb5a9..82ca865c5ff74 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveRegion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RemoveRegion.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameColumn.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameColumn.java index 7866b03a2c746..b6d3059c885f8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameColumn.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameColumn.java @@ -28,15 +28,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class RenameColumn extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameTable.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameTable.java index eb39c4b176b94..ae939ecfa4f86 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameTable.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RenameTable.java @@ -28,15 +28,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class RenameTable extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetColumnComment.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetColumnComment.java index 117f0bd305415..2f2e420b00156 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetColumnComment.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetColumnComment.java @@ -27,8 +27,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -36,8 +36,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SetColumnComment extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetProperties.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetProperties.java index 81722d2360e8f..827d96d87119f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetProperties.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetProperties.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; @@ -35,8 +35,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SetProperties extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetSqlDialect.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetSqlDialect.java index 95475c2c2b2dd..f8c5f457c15d8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetSqlDialect.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetSqlDialect.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetTableComment.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetTableComment.java index b1dd078f708f5..b91d21150fe4e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetTableComment.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SetTableComment.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; @@ -35,7 +35,7 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SetTableComment extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAIDevices.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAIDevices.java index 58e6847e29332..e3c405b965e4c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAIDevices.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAIDevices.java @@ -28,7 +28,7 @@ import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowAIDevices extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAINodes.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAINodes.java index d26ca18343474..0dfa3306cec98 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAINodes.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAINodes.java @@ -25,14 +25,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowAINodes extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAvailableUrls.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAvailableUrls.java index f41e2982e8a54..6e7f82b8b8936 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAvailableUrls.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowAvailableUrls.java @@ -23,13 +23,13 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowAvailableUrls extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCluster.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCluster.java index 9032acf617b7e..c2dd3f01c763c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCluster.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCluster.java @@ -25,16 +25,16 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowCluster extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowClusterId.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowClusterId.java index ef744b5c0987a..b6536b97e3080 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowClusterId.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowClusterId.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowConfigNodes.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowConfigNodes.java index f998b4eee43fa..b6cb93f81ef68 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowConfigNodes.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowConfigNodes.java @@ -25,14 +25,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowConfigNodes extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java index 330a7145faaec..e67ccd48f4bf8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java index a9a6c4cbe598a..9242e615d89d4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java @@ -26,8 +26,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowCreatePipe extends PipeStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java index 6f2822c7ec41b..7ffea0f23b9e7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java @@ -26,8 +26,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowCreateTopic extends SubscriptionStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentDatabase.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentDatabase.java index 462ed02b34aff..57a98419b8053 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentDatabase.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentDatabase.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentSqlDialect.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentSqlDialect.java index 6e6653478b129..6069340713ad9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentSqlDialect.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentSqlDialect.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentTimestamp.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentTimestamp.java index b57b90b41b926..f1f070b1be1c8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentTimestamp.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentTimestamp.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentUser.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentUser.java index 028d9a541bb09..4da2793f25628 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentUser.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCurrentUser.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDB.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDB.java index 6ce49e38bcb9a..ac17d47235ac5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDB.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDB.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDataNodes.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDataNodes.java index ed3324f768d8a..77148b2da4a9b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDataNodes.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDataNodes.java @@ -25,14 +25,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowDataNodes extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowFunctions.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowFunctions.java index e2cc2101bb105..66b0b16813d7c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowFunctions.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowFunctions.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowIndex.java index 5db8a72f10c4b..2de02b71e9bbe 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowIndex.java @@ -26,15 +26,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowIndex extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowModels.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowModels.java index 0e757ac711591..02210bfe15f5b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowModels.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowModels.java @@ -23,13 +23,13 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowModels extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipePlugins.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipePlugins.java index 0745175206f00..c2f008af9c086 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipePlugins.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipePlugins.java @@ -24,7 +24,7 @@ import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowPipePlugins extends PipeStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipes.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipes.java index aef298b2b73cb..d13cb54fbc37e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipes.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowPipes.java @@ -28,7 +28,7 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowPipes extends PipeStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowRegions.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowRegions.java index cfdc1587ebed9..b5b7f4c8cbfa5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowRegions.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowRegions.java @@ -24,14 +24,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowRegions extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowStatement.java index 8296abf808788..0f284217c86af 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowStatement.java @@ -28,16 +28,16 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.OrderBy; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowStatement extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowSubscriptions.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowSubscriptions.java index 81af08652d984..6feb27ccb082c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowSubscriptions.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowSubscriptions.java @@ -26,7 +26,7 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowSubscriptions extends SubscriptionStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTables.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTables.java index 2bbab7f5779ce..b4dd071c69e83 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTables.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTables.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -36,8 +36,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowTables extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTopics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTopics.java index 8dd224f3fe396..680f69684ede6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTopics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowTopics.java @@ -26,7 +26,7 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowTopics extends SubscriptionStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVariables.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVariables.java index ab0f4f6da5853..48b12ad3f7321 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVariables.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVariables.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVersion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVersion.java index accc7ed0f8235..6148e7c3b9c6d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVersion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowVersion.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java index 94dda53110922..cc98c2254e010 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartExternalService.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StartExternalService extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartPipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartPipe.java index 8ce1c13a2346b..b4d2426c53533 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartPipe.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StartPipe.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StartPipe extends PipeStatement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java index a507b6d6ede5c..d482c2c5064da 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopExternalService.java @@ -25,15 +25,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StopExternalService extends Statement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopPipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopPipe.java index 81e0d2b859172..edfec73dd7641 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopPipe.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/StopPipe.java @@ -27,8 +27,8 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StopPipe extends PipeStatement { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(StopPipe.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SubscriptionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SubscriptionStatement.java index 4ecd52267d753..1a3b4b2486528 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SubscriptionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/SubscriptionStatement.java @@ -21,8 +21,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Update.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Update.java index dcbdf218f0fee..84833ed6859a1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Update.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Update.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Table; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -35,8 +35,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Update extends AbstractTraverseDevice { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(Update.class); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/UpdateAssignment.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/UpdateAssignment.java index 067b02244c9de..2a58e0a57541f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/UpdateAssignment.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/UpdateAssignment.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Use.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Use.java index 0c4be16cf099f..5e3199967cdb0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Use.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/Use.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; @@ -35,8 +35,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class Use extends Statement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AntlrATNCacheFields.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AntlrATNCacheFields.java index 9b21d853e423a..aa44ee0bc1a7b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AntlrATNCacheFields.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AntlrATNCacheFields.java @@ -29,8 +29,8 @@ import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class AntlrATNCacheFields { private final ATN atn; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index 87e1b417e44de..cd398643a8e00 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -281,8 +281,8 @@ import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; import org.apache.iotdb.db.utils.DataNodeDateTimeUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; @@ -314,7 +314,6 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.lang.Long.parseLong; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; @@ -354,6 +353,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AsofJoinOn.constructAsofJoinOn; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.QueryUtil.selectList; import static org.apache.iotdb.db.queryengine.plan.relational.sql.util.QueryUtil.table; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class AstBuilder extends RelationalSqlBaseVisitor { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/ErrorHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/ErrorHandler.java index 94c774127e05b..6f581112f8f41 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/ErrorHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/ErrorHandler.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.parser.ParsingException; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.NoViableAltException; import org.antlr.v4.runtime.Parser; @@ -54,8 +54,8 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.firstNonNull; import static org.antlr.v4.runtime.atn.ATNState.RULE_START; +import static org.apache.iotdb.google.common.base.MoreObjects.firstNonNull; public class ErrorHandler extends BaseErrorListener { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewrite.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewrite.java index 8bbad5dafe5fc..2f7d78c6fb6c4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewrite.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewrite.java @@ -27,8 +27,7 @@ import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.execution.warnings.WarningCollector; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.StatementAnalyzerFactory; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Map; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewriteFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewriteFactory.java index 15ec9a9a693d5..165c5f54f3a05 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewriteFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/rewrite/StatementRewriteFactory.java @@ -18,7 +18,7 @@ */ package org.apache.iotdb.db.queryengine.plan.relational.sql.rewrite; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; public class StatementRewriteFactory { private final StatementRewrite statementRewrite; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/AstUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/AstUtil.java index 92d4a7032e128..20ef25e330071 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/AstUtil.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/AstUtil.java @@ -29,9 +29,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral; import org.apache.iotdb.commons.utils.CommonDateTimeUtils; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.graph.SuccessorsFunction; -import com.google.common.graph.Traverser; +import org.apache.iotdb.google.common.graph.SuccessorsFunction; +import org.apache.iotdb.google.common.graph.Traverser; import java.time.ZoneId; import java.util.List; @@ -40,9 +39,9 @@ import java.util.function.Function; import java.util.stream.Stream; -import static com.google.common.collect.Streams.stream; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.parser.ASTVisitor.parseDateTimeFormat; +import static org.apache.iotdb.google.common.collect.Streams.stream; public final class AstUtil { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java index 28a8dc471381a..54ed994af4e75 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java @@ -83,8 +83,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.StopPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Update; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.UpdateAssignment; - -import com.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Joiner; import java.util.List; import java.util.Objects; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/QueryUtil.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/QueryUtil.java index 93cb99766de90..8cb275175c546 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/QueryUtil.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/QueryUtil.java @@ -47,8 +47,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.TableSubquery; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WhenClause; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowDefinition; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/TypeCoercionUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/TypeCoercionUtils.java index 8d3532d93c41e..673ce886e9b48 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/TypeCoercionUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/TypeCoercionUtils.java @@ -19,8 +19,9 @@ package org.apache.iotdb.db.queryengine.plan.relational.type; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; + import org.apache.tsfile.read.common.type.IntType; import org.apache.tsfile.read.common.type.LongType; import org.apache.tsfile.read.common.type.Type; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/DisjointSet.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/DisjointSet.java index fe643f22f55e9..be727f028dbf3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/DisjointSet.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/DisjointSet.java @@ -25,8 +25,8 @@ import java.util.Map; import java.util.Set; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verify; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Verify.verify; public class DisjointSet { private static class Entry { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/MoreLists.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/MoreLists.java index cd589d1ac27d1..20370f5143510 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/MoreLists.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/MoreLists.java @@ -20,8 +20,7 @@ package org.apache.iotdb.db.queryengine.plan.relational.utils; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.function.Function; @@ -29,10 +28,10 @@ import java.util.function.Predicate; import java.util.stream.IntStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Streams.stream; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Streams.stream; public final class MoreLists { public static List> listOfListsCopy(List> lists) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/NodeUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/NodeUtils.java index 042b8c9b4487f..1ba944e8a3438 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/NodeUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/utils/NodeUtils.java @@ -21,8 +21,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.OrderBy; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SortItem; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Optional; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java index 7ae9d8d6f77d7..9ff8df797e1ee 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java @@ -79,9 +79,9 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static com.google.common.util.concurrent.Futures.immediateFuture; import static org.apache.iotdb.calc.metric.QueryExecutionMetricSet.DISPATCH_READ; import static org.apache.iotdb.db.utils.ErrorHandlingUtils.onThriftFrameOversizeException; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; public class FragmentInstanceDispatcherImpl implements IFragInstanceDispatcher { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileDispatcherImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileDispatcherImpl.java index 3f5020cccf31e..63fdaea1eb99c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileDispatcherImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/load/LoadTsFileDispatcherImpl.java @@ -72,7 +72,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import static com.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; public class LoadTsFileDispatcherImpl implements IFragInstanceDispatcher, AutoCloseable { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/OrderByComponent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/OrderByComponent.java index 8f07439b76e55..5c3a66d81b5d6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/OrderByComponent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/component/OrderByComponent.java @@ -27,7 +27,7 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** The order of query result set */ public class OrderByComponent extends StatementNode { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDatabaseStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDatabaseStatement.java index 2367aa0c31264..e94864b5a2547 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDatabaseStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDatabaseStatement.java @@ -29,9 +29,9 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java index f6466a86f0707..444ca5a02c25a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/CreateExternalServiceStatement.java @@ -29,7 +29,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class CreateExternalServiceStatement extends Statement implements IConfigStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java index babcbd433fed9..34e87fe771303 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/DropExternalServiceStatement.java @@ -29,7 +29,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DropExternalServiceStatement extends Statement implements IConfigStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java index b9ac15562d958..99c83fb5cc3b3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/ShowExternalServiceStatement.java @@ -29,7 +29,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ShowExternalServiceStatement extends ShowStatement implements IConfigStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java index 9565dabe1868f..cea46990576b2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StartExternalServiceStatement.java @@ -29,7 +29,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StartExternalServiceStatement extends Statement implements IConfigStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java index 928530bf5c4bc..af9d85f416ec1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/externalservice/StopExternalServiceStatement.java @@ -29,7 +29,7 @@ import java.util.Collections; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StopExternalServiceStatement extends Statement implements IConfigStatement { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java index c9841112df648..442fe4ad48a7c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java @@ -129,9 +129,9 @@ import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; import org.apache.iotdb.db.storageengine.rescon.memory.SystemInfo; import org.apache.iotdb.db.utils.SchemaUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.enums.CompressionType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java index c29f4ec7d3941..2174bd3ce6e65 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java @@ -79,9 +79,9 @@ import org.apache.iotdb.db.schemaengine.template.ClusterTemplateManager; import org.apache.iotdb.db.storageengine.rescon.quotas.DataNodeSpaceQuotaManager; import org.apache.iotdb.db.utils.SchemaUtils; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.enums.CompressionType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java index 188620ef6a51e..9f8749a8da67c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java @@ -70,8 +70,8 @@ import org.apache.iotdb.db.schemaengine.schemaregion.utils.MetaFormatUtils; import org.apache.iotdb.db.schemaengine.schemaregion.utils.filter.DeviceFilterVisitor; import org.apache.iotdb.db.schemaengine.template.ClusterTemplateManager; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.enums.CompressionType; import org.apache.tsfile.file.metadata.enums.TSEncoding; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/ISchemaReader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/ISchemaReader.java index f45560cdf2e12..345dcfc925175 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/ISchemaReader.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/ISchemaReader.java @@ -20,11 +20,10 @@ package org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ISchemaInfo; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; - -import static com.google.common.util.concurrent.Futures.immediateFuture; -import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; /** * ISchemaReader is a non-blocking iterator. diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java index 04f4ca8b6f934..96563766c1a58 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java @@ -21,13 +21,12 @@ import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ISchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import java.util.NoSuchElementException; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; public class SchemaReaderLimitOffsetWrapper implements ISchemaReader { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/TimeseriesReaderWithViewFetch.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/TimeseriesReaderWithViewFetch.java index da4790e104bde..cbb6260091b87 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/TimeseriesReaderWithViewFetch.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/TimeseriesReaderWithViewFetch.java @@ -39,9 +39,9 @@ import org.apache.iotdb.db.schemaengine.schemaregion.utils.filter.TimeseriesFilterVisitor; import org.apache.iotdb.db.schemaengine.schemaregion.view.visitor.GetSourcePathsVisitor; import org.apache.iotdb.db.schemaengine.schemaregion.view.visitor.TransformToExpressionVisitor; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/InformationSchemaUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/InformationSchemaUtils.java index 443352ae919c3..cf0b1a86d3d51 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/InformationSchemaUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/InformationSchemaUtils.java @@ -32,9 +32,9 @@ import org.apache.iotdb.db.protocol.session.IClientSession; import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java index baf326ce53c1d..b5de8421089b7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java @@ -51,9 +51,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.commons.externalservice.ServiceInfo.State.RUNNING; import static org.apache.iotdb.commons.externalservice.ServiceInfo.State.STOPPED; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class ExternalServiceManagementService { @GuardedBy("lock") diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/BloomFilterCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/BloomFilterCache.java index eb0ae5554a57c..5c54a7fefe173 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/BloomFilterCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/BloomFilterCache.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.i18n.StorageEngineMessages; import org.apache.iotdb.db.storageengine.dataregion.read.control.FileReaderManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileID; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Weigher; import org.apache.tsfile.read.TsFileSequenceReader; import org.apache.tsfile.utils.BloomFilter; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/ChunkCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/ChunkCache.java index dc569acfb415a..0615947ad66ec 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/ChunkCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/ChunkCache.java @@ -32,10 +32,10 @@ import org.apache.iotdb.db.queryengine.metric.SeriesScanCostMetricSet; import org.apache.iotdb.db.storageengine.dataregion.read.control.FileReaderManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileID; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Weigher; import org.apache.tsfile.file.metadata.statistics.Statistics; import org.apache.tsfile.read.TsFileSequenceReader; import org.apache.tsfile.read.common.Chunk; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/TimeSeriesMetadataCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/TimeSeriesMetadataCache.java index 68457df0ebbcd..4870d5d6f80de 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/TimeSeriesMetadataCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/buffer/TimeSeriesMetadataCache.java @@ -31,10 +31,10 @@ import org.apache.iotdb.db.queryengine.metric.TimeSeriesMetadataCacheMetrics; import org.apache.iotdb.db.storageengine.dataregion.read.control.FileReaderManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileID; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Weigher; import org.apache.tsfile.common.constant.TsFileConstant; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java index cfdc63edbdfbe..b8ee1d4decfdc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java @@ -168,13 +168,13 @@ import org.apache.iotdb.db.utils.CommonUtils; import org.apache.iotdb.db.utils.EncryptDBUtils; import org.apache.iotdb.db.utils.ModificationUtils; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.google.common.io.BaseEncoding; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.google.common.io.BaseEncoding; import org.apache.thrift.TException; import org.apache.tsfile.external.commons.io.FileUtils; import org.apache.tsfile.external.commons.lang3.tuple.Triple; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/reader/SeriesDataBlockReader.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/reader/SeriesDataBlockReader.java index f89f85100720d..ef2fd4e4bcf39 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/reader/SeriesDataBlockReader.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/execute/utils/reader/SeriesDataBlockReader.java @@ -31,8 +31,8 @@ import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.read.common.block.TsBlock; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/io/CompactionTsFileOutput.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/io/CompactionTsFileOutput.java index cdd89d9ed6455..1851943c291d3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/io/CompactionTsFileOutput.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/io/CompactionTsFileOutput.java @@ -19,7 +19,8 @@ package org.apache.iotdb.db.storageengine.dataregion.compaction.io; -import com.google.common.util.concurrent.RateLimiter; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; + import org.apache.tsfile.write.writer.TsFileOutput; import java.io.IOException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/schedule/CompactionTaskManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/schedule/CompactionTaskManager.java index 169d10137d8be..67eb07bef69a2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/schedule/CompactionTaskManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/compaction/schedule/CompactionTaskManager.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.storageengine.dataregion.compaction.execute.task.CompactionTaskSummary; import org.apache.iotdb.db.storageengine.dataregion.compaction.schedule.comparator.DefaultCompactionTaskComparatorImpl; import org.apache.iotdb.db.utils.datastructure.FixedPriorityBlockingQueue; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.RateLimiter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java index 4b33759af1c40..0cdd17ac48dd2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java @@ -51,8 +51,8 @@ import org.apache.iotdb.db.storageengine.dataregion.tsfile.timeindex.PlainDeviceTimeIndex; import org.apache.iotdb.db.storageengine.dataregion.tsfile.timeindex.TimeIndexLevel; import org.apache.iotdb.db.storageengine.rescon.disk.TierManager; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.file.metadata.IChunkMetadata; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.IDeviceID.Deserializer; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ArrayDeviceTimeIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ArrayDeviceTimeIndex.java index a0ceb57de9a9a..5fe543708606a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ArrayDeviceTimeIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ArrayDeviceTimeIndex.java @@ -26,8 +26,8 @@ import org.apache.iotdb.db.exception.load.PartitionViolationException; import org.apache.iotdb.db.i18n.StorageEngineMessages; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.IDeviceID.Deserializer; import org.apache.tsfile.utils.FilePathUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/FileTimeIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/FileTimeIndex.java index 0230c052392a8..0ff70fef10d18 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/FileTimeIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/FileTimeIndex.java @@ -26,8 +26,8 @@ import org.apache.iotdb.db.exception.load.PartitionViolationException; import org.apache.iotdb.db.i18n.StorageEngineMessages; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.fileSystem.FSFactoryProducer; import org.apache.tsfile.utils.FilePathUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ITimeIndex.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ITimeIndex.java index b5110786943c6..30035d8917dd2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ITimeIndex.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/timeindex/ITimeIndex.java @@ -23,8 +23,8 @@ import org.apache.iotdb.db.exception.load.PartitionViolationException; import org.apache.iotdb.db.i18n.StorageEngineMessages; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java index 220be0cd0c691..ee492649aa4ca 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/LoadTsFileManager.java @@ -56,10 +56,10 @@ import org.apache.iotdb.db.storageengine.load.splitter.ChunkData; import org.apache.iotdb.db.storageengine.load.splitter.DeletionData; import org.apache.iotdb.db.storageengine.load.splitter.TsFileData; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; import org.apache.iotdb.metrics.utils.MetricLevel; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.tsfile.common.constant.TsFileConstant; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.PageException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadPathHelper.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadPathHelper.java index 7792cae1a77dc..ac0fc9cd9e309 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadPathHelper.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/load/active/ActiveLoadPathHelper.java @@ -24,8 +24,7 @@ import org.apache.iotdb.db.i18n.StorageEngineMessages; import org.apache.iotdb.db.queryengine.plan.statement.crud.LoadTsFileStatement; import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; - -import com.google.common.io.BaseEncoding; +import org.apache.iotdb.google.common.io.BaseEncoding; import java.io.File; import java.io.UnsupportedEncodingException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/disk/TierManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/disk/TierManager.java index 87c5cf002739f..6a1bf04d5af2a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/disk/TierManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/rescon/disk/TierManager.java @@ -26,9 +26,9 @@ import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.i18n.StorageEngineMessages; +import org.apache.iotdb.google.common.io.BaseEncoding; import org.apache.iotdb.metrics.utils.FileStoreUtils; -import com.google.common.io.BaseEncoding; import org.apache.ratis.util.MemoizedCheckedSupplier; import org.apache.tsfile.fileSystem.FSFactoryProducer; import org.apache.tsfile.fileSystem.FSType; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java index 1115c465c15f6..8d6ea24a5302e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionBroker.java @@ -28,6 +28,8 @@ import org.apache.iotdb.db.subscription.event.SubscriptionEvent; import org.apache.iotdb.db.subscription.metric.SubscriptionPrefetchingQueueMetrics; import org.apache.iotdb.db.subscription.resource.SubscriptionDataNodeResourceManager; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.LoadingCache; import org.apache.iotdb.pipe.api.event.Event; import org.apache.iotdb.rpc.subscription.config.TopicConstant; import org.apache.iotdb.rpc.subscription.exception.SubscriptionException; @@ -36,8 +38,6 @@ import org.apache.iotdb.rpc.subscription.payload.poll.TerminationPayload; import org.apache.iotdb.session.subscription.util.PollTimer; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java index 0bfccd3a78b4f..34b3d0d070b15 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueue.java @@ -37,6 +37,7 @@ import org.apache.iotdb.db.subscription.event.batch.SubscriptionPipeEventBatches; import org.apache.iotdb.db.subscription.event.batch.SubscriptionPipeTsFileEventBatch; import org.apache.iotdb.db.subscription.task.subtask.SubscriptionReceiverSubtask; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.pipe.api.event.Event; import org.apache.iotdb.pipe.api.event.dml.insertion.TabletInsertionEvent; import org.apache.iotdb.pipe.api.event.dml.insertion.TsFileInsertionEvent; @@ -46,7 +47,6 @@ import org.apache.iotdb.rpc.subscription.payload.poll.SubscriptionPollResponseType; import org.apache.iotdb.session.subscription.util.PollTimer; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueueStates.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueueStates.java index 70eacd2eaaae9..747ee3d74f850 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueueStates.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/SubscriptionPrefetchingQueueStates.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; /** * The {@link SubscriptionPrefetchingQueueStates} manages the state of a {@link diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/TsFileDeduplicationBlockingPendingQueue.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/TsFileDeduplicationBlockingPendingQueue.java index 5f614013e87fb..1ebeca534ef27 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/TsFileDeduplicationBlockingPendingQueue.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/TsFileDeduplicationBlockingPendingQueue.java @@ -25,10 +25,10 @@ import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent; import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; import org.apache.iotdb.pipe.api.event.Event; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/SubscriptionEvent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/SubscriptionEvent.java index 3c99a17f49fc2..41bb0cd92b61f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/SubscriptionEvent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/SubscriptionEvent.java @@ -48,7 +48,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SubscriptionEvent implements Comparable { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/batch/SubscriptionPipeEventBatches.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/batch/SubscriptionPipeEventBatches.java index 0cb81ca43e699..62c53f6ae285a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/batch/SubscriptionPipeEventBatches.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/batch/SubscriptionPipeEventBatches.java @@ -25,8 +25,8 @@ import org.apache.iotdb.db.subscription.broker.SubscriptionPrefetchingTabletQueue; import org.apache.iotdb.db.subscription.broker.SubscriptionPrefetchingTsFileQueue; import org.apache.iotdb.db.subscription.event.SubscriptionEvent; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/cache/SubscriptionPollResponseCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/cache/SubscriptionPollResponseCache.java index c4b37b88263d3..28fd906790fdc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/cache/SubscriptionPollResponseCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/cache/SubscriptionPollResponseCache.java @@ -24,11 +24,11 @@ import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager; import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryBlock; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.LoadingCache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Weigher; import org.apache.iotdb.rpc.subscription.payload.poll.SubscriptionPollResponse; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import com.github.benmanes.caffeine.cache.Weigher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeEmptyEvent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeEmptyEvent.java index df016b3a39c0a..c7ecb761ab688 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeEmptyEvent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeEmptyEvent.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.subscription.event.pipe; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SubscriptionPipeEmptyEvent implements SubscriptionPipeEvents { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTabletBatchEvents.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTabletBatchEvents.java index f96714921e493..1f70109732a71 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTabletBatchEvents.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTabletBatchEvents.java @@ -27,7 +27,7 @@ import java.util.Objects; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SubscriptionPipeTabletBatchEvents implements SubscriptionPipeEvents { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFileBatchEvents.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFileBatchEvents.java index 651763ab652a5..db080d1984ae9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFileBatchEvents.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFileBatchEvents.java @@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SubscriptionPipeTsFileBatchEvents implements SubscriptionPipeEvents { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFilePlainEvent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFilePlainEvent.java index 42dc20eb7bc09..3c73bd6606168 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFilePlainEvent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/event/pipe/SubscriptionPipeTsFilePlainEvent.java @@ -21,7 +21,7 @@ import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SubscriptionPipeTsFilePlainEvent implements SubscriptionPipeEvents { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/ConsensusSubscriptionPrefetchingQueueMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/ConsensusSubscriptionPrefetchingQueueMetrics.java index 50ea840211843..7df3b01a90cb4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/ConsensusSubscriptionPrefetchingQueueMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/ConsensusSubscriptionPrefetchingQueueMetrics.java @@ -23,13 +23,13 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.subscription.broker.consensus.ConsensusPrefetchingQueue; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/SubscriptionPrefetchingQueueMetrics.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/SubscriptionPrefetchingQueueMetrics.java index 250191fcdf140..da86d8be407da 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/SubscriptionPrefetchingQueueMetrics.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/metric/SubscriptionPrefetchingQueueMetrics.java @@ -23,13 +23,13 @@ import org.apache.iotdb.commons.service.metric.enums.Tag; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.subscription.broker.SubscriptionPrefetchingQueue; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.type.Rate; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/resource/log/SubscriptionLogStatus.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/resource/log/SubscriptionLogStatus.java index d1f0257531c90..91d8ffa30b095 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/resource/log/SubscriptionLogStatus.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/resource/log/SubscriptionLogStatus.java @@ -21,9 +21,9 @@ import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; import org.apache.iotdb.db.subscription.agent.SubscriptionAgent; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/subtask/SubscriptionSinkSubtask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/subtask/SubscriptionSinkSubtask.java index 6c26e0fab1063..928fbb0cc7ce9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/subtask/SubscriptionSinkSubtask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/subtask/SubscriptionSinkSubtask.java @@ -24,11 +24,11 @@ import org.apache.iotdb.db.pipe.agent.task.subtask.sink.PipeSinkSubtask; import org.apache.iotdb.db.subscription.agent.SubscriptionAgent; import org.apache.iotdb.db.subscription.broker.consensus.ConsensusSubscriptionSetupHandler; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.pipe.api.PipeConnector; import org.apache.iotdb.pipe.api.event.Event; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/CommonUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/CommonUtils.java index be2e9d81aac0a..541d0d0bc9df5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/CommonUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/CommonUtils.java @@ -33,6 +33,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.IQueryExecution; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.literal.BinaryLiteral; +import org.apache.iotdb.google.common.base.Throwables; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.service.rpc.thrift.TSAggregationQueryReq; import org.apache.iotdb.service.rpc.thrift.TSFastLastDataQueryForOneDeviceReq; @@ -40,7 +41,6 @@ import org.apache.iotdb.service.rpc.thrift.TSLastDataQueryReq; import org.apache.iotdb.service.rpc.thrift.TSRawDataQueryReq; -import com.google.common.base.Throwables; import io.airlift.airline.Cli; import io.airlift.airline.Help; import io.airlift.airline.ParseArgumentsMissingException; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java index 0ca603570734f..f146a8a884cea 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.db.i18n.DataNodeMiscMessages; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.AbstractAlignedChunkMetadata; import org.apache.tsfile.file.metadata.AbstractAlignedTimeSeriesMetadata; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/FixedPriorityBlockingQueue.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/FixedPriorityBlockingQueue.java index 382650b2b35b6..140eb0c7b66a5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/FixedPriorityBlockingQueue.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/FixedPriorityBlockingQueue.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.utils.datastructure; -import com.google.common.collect.MinMaxPriorityQueue; +import org.apache.iotdb.google.common.collect.MinMaxPriorityQueue; import java.util.ArrayList; import java.util.Comparator; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java index 506f82d9e10e4..46ef0b2d4a47e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/TVList.java @@ -28,8 +28,8 @@ import org.apache.iotdb.db.storageengine.dataregion.wal.buffer.WALEntryValue; import org.apache.iotdb.db.storageengine.rescon.memory.PrimitiveArrayManager; import org.apache.iotdb.db.utils.MathUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.read.TimeValuePair; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/stats/CpuTimer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/stats/CpuTimer.java index 2e4947ee57712..453e144a84a4b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/stats/CpuTimer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/stats/CpuTimer.java @@ -23,8 +23,8 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; /** * this class is copied from diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/DNAuditLoggerUserRoleModificationTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/DNAuditLoggerUserRoleModificationTest.java index 57e8d327017d6..33ccb6fb4487f 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/DNAuditLoggerUserRoleModificationTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/DNAuditLoggerUserRoleModificationTest.java @@ -34,10 +34,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.junit.Test; import org.mockito.ArgumentCaptor; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/PasswordChangeAuditContextTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/PasswordChangeAuditContextTest.java index ac340762e8c7b..dae2f16089b1e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/PasswordChangeAuditContextTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/audit/PasswordChangeAuditContextTest.java @@ -33,10 +33,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.junit.Test; import org.mockito.ArgumentCaptor; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerTest.java index 51a018d48d8f6..8e34ee7f60009 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtaskWorkerTest.java @@ -20,9 +20,9 @@ package org.apache.iotdb.db.pipe.agent.task.subtask.processor; import org.apache.iotdb.commons.pipe.event.EnrichedEvent; +import org.apache.iotdb.google.common.util.concurrent.ListeningExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningScheduledExecutorService; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; import org.junit.Assert; import org.junit.Test; import org.mockito.InOrder; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/ConfigExecutionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/ConfigExecutionTest.java index e2522b2730dea..bfe19338deea9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/ConfigExecutionTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/ConfigExecutionTest.java @@ -30,10 +30,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.IntColumn; @@ -44,7 +44,7 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; -import static com.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/DataDriverTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/DataDriverTest.java index 2eda4063c3552..34e2ba6f35a85 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/DataDriverTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/DataDriverTest.java @@ -49,8 +49,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachineTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachineTest.java index ad7ab0947481e..499ce97e0e319 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachineTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachineTest.java @@ -23,17 +23,17 @@ import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; import org.apache.iotdb.commons.exception.IoTDBException; import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.junit.Assert; import org.junit.Test; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSinkChannelTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSinkChannelTest.java index 944094e0fbf23..1b0f0c2be256d 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSinkChannelTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSinkChannelTest.java @@ -26,14 +26,14 @@ import org.apache.iotdb.db.queryengine.execution.exchange.source.LocalSourceHandle; import org.apache.iotdb.db.queryengine.execution.memory.LocalMemoryManager; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; -import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; public class LocalSinkChannelTest { @Test diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSourceHandleTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSourceHandleTest.java index c4378f82efc9d..a06fee47d127b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSourceHandleTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/LocalSourceHandleTest.java @@ -23,14 +23,14 @@ import org.apache.iotdb.db.queryengine.execution.exchange.source.LocalSourceHandle; import org.apache.iotdb.db.queryengine.execution.memory.LocalMemoryManager; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; -import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; public class LocalSourceHandleTest { @Test diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueueTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueueTest.java index a92eaa5d0e283..fac930eec8913 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueueTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/SharedTsBlockQueueTest.java @@ -23,10 +23,10 @@ import org.apache.iotdb.db.queryengine.execution.memory.LocalMemoryManager; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool.MemoryReservationResult; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.read.common.block.TsBlock; import org.junit.Assert; @@ -37,7 +37,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; public class SharedTsBlockQueueTest { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/ShuffleSinkHandleTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/ShuffleSinkHandleTest.java index 01725611b6eca..ff683c3d1ab25 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/ShuffleSinkHandleTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/ShuffleSinkHandleTest.java @@ -28,9 +28,9 @@ import org.apache.iotdb.db.queryengine.execution.exchange.source.LocalSourceHandle; import org.apache.iotdb.db.queryengine.execution.memory.LocalMemoryManager; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; @@ -42,7 +42,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; public class ShuffleSinkHandleTest { @Test diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/StubSink.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/StubSink.java index 6217e6a8e8bec..0725a6110a619 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/StubSink.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/StubSink.java @@ -20,16 +20,16 @@ import org.apache.iotdb.db.queryengine.execution.exchange.sink.ISink; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.mpp.rpc.thrift.TFragmentInstanceId; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.ArrayList; import java.util.List; -import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; public class StubSink implements ISink { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/Utils.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/Utils.java index b09498ad949dd..24368945c1a4f 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/Utils.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/exchange/Utils.java @@ -21,8 +21,8 @@ import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool.MemoryReservationResult; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.TsBlockSerde; import org.mockito.Mockito; @@ -33,7 +33,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; -import static com.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; public class Utils { public static List createMockTsBlocks(int numOfTsBlocks, long mockTsBlockSize) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceExecutionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceExecutionTest.java index 79e66f92d9e66..b0c1156a4e373 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceExecutionTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceExecutionTest.java @@ -43,8 +43,8 @@ import org.apache.iotdb.db.storageengine.dataregion.memtable.ReadOnlyMemChunk; import org.apache.iotdb.db.utils.datastructure.AlignedTVList; import org.apache.iotdb.db.utils.datastructure.TVList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.enums.CompressionType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceStateMachineTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceStateMachineTest.java index d0121e320c143..25fea6b5cd3f7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceStateMachineTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceStateMachineTest.java @@ -22,8 +22,8 @@ import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; import org.apache.iotdb.db.queryengine.common.FragmentInstanceId; import org.apache.iotdb.db.queryengine.common.PlanFragmentId; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Test; import org.mockito.Mockito; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemoryPoolTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemoryPoolTest.java index 8614d39f20a54..015dda6b520a7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemoryPoolTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemoryPoolTest.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.memory.MemoryManager; import org.apache.iotdb.db.queryengine.execution.memory.MemoryPool.MemoryReservationResult; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Assert; import org.junit.Before; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemorySourceHandleTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemorySourceHandleTest.java index af6bcd2c55443..114375decfe0b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemorySourceHandleTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/memory/MemorySourceHandleTest.java @@ -20,8 +20,8 @@ package org.apache.iotdb.db.queryengine.execution.memory; import org.apache.iotdb.db.queryengine.plan.execution.memory.MemorySourceHandle; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.junit.Assert; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationOperatorTest.java index a32d0883b4ebb..bc2233711b130 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/AggregationOperatorTest.java @@ -45,8 +45,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java index ab476f61ee9ea..d68e0982ce306 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java @@ -28,8 +28,8 @@ import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; import org.apache.iotdb.db.queryengine.common.QueryId; import org.apache.iotdb.db.queryengine.execution.operator.source.relational.CteScanOperator; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/DeviceViewIntoOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/DeviceViewIntoOperatorTest.java index 3262bf97f1356..91566a8818e5a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/DeviceViewIntoOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/DeviceViewIntoOperatorTest.java @@ -70,9 +70,9 @@ import java.util.Set; import java.util.concurrent.ExecutorService; -import static com.google.common.util.concurrent.Futures.immediateFuture; import static org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; import static org.apache.iotdb.db.queryengine.execution.operator.OperatorTestUtils.lastNonNullOrEmpty; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; import static org.apache.iotdb.rpc.RpcUtils.SUCCESS_STATUS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/FillOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/FillOperatorTest.java index a0d3d57a5823f..f7f954712bfde 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/FillOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/FillOperatorTest.java @@ -33,8 +33,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.process.TreeFillOperator; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryOperatorTest.java index 9c6173e400334..17856c12eab74 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryOperatorTest.java @@ -40,8 +40,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryTreeSortOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryTreeSortOperatorTest.java index c8e18ab6a4549..03e13abb88d36 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryTreeSortOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/LastQueryTreeSortOperatorTest.java @@ -40,8 +40,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MergeTreeSortOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MergeTreeSortOperatorTest.java index cfbcf0e26306c..163b4bed6dec5 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MergeTreeSortOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MergeTreeSortOperatorTest.java @@ -57,10 +57,10 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; import org.apache.iotdb.isession.SessionConfig; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MultiColumnMergerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MultiColumnMergerTest.java index 9dec387de9188..12852a1abcd88 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MultiColumnMergerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/MultiColumnMergerTest.java @@ -20,8 +20,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.MultiColumnMerger; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonOverlappedMultiColumnMergerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonOverlappedMultiColumnMergerTest.java index e6a9a8542dfaf..46be7a94d5b5b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonOverlappedMultiColumnMergerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonOverlappedMultiColumnMergerTest.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.parameter.InputLocation; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.AscTimeComparator; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.NonOverlappedMultiColumnMerger; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorMemoryTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorMemoryTest.java index 350860e57ae4b..f3e983dae6b06 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorMemoryTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorMemoryTest.java @@ -88,8 +88,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.SeriesScanOptions; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesAggregationScanOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesAggregationScanOperatorTest.java index 43efba320e9a8..d3719da60970c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesAggregationScanOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesAggregationScanOperatorTest.java @@ -40,8 +40,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesScanOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesScanOperatorTest.java index 23dda52f2bdd7..f3fa642dad42b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesScanOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SeriesScanOperatorTest.java @@ -36,8 +36,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import io.airlift.units.Duration; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SortOperatorSortBranchTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SortOperatorSortBranchTest.java index fbe0acc894860..9e40b054490d6 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SortOperatorSortBranchTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/SortOperatorSortBranchTest.java @@ -39,8 +39,8 @@ import org.apache.iotdb.db.queryengine.plan.statement.component.OrderByKey; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; import org.apache.iotdb.db.queryengine.plan.statement.component.SortItem; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TransformOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TransformOperatorTest.java index d6684a4653d4f..313fd542f63aa 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TransformOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TransformOperatorTest.java @@ -39,9 +39,9 @@ import org.apache.iotdb.db.queryengine.transformation.api.LayerReader; import org.apache.iotdb.db.queryengine.transformation.dag.input.QueryDataSetInputLayer; import org.apache.iotdb.db.queryengine.transformation.dag.input.TsBlockInputDataSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeIntoOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeIntoOperatorTest.java index 81cc4c6654acf..9727092785ee3 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeIntoOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeIntoOperatorTest.java @@ -67,9 +67,9 @@ import java.util.Set; import java.util.concurrent.ExecutorService; -import static com.google.common.util.concurrent.Futures.immediateFuture; import static org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; import static org.apache.iotdb.db.queryengine.execution.operator.OperatorTestUtils.lastNonNullOrEmpty; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; import static org.apache.iotdb.rpc.RpcUtils.SUCCESS_STATUS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeLinearFillOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeLinearFillOperatorTest.java index 7d1962fb3f46c..0f13df50cf2eb 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeLinearFillOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeLinearFillOperatorTest.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.process.TreeLinearFillOperator; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeTopKOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeTopKOperatorTest.java index e9960994ab59d..c7c8826829433 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeTopKOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/TreeTopKOperatorTest.java @@ -50,8 +50,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.common.conf.TSFileDescriptor; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/UpdateLastCacheOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/UpdateLastCacheOperatorTest.java index 2454ffb4fa87d..38e4e161a0515 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/UpdateLastCacheOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/UpdateLastCacheOperatorTest.java @@ -40,8 +40,8 @@ import org.apache.iotdb.db.storageengine.dataregion.read.QueryDataSource; import org.apache.iotdb.db.storageengine.dataregion.read.reader.series.SeriesReaderTestUtil; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.exception.write.WriteProcessException; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/TableStreamSortOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/TableStreamSortOperatorTest.java index 1ad81e8a2ff9a..9560f6bcb31d6 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/TableStreamSortOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/TableStreamSortOperatorTest.java @@ -36,8 +36,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/ValuesOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/ValuesOperatorTest.java index a7aa7e29bfc08..7ca34588b729c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/ValuesOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/ValuesOperatorTest.java @@ -29,8 +29,8 @@ import org.apache.iotdb.db.queryengine.execution.driver.DriverContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/fill/NextFillTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/fill/NextFillTest.java index 497fe6efe4d2c..958c3d8dec163 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/fill/NextFillTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/fill/NextFillTest.java @@ -26,8 +26,8 @@ import org.apache.iotdb.calc.plan.planner.CommonOperatorUtils; import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWMoOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWMoOperatorTest.java index 18880c56ff3a4..341fe92bca30e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWMoOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWMoOperatorTest.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWoMoOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWoMoOperatorTest.java index a277443e48f0b..ea51db43b59fb 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWoMoOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWGroupWoMoOperatorTest.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWMoOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWMoOperatorTest.java index 0947158ae200b..db1a438f10efe 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWMoOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWMoOperatorTest.java @@ -33,8 +33,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWoMoOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWoMoOperatorTest.java index ebd32ff2e193c..05f2bda60d7e3 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWoMoOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/gapfill/GapFillWoGroupWoMoOperatorTest.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.TsBlockBuilder; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/InnerTimeJoinOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/InnerTimeJoinOperatorTest.java index b0630508eed31..120f46d3a32f3 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/InnerTimeJoinOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/InnerTimeJoinOperatorTest.java @@ -24,8 +24,8 @@ import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.AscTimeComparator; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.DescTimeComparator; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/LeftOuterTimeJoinOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/LeftOuterTimeJoinOperatorTest.java index 6964ca70b67f0..ebf62adb59ddd 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/LeftOuterTimeJoinOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/join/LeftOuterTimeJoinOperatorTest.java @@ -23,8 +23,8 @@ import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.AscTimeComparator; import org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.DescTimeComparator; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/rowpattern/MatcherTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/rowpattern/MatcherTest.java index 40037463ac44c..b66bf6180a2f6 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/rowpattern/MatcherTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/rowpattern/MatcherTest.java @@ -29,9 +29,9 @@ import org.apache.iotdb.calc.execution.operator.process.window.partition.Partition; import org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrLabel; import org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrRowPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.TsBlock; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/RowNumberOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/RowNumberOperatorTest.java index 65398e9782ce0..6a96cb7d205d1 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/RowNumberOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/RowNumberOperatorTest.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.process.TreeLinearFillOperator; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java index 221e351574382..198a0eba64643 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java @@ -35,8 +35,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.process.TreeLinearFillOperator; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaOperatorTestUtil.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaOperatorTestUtil.java index e608b65563c15..c3b66cc7768dd 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaOperatorTestUtil.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaOperatorTestUtil.java @@ -23,8 +23,8 @@ import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ISchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import org.mockito.Mockito; import java.util.Iterator; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskTimeoutSentinelThreadTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskTimeoutSentinelThreadTest.java index 328e08a853ee9..db3d994402cdf 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskTimeoutSentinelThreadTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskTimeoutSentinelThreadTest.java @@ -28,9 +28,9 @@ import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTask; import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTaskId; import org.apache.iotdb.db.queryengine.execution.schedule.task.DriverTaskStatus; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java index b29c11c2a91af..9bd098655cab2 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java @@ -24,9 +24,9 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.junit.Test; import org.mockito.Mockito; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java index 0822b51f1cbeb..e5c790a7a6c5c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java @@ -22,9 +22,9 @@ import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; import org.apache.iotdb.rpc.TSStatusCode; -import com.google.common.util.concurrent.SettableFuture; import org.apache.tsfile.read.common.block.TsBlock; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/FragmentInstanceSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/FragmentInstanceSerdeTest.java index b387baddff6b2..a159c9c618d13 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/FragmentInstanceSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/FragmentInstanceSerdeTest.java @@ -44,8 +44,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.join.FullOuterTimeJoinNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.source.SeriesScanNode; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.junit.Test; import java.nio.ByteBuffer; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/AggregationAlignByDeviceTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/AggregationAlignByDeviceTest.java index be8fe8599a4e3..c458096a592ef 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/AggregationAlignByDeviceTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/AggregationAlignByDeviceTest.java @@ -40,8 +40,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.sink.ShuffleSinkNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.source.SeriesAggregationScanNode; import org.apache.iotdb.db.queryengine.plan.statement.component.SortItem; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.junit.Test; import java.util.List; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/PlanGraphJsonPrinterTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/PlanGraphJsonPrinterTest.java index 8c901d2124311..0389a52a415d0 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/PlanGraphJsonPrinterTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/PlanGraphJsonPrinterTest.java @@ -56,10 +56,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeDeviceViewScanNode; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExplainOutputFormat; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/AggregationNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/AggregationNodeSerdeTest.java index 575b6475b4e10..71713638e54a1 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/AggregationNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/AggregationNodeSerdeTest.java @@ -32,8 +32,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.AggregationStep; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByTimeParameter; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.Sets; -import com.google.common.collect.Sets; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.utils.TimeDuration; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/IntoNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/IntoNodeSerdeTest.java index c75e8cdd72dc8..0f8b22d103cd1 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/IntoNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/IntoNodeSerdeTest.java @@ -30,8 +30,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.IntoNode; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.DeviceViewIntoPathDescriptor; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.IntoPathDescriptor; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.FloatType; import org.apache.tsfile.read.common.type.StringType; import org.apache.tsfile.read.common.type.TimestampType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/SortNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/SortNodeSerdeTest.java index 447b86bf652b5..9ebf040d74e0b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/SortNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/SortNodeSerdeTest.java @@ -28,8 +28,8 @@ import org.apache.iotdb.db.queryengine.plan.statement.component.OrderByKey; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; import org.apache.iotdb.db.queryengine.plan.statement.component.SortItem; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/WindowNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/WindowNodeSerdeTest.java index b8d1abe622db7..d49a6cd38272b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/WindowNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/process/WindowNodeSerdeTest.java @@ -38,8 +38,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.source.SeriesScanNode; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.DoubleType; import org.apache.tsfile.read.common.type.LongType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationCornerCaseTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationCornerCaseTest.java index d40c73dbf6a2f..80268db38738b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationCornerCaseTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationCornerCaseTest.java @@ -35,9 +35,9 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationTest.java index 9998ca76f4e14..51870a7032910 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationTest.java @@ -27,10 +27,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import java.util.Optional; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java index 5b5a384f756af..d28791eed3fac 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java @@ -84,8 +84,8 @@ import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.IDeviceID.Factory; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofInnerJoinOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofInnerJoinOperatorTest.java index 7714bfc74283f..65d8d1a714923 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofInnerJoinOperatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofInnerJoinOperatorTest.java @@ -27,8 +27,8 @@ import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.plan.planner.memory.ThreadSafeMemoryReservationManager; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofJoinTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofJoinTest.java index 40ba055604322..95e8e95922513 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofJoinTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AsofJoinTest.java @@ -26,10 +26,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SortItem.NullOrdering.LAST; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/DistinctTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/DistinctTest.java index e1fa72c3dfa9f..7433d0a151023 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/DistinctTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/DistinctTest.java @@ -22,10 +22,10 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import java.util.Optional; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExtractExpressionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExtractExpressionTest.java index cda0113527b76..0eea218f470fa 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExtractExpressionTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExtractExpressionTest.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.junit.BeforeClass; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/JoinTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/JoinTest.java index 411ef3f6829b5..95d3077087a03 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/JoinTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/JoinTest.java @@ -55,11 +55,11 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTSBSDataPartition.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTSBSDataPartition.java index 2bf634a12b847..ec3255207d852 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTSBSDataPartition.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTSBSDataPartition.java @@ -30,8 +30,7 @@ import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.partition.executor.SeriesPartitionExecutor; import org.apache.iotdb.db.conf.IoTDBDescriptor; - -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.Arrays; import java.util.Collections; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTableModelDataPartition.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTableModelDataPartition.java index a3ced149f843f..866f5abebb7c4 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTableModelDataPartition.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/MockTableModelDataPartition.java @@ -30,8 +30,8 @@ import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.partition.executor.SeriesPartitionExecutor; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.utils.Binary; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PushAggregationThroughUnionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PushAggregationThroughUnionTest.java index aa48336e68320..30819664e4e78 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PushAggregationThroughUnionTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/PushAggregationThroughUnionTest.java @@ -29,9 +29,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.TableLogicalPlanner; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; import org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributedPlanner; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.junit.Before; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/SortTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/SortTest.java index f7b393b724a27..466dd944f22a7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/SortTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/SortTest.java @@ -42,9 +42,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.junit.BeforeClass; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java index 4794b19994e88..a150d43f4db70 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSMetadata.java @@ -47,10 +47,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.OperatorNotFoundException; import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableHeaderSchemaValidator; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.mpp.rpc.thrift.TRegionRouteReq; import org.apache.iotdb.udf.api.relational.TableFunction; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.StringArrayDeviceID; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSTest.java index f3b76a70ba401..63d52ffe42163 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TSBSTest.java @@ -29,10 +29,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Ignore; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TableFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TableFunctionTest.java index 9d09ff057b30a..0f3a08d004bf2 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TableFunctionTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TableFunctionTest.java @@ -26,12 +26,12 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.TableFunctionProcessorMatcher; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.udf.api.relational.EmptyTableFunctionHandle; import org.apache.iotdb.udf.api.relational.table.MapTableFunctionHandle; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import java.util.Collections; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java index ac266f19398ca..fc4346e410e27 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestMetadata.java @@ -61,11 +61,11 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableHeaderSchemaValidator; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; import org.apache.iotdb.db.schemaengine.table.InformationSchemaUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.mpp.rpc.thrift.TRegionRouteReq; import org.apache.iotdb.udf.api.relational.TableFunction; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.StringArrayDeviceID; import org.apache.tsfile.read.common.type.StringType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestUtils.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestUtils.java index 9ad78fbea89fa..a24fa793b60db 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestUtils.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TestUtils.java @@ -36,8 +36,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.junit.Assert; import java.time.ZoneId; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TreeViewTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TreeViewTest.java index 959bd4b9d6abb..347e61149c780 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TreeViewTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/TreeViewTest.java @@ -25,10 +25,10 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/AggregationNodeRateSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/AggregationNodeRateSerdeTest.java index 1c5861e91dd1e..843202ba3869a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/AggregationNodeRateSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/AggregationNodeRateSerdeTest.java @@ -30,9 +30,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.planner.node.PlanNodeDeserializeHelper; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.TypeFactory; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CorrelatedSubqueryTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CorrelatedSubqueryTest.java index ddcce852d1c22..e3dd10d56bd60 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CorrelatedSubqueryTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CorrelatedSubqueryTest.java @@ -27,10 +27,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import java.util.Collections; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializerTest.java index 21225e38e590f..701c3e0f29e2e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteMaterializerTest.java @@ -36,10 +36,10 @@ import org.apache.iotdb.db.queryengine.plan.execution.QueryExecution; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.LongColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java index ef1b289a892b8..cf771ae2e5da8 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java @@ -35,9 +35,9 @@ import org.apache.iotdb.db.queryengine.plan.execution.QueryExecution; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.read.common.block.column.LongColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExampleTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExampleTest.java index d4013efb9302c..f5f19ef49aeed 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExampleTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/ExampleTest.java @@ -29,10 +29,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ArithmeticBinaryExpression.Operator.ADD; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/NextFillNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/NextFillNodeSerdeTest.java index 96d57ca6b9bd5..795a8ddd1f398 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/NextFillNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/NextFillNodeSerdeTest.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.NextFillNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.PreviousFillNode; import org.apache.iotdb.db.queryengine.plan.planner.node.PlanNodeDeserializeHelper; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.TimeDuration; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PatternRecognitionNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PatternRecognitionNodeSerdeTest.java index 4651cb2720b38..11a75c572be9c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PatternRecognitionNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PatternRecognitionNodeSerdeTest.java @@ -42,9 +42,9 @@ import org.apache.iotdb.db.queryengine.plan.planner.node.PlanNodeDeserializeHelper; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.source.SeriesScanNode; import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.DoubleType; import org.apache.tsfile.read.common.type.Type; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java index 573c06453422f..fc0c27d06ef7a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java @@ -48,8 +48,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.security.AllowAllAccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; import org.apache.iotdb.db.queryengine.plan.relational.sql.rewrite.StatementRewriteFactory; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.mockito.Mockito; import java.time.ZoneId; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/UncorrelatedSubqueryTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/UncorrelatedSubqueryTest.java index dd7883f26adbd..46964c60f397d 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/UncorrelatedSubqueryTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/UncorrelatedSubqueryTest.java @@ -28,10 +28,10 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.PredicateWithUncorrelatedScalarSubqueryReconstructor; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionOptimizationTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionOptimizationTest.java index 19bae09093052..2b92bc284acd8 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionOptimizationTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionOptimizationTest.java @@ -21,9 +21,9 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionTest.java index 4c10a9961dc89..3efe5b7b79b20 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/WindowFunctionTest.java @@ -21,9 +21,9 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.junit.Test; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationDeviceTableScanMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationDeviceTableScanMatcher.java index 28cf3727c5e1d..d3adb78d018db 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationDeviceTableScanMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationDeviceTableScanMatcher.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.List; @@ -34,12 +33,12 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class AggregationDeviceTableScanMatcher extends DeviceTableScanMatcher { private final PlanMatchPattern.GroupingSetDescriptor groupingSets; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionMatcher.java index 8f84da3f7f122..a04ada6f32008 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionMatcher.java @@ -29,8 +29,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class AggregationFunctionMatcher implements RvalueMatcher { private final ExpectedValueProvider callMaker; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionProvider.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionProvider.java index 38ad646f26326..078c19e8cb0f1 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionProvider.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationFunctionProvider.java @@ -23,10 +23,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.List; import java.util.Optional; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationMatcher.java index 54c7cdc70041c..1445cb493b163 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationMatcher.java @@ -30,12 +30,12 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class AggregationMatcher implements Matcher { private final PlanMatchPattern.GroupingSetDescriptor groupingSets; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationStepMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationStepMatcher.java index c2088792942cb..a4559ee612f79 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationStepMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AggregationStepMatcher.java @@ -24,10 +24,10 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class AggregationStepMatcher implements Matcher { private final AggregationNode.Step step; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AssignUniqueIdMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AssignUniqueIdMatcher.java index df7c743c338cc..e432675c3f386 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AssignUniqueIdMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/AssignUniqueIdMatcher.java @@ -27,7 +27,7 @@ import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AssignUniqueIdMatcher implements RvalueMatcher { @Override diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/BaseStrictSymbolsMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/BaseStrictSymbolsMatcher.java index 7617382df17de..4eab91999508a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/BaseStrictSymbolsMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/BaseStrictSymbolsMatcher.java @@ -27,8 +27,8 @@ import java.util.Set; import java.util.function.Function; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public abstract class BaseStrictSymbolsMatcher implements Matcher { private final Function> getActual; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java index 60aade69f5eca..a384b6ee7f7c4 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ColumnReference.java @@ -31,9 +31,9 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class ColumnReference implements RvalueMatcher { private final String tableName; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/CteScanMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/CteScanMatcher.java index 1c63a84ce70bb..6672b76ed72f0 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/CteScanMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/CteScanMatcher.java @@ -30,9 +30,9 @@ import java.util.List; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class CteScanMatcher implements Matcher { protected final String expectedCteName; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/DeviceTableScanMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/DeviceTableScanMatcher.java index afa8884d55fdf..71ca892de140f 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/DeviceTableScanMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/DeviceTableScanMatcher.java @@ -27,7 +27,7 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class DeviceTableScanMatcher extends TableScanMatcher { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExchangeNodeMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExchangeNodeMatcher.java index 127eaf6b199d7..15531f946d002 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExchangeNodeMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExchangeNodeMatcher.java @@ -26,7 +26,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class ExchangeNodeMatcher implements Matcher { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionMatcher.java index aa60d377c1c71..f6e2ba353b8e0 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionMatcher.java @@ -25,15 +25,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class ExpressionMatcher implements RvalueMatcher { private final String sql; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionVerifier.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionVerifier.java index 084f64db4b2d7..2e205faae200f 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionVerifier.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/ExpressionVerifier.java @@ -54,9 +54,9 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; /** * Expression visitor which verifies if given expression (actual) is matching other expression given diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/FilterMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/FilterMatcher.java index 5f8b050009161..4d9f700d4f0e7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/FilterMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/FilterMatcher.java @@ -25,9 +25,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; final class FilterMatcher implements Matcher { private final Expression predicate; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/GroupMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/GroupMatcher.java index 2a5a93fd0b8a1..2e32fc736a101 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/GroupMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/GroupMatcher.java @@ -26,8 +26,8 @@ import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class GroupMatcher extends SortMatcher { private final int partitionKeyCount; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/IdentityProjectionMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/IdentityProjectionMatcher.java index 19f7513afb8bd..d7a55a20572a9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/IdentityProjectionMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/IdentityProjectionMatcher.java @@ -24,9 +24,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class IdentityProjectionMatcher implements Matcher { @Override diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/InformationSchemaTableScanMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/InformationSchemaTableScanMatcher.java index b844eba482618..93c8e31f8fde9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/InformationSchemaTableScanMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/InformationSchemaTableScanMatcher.java @@ -30,9 +30,9 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class InformationSchemaTableScanMatcher extends TableScanMatcher { private final Optional dataNodeId; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/JoinMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/JoinMatcher.java index 82b5af04d4ce0..55bc6970a5b8a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/JoinMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/JoinMatcher.java @@ -25,23 +25,23 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; import java.util.Optional; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.asofJoinClause; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.equiJoinClause; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.node; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public final class JoinMatcher implements Matcher { private final JoinNode.JoinType joinType; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/LimitMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/LimitMatcher.java index b634485e79911..5eba468ec0c89 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/LimitMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/LimitMatcher.java @@ -25,17 +25,16 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.LimitNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.Ordering; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.Util.orderingSchemeMatches; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class LimitMatcher implements Matcher { private final long limit; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/MarkDistinctMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/MarkDistinctMatcher.java index a37204b8d156d..9f7790e03ad22 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/MarkDistinctMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/MarkDistinctMatcher.java @@ -23,19 +23,18 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.MarkDistinctNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class MarkDistinctMatcher implements Matcher { private final PlanTestSymbol markerSymbol; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/NotPlanNodeMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/NotPlanNodeMatcher.java index f0f66cd38c45a..bce3381831e46 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/NotPlanNodeMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/NotPlanNodeMatcher.java @@ -23,10 +23,10 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; final class NotPlanNodeMatcher implements Matcher { private final Class excludedNodeClass; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OffsetMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OffsetMatcher.java index 8db7e0744ac12..834051d81bf18 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OffsetMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OffsetMatcher.java @@ -24,8 +24,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.OffsetNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class OffsetMatcher implements Matcher { private final long rowCount; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OutputMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OutputMatcher.java index 30babd3c4ba1c..9b9f1420136f0 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OutputMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/OutputMatcher.java @@ -24,15 +24,14 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class OutputMatcher implements Matcher { private final List aliases; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java index 6d5102d408a19..4277d556c7938 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java @@ -65,9 +65,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeDeviceViewScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TreeNonAlignedDeviceViewScanNode; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.time.ZoneId; import java.util.ArrayList; @@ -80,10 +79,6 @@ import java.util.function.Consumer; import java.util.function.Predicate; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.Collections.nCopies; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder.ASC_NULLS_FIRST; @@ -98,6 +93,10 @@ import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.StrictAssignedSymbolsMatcher.actualAssignments; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.StrictSymbolsMatcher.actualOutputs; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; public final class PlanMatchPattern { private final List matchers = new ArrayList<>(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingState.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingState.java index e065688f698e9..864bfc19b3e88 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingState.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingState.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.queryengine.plan.relational.planner.assertions; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingVisitor.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingVisitor.java index 7d78f4fdc7f49..a3c8fee677357 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingVisitor.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchingVisitor.java @@ -30,11 +30,11 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.Assignments.identity; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; final class PlanMatchingVisitor implements PlanVisitor { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanNodeMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanNodeMatcher.java index 98a4269d16394..c9e272d96d5f7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanNodeMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanNodeMatcher.java @@ -23,10 +23,10 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; final class PlanNodeMatcher implements Matcher { private final Class nodeClass; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SemiJoinMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SemiJoinMatcher.java index 67916ee1c5bee..f93e1089b7b53 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SemiJoinMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SemiJoinMatcher.java @@ -24,11 +24,11 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SemiJoinNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; final class SemiJoinMatcher implements Matcher { private final String sourceSymbolAlias; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SortMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SortMatcher.java index c4a55172f402a..22618f1c9cd4b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SortMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SortMatcher.java @@ -27,11 +27,11 @@ import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.Util.orderingSchemeMatches; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; class SortMatcher implements Matcher { protected final List orderBy; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SpecificationProvider.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SpecificationProvider.java index 35961e93bc56d..86e3022c9acb7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SpecificationProvider.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SpecificationProvider.java @@ -22,18 +22,17 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.DataOrganizationSpecification; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; import org.apache.iotdb.commons.queryengine.plan.relational.planner.SortOrder; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.List; import java.util.Map; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap; public class SpecificationProvider implements ExpectedValueProvider { private final List partitionBy; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictAssignedSymbolsMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictAssignedSymbolsMatcher.java index 6ccdf9b9b585e..9acb270688d10 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictAssignedSymbolsMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictAssignedSymbolsMatcher.java @@ -24,16 +24,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.Optional; import java.util.Set; import java.util.function.Function; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class StrictAssignedSymbolsMatcher extends BaseStrictSymbolsMatcher { private final Collection getExpected; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictSymbolsMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictSymbolsMatcher.java index 1000316be76e8..73b2b9079dfa5 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictSymbolsMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/StrictSymbolsMatcher.java @@ -23,16 +23,15 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.List; import java.util.Set; import java.util.function.Function; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class StrictSymbolsMatcher extends BaseStrictSymbolsMatcher { private final List expectedAliases; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SymbolAliases.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SymbolAliases.java index 3f7edff7f74af..f6018066507ac 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SymbolAliases.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/SymbolAliases.java @@ -25,17 +25,16 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter; import org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter; - -import com.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public final class SymbolAliases { private final Map map; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableFunctionProcessorMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableFunctionProcessorMatcher.java index 84f57489a3a87..8756df22b6d3c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableFunctionProcessorMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableFunctionProcessorMatcher.java @@ -25,19 +25,18 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.TableFunctionProcessorNode; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; import org.apache.iotdb.udf.api.relational.table.TableFunctionHandle; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.match; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class TableFunctionProcessorMatcher implements Matcher { private final String name; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableScanMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableScanMatcher.java index b9629b443e532..9d2f5954aa1eb 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableScanMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TableScanMatcher.java @@ -31,9 +31,9 @@ import java.util.Set; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public abstract class TableScanMatcher implements Matcher { protected final String expectedTableName; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TopKMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TopKMatcher.java index a6a645961bcec..69d862d9ff4d8 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TopKMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/TopKMatcher.java @@ -26,10 +26,10 @@ import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.Util.orderingSchemeMatches; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class TopKMatcher implements Matcher { private final List orderBy; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/WindowFunctionMatcher.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/WindowFunctionMatcher.java index 406ae9ae8771d..28a550ee5699f 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/WindowFunctionMatcher.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/WindowFunctionMatcher.java @@ -30,9 +30,9 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.MatchResult.NO_MATCH; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.Util.orderingSchemeMatches; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class WindowFunctionMatcher implements Matcher { private final PlanMatchPattern.Specification specification; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGeneratorRateTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGeneratorRateTest.java index ddd5352ee1916..2ead5430289e0 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGeneratorRateTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGeneratorRateTest.java @@ -29,9 +29,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.AggregationNode; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.common.type.TypeFactory; import org.junit.Test; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java index 91673ac899d46..d55f9e30b4d86 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/CurrentQueriesTest.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.junit.Test; import java.util.Optional; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/ShowQueriesTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/ShowQueriesTest.java index 6226dd98627d5..dc989bf94b64b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/ShowQueriesTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/informationschema/ShowQueriesTest.java @@ -20,8 +20,8 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.junit.Test; import java.util.Locale; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/ParallelizeGroupingTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/ParallelizeGroupingTest.java index e2326688112b2..6a7e31416ea6e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/ParallelizeGroupingTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/ParallelizeGroupingTest.java @@ -34,9 +34,9 @@ import org.apache.iotdb.db.queryengine.plan.relational.function.tvf.read_tsfile.ExternalTsFileQueryResource; import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExternalTsFileScanNode; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.junit.Test; import java.nio.file.Path; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/FromBase32ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/FromBase32ColumnTransformerTest.java index 068654977deca..860f077cd200e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/FromBase32ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/FromBase32ColumnTransformerTest.java @@ -25,8 +25,8 @@ import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; import org.apache.iotdb.commons.exception.SemanticException; +import org.apache.iotdb.google.common.io.BaseEncoding; -import com.google.common.io.BaseEncoding; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/ToBase32ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/ToBase32ColumnTransformerTest.java index d000b22571119..44cd8196a59dd 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/ToBase32ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/base64Encoding/ToBase32ColumnTransformerTest.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.io.BaseEncoding; -import com.google.common.io.BaseEncoding; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Md5ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Md5ColumnTransformerTest.java index 7135f334a182e..8e14ccff66c9d 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Md5ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Md5ColumnTransformerTest.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.hash.Hashing; -import com.google.common.hash.Hashing; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Murmur3ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Murmur3ColumnTransformerTest.java index 2ce9dba7d8805..0c62520400de1 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Murmur3ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Murmur3ColumnTransformerTest.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.hash.Hashing; -import com.google.common.hash.Hashing; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha1ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha1ColumnTransformerTest.java index ef9ae8e222cf4..5225c6e28560b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha1ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha1ColumnTransformerTest.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.hash.Hashing; -import com.google.common.hash.Hashing; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha256ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha256ColumnTransformerTest.java index 902693f87afc1..df8da27566ddc 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha256ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha256ColumnTransformerTest.java @@ -24,8 +24,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.hash.Hashing; -import com.google.common.hash.Hashing; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha512ColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha512ColumnTransformerTest.java index 488b67d02d52d..950f679fcbf34 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha512ColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hashing/Sha512ColumnTransformerTest.java @@ -22,8 +22,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.hash.Hashing; -import com.google.common.hash.Hashing; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hexEncoding/ToHexColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hexEncoding/ToHexColumnTransformerTest.java index f846ddce17c57..f5946bfe5203c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hexEncoding/ToHexColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/unary/scalar/hexEncoding/ToHexColumnTransformerTest.java @@ -22,8 +22,8 @@ import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.GenericCodecColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.unary.scalar.factory.CodecStrategiesFactory; +import org.apache.iotdb.google.common.io.BaseEncoding; -import com.google.common.io.BaseEncoding; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.read.common.block.column.BinaryColumn; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java index 7507d604838a1..9f44a1df16dba 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java @@ -21,8 +21,7 @@ import org.apache.iotdb.commons.exception.MetadataLeaseFencedException; import org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy; - -import com.google.common.util.concurrent.MoreExecutors; +import org.apache.iotdb.google.common.util.concurrent.MoreExecutors; import java.util.Collections; import java.util.concurrent.atomic.AtomicLong; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java index b0b9ce2880ab8..d6c2bb984b542 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java @@ -25,8 +25,8 @@ import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException; import org.apache.iotdb.db.storageengine.dataregion.DataRegion; import org.apache.iotdb.db.storageengine.dataregion.wal.recover.WALRecoverManager; +import org.apache.iotdb.google.common.collect.Lists; -import com.google.common.collect.Lists; import org.junit.After; import org.junit.Assert; import org.junit.Before; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/CompactionTaskComparatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/CompactionTaskComparatorTest.java index 4878948ee91fc..f3dafa2eec3be 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/CompactionTaskComparatorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/CompactionTaskComparatorTest.java @@ -37,8 +37,8 @@ import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; import org.apache.iotdb.db.utils.datastructure.FixedPriorityBlockingQueue; +import org.apache.iotdb.google.common.collect.MinMaxPriorityQueue; -import com.google.common.collect.MinMaxPriorityQueue; import org.junit.After; import org.junit.Assert; import org.junit.Before; diff --git a/iotdb-core/node-commons/pom.xml b/iotdb-core/node-commons/pom.xml index 2baf82bf21cb0..2b9b75d4f6abe 100644 --- a/iotdb-core/node-commons/pom.xml +++ b/iotdb-core/node-commons/pom.xml @@ -135,10 +135,6 @@ commons-cli commons-cli - - com.google.guava - guava - com.google.code.findbugs jsr305 @@ -156,10 +152,6 @@ org.antlr antlr4-runtime - - com.github.ben-manes.caffeine - caffeine - com.github.luben zstd-jni diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java index b13d1ab06373d..b5efb9cbfe88b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.client.exception.ClientManagerException; import org.apache.iotdb.commons.i18n.ClientMessages; import org.apache.iotdb.commons.utils.function.CheckedTriConsumer; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.thrift.TException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/threadpool/ScheduledExecutorUtil.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/threadpool/ScheduledExecutorUtil.java index 4f1d4a5970542..405cb360f5157 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/threadpool/ScheduledExecutorUtil.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/concurrent/threadpool/ScheduledExecutorUtil.java @@ -19,8 +19,8 @@ package org.apache.iotdb.commons.concurrent.threadpool; import org.apache.iotdb.commons.i18n.CommonMessages; +import org.apache.iotdb.google.common.base.Throwables; -import com.google.common.base.Throwables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java index 4adacb0d7aa1c..67c1b6aa60540 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java @@ -29,9 +29,9 @@ import org.apache.iotdb.commons.pipe.config.PipeConfig; import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.commons.utils.KillPoint.KillPoint; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; import org.apache.iotdb.rpc.RpcUtils; -import com.google.common.util.concurrent.RateLimiter; import org.apache.tsfile.fileSystem.FSType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/ProgressIndex.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/ProgressIndex.java index 83fdac10b5c8e..e1a9fcbbccc29 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/ProgressIndex.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/ProgressIndex.java @@ -22,8 +22,8 @@ import org.apache.iotdb.commons.consensus.index.impl.HybridProgressIndex; import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex; import org.apache.iotdb.commons.consensus.index.impl.StateProgressIndex; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.Accountable; import org.apache.tsfile.utils.RamUsageEstimator; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/HybridProgressIndex.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/HybridProgressIndex.java index 724a150091158..dd315c565ec54 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/HybridProgressIndex.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/HybridProgressIndex.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.consensus.index.ProgressIndex; import org.apache.iotdb.commons.consensus.index.ProgressIndexType; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/RecoverProgressIndex.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/RecoverProgressIndex.java index f42ad50f63a6a..e2706c75aae19 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/RecoverProgressIndex.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/RecoverProgressIndex.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.consensus.index.ProgressIndex; import org.apache.iotdb.commons.consensus.index.ProgressIndexType; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/StateProgressIndex.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/StateProgressIndex.java index ed2b4d9114525..7715fa905664b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/StateProgressIndex.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/StateProgressIndex.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.consensus.index.ProgressIndex; import org.apache.iotdb.commons.consensus.index.ProgressIndexType; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimePartitionProgressIndex.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimePartitionProgressIndex.java index 05ce7c86718bf..2639e5e7c2d2f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimePartitionProgressIndex.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimePartitionProgressIndex.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.consensus.index.ProgressIndex; import org.apache.iotdb.commons.consensus.index.ProgressIndexType; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimeWindowStateProgressIndex.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimeWindowStateProgressIndex.java index 56293fd5fc0d6..1eac76363a31d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimeWindowStateProgressIndex.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/consensus/index/impl/TimeWindowStateProgressIndex.java @@ -22,8 +22,8 @@ import org.apache.iotdb.commons.consensus.index.ProgressIndex; import org.apache.iotdb.commons.consensus.index.ProgressIndexType; import org.apache.iotdb.commons.i18n.CommonMessages; +import org.apache.iotdb.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMap; import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java index be31e57e839c0..7ffda04121d7b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.i18n.ServiceMessages; import org.apache.iotdb.externalservice.api.IExternalService; +import org.apache.iotdb.google.common.base.Objects; -import com.google.common.base.Objects; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/log/LoggerPeriodicalLogReducer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/log/LoggerPeriodicalLogReducer.java index 4f863e2cc40cb..af6d922370209 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/log/LoggerPeriodicalLogReducer.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/log/LoggerPeriodicalLogReducer.java @@ -21,9 +21,9 @@ import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.i18n.CommonMessages; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.tsfile.utils.RamUsageEstimator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/fa/FAFactory.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/fa/FAFactory.java index d4da9d658106e..eb8615348ad62 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/fa/FAFactory.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/fa/FAFactory.java @@ -21,9 +21,8 @@ import org.apache.iotdb.commons.path.fa.dfa.PatternDFA; import org.apache.iotdb.commons.path.fa.nfa.SimpleNFA; import org.apache.iotdb.commons.schema.SchemaConstant; - -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; +import org.apache.iotdb.github.benmanes.caffeine.cache.LoadingCache; public class FAFactory { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/execution/PipeSubtaskExecutor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/execution/PipeSubtaskExecutor.java index 7fe169f7a38fe..a225880c57335 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/execution/PipeSubtaskExecutor.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/execution/PipeSubtaskExecutor.java @@ -25,10 +25,10 @@ import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.pipe.agent.task.subtask.PipeSubtask; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.google.common.util.concurrent.ListeningExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningScheduledExecutorService; +import org.apache.iotdb.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java index 7a07345c4ae2e..2a38d56526af6 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java @@ -29,15 +29,15 @@ import org.apache.iotdb.commons.pipe.resource.PipeStopStrategy; import org.apache.iotdb.commons.pipe.resource.log.PipeLogger; import org.apache.iotdb.commons.utils.ErrorHandlingCommonUtils; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListeningExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningScheduledExecutorService; import org.apache.iotdb.pipe.api.PipeConnector; import org.apache.iotdb.pipe.api.event.Event; import org.apache.iotdb.pipe.api.exception.PipeConnectionException; import org.apache.iotdb.pipe.api.exception.PipeException; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeSubtask.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeSubtask.java index 108f202e00293..a608c034caf56 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeSubtask.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeSubtask.java @@ -22,11 +22,11 @@ import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.pipe.agent.task.execution.PipeSubtaskScheduler; import org.apache.iotdb.commons.pipe.event.EnrichedEvent; +import org.apache.iotdb.google.common.util.concurrent.FutureCallback; +import org.apache.iotdb.google.common.util.concurrent.ListeningExecutorService; +import org.apache.iotdb.google.common.util.concurrent.ListeningScheduledExecutorService; import org.apache.iotdb.pipe.api.event.Event; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/queue/ConcurrentIterableLinkedQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/queue/ConcurrentIterableLinkedQueue.java index a73c6cc66eb9d..917cf31e30440 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/queue/ConcurrentIterableLinkedQueue.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/queue/ConcurrentIterableLinkedQueue.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.pipe.datastructure.queue; import org.apache.iotdb.commons.i18n.PipeMessages; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/metric/PipeEventCommitMetrics.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/metric/PipeEventCommitMetrics.java index 05737e5643c45..37572f31f04e1 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/metric/PipeEventCommitMetrics.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/metric/PipeEventCommitMetrics.java @@ -23,12 +23,12 @@ import org.apache.iotdb.commons.pipe.agent.task.progress.PipeEventCommitter; import org.apache.iotdb.commons.service.metric.enums.Metric; import org.apache.iotdb.commons.service.metric.enums.Tag; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/GlobalRateLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/GlobalRateLimiter.java index 358e6c844ec90..80d82d4fe2b6d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/GlobalRateLimiter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/GlobalRateLimiter.java @@ -20,9 +20,8 @@ package org.apache.iotdb.commons.pipe.sink.limiter; import org.apache.iotdb.commons.pipe.config.PipeConfig; - -import com.google.common.util.concurrent.AtomicDouble; -import com.google.common.util.concurrent.RateLimiter; +import org.apache.iotdb.google.common.util.concurrent.AtomicDouble; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; import java.util.concurrent.TimeUnit; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/PipeEndPointRateLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/PipeEndPointRateLimiter.java index 2701e5bb23702..32165b4b160e1 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/PipeEndPointRateLimiter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/limiter/PipeEndPointRateLimiter.java @@ -22,8 +22,7 @@ import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.pipe.agent.task.PipeTaskAgent; import org.apache.iotdb.commons.pipe.config.PipeConfig; - -import com.google.common.util.concurrent.RateLimiter; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/protocol/IoTDBSslSyncSink.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/protocol/IoTDBSslSyncSink.java index 4595a0448eae9..7ae4eabf5593f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/protocol/IoTDBSslSyncSink.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/protocol/IoTDBSslSyncSink.java @@ -28,6 +28,7 @@ import org.apache.iotdb.commons.pipe.sink.client.IoTDBSyncClientManager; import org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferFilePieceReq; import org.apache.iotdb.commons.pipe.sink.payload.thrift.response.PipeTransferFilePieceResp; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.pipe.api.annotation.TableModel; import org.apache.iotdb.pipe.api.annotation.TreeModel; import org.apache.iotdb.pipe.api.customizer.configuration.PipeConnectorRuntimeConfiguration; @@ -38,7 +39,6 @@ import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatGroupByHash.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatGroupByHash.java index be8069486c520..105fd0f0e96b9 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatGroupByHash.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatGroupByHash.java @@ -32,11 +32,11 @@ import java.util.Arrays; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Verify.verify; import static java.lang.Math.min; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.FlatHash.sumExact; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Verify.verify; import static org.apache.tsfile.utils.RamUsageEstimator.sizeOf; // This implementation assumes arrays used in the hash are always a power of 2 diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatHash.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatHash.java index 1484730dec464..7db3024ee6585 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatHash.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/FlatHash.java @@ -26,15 +26,15 @@ import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.utils.RamUsageEstimator; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Throwables.throwIfUnchecked; -import static com.google.common.base.Verify.verify; import static java.lang.Math.addExact; import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Math.toIntExact; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.VariableWidthData.EMPTY_CHUNK; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.VariableWidthData.POINTER_SIZE; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; +import static org.apache.iotdb.google.common.base.Verify.verify; import static org.apache.tsfile.utils.RamUsageEstimator.sizeOf; import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfByteArray; import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfIntArray; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/GroupByHash.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/GroupByHash.java index 50d30045b19ae..92ef039d2564a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/GroupByHash.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/GroupByHash.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash; import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; -import com.google.common.annotations.VisibleForTesting; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.TsBlockBuilder; import org.apache.tsfile.read.common.type.Type; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/HashStrategy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/HashStrategy.java index 39fb108f80855..e7e41180c811e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/HashStrategy.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/HashStrategy.java @@ -19,8 +19,8 @@ package org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash; import org.apache.iotdb.commons.queryengine.plan.relational.utils.TypeUtil; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.block.column.ColumnBuilder; import org.apache.tsfile.read.common.type.Type; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/MarkDistinctHash.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/MarkDistinctHash.java index 54b58bccf388e..ee9995858a5b4 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/MarkDistinctHash.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/MarkDistinctHash.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.UpdateMemory; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; -import com.google.common.annotations.VisibleForTesting; import org.apache.tsfile.block.column.Column; import org.apache.tsfile.read.common.block.column.BooleanColumn; import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; @@ -31,10 +31,10 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.GroupByHash.DEFAULT_GROUP_NUMBER; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.GroupByHash.createGroupByHash; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class MarkDistinctHash { private final GroupByHash groupByHash; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/VariableWidthData.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/VariableWidthData.java index 5684f55da818d..44c677d1d709c 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/VariableWidthData.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/execution/operator/source/relational/aggregation/grouped/hash/VariableWidthData.java @@ -20,21 +20,21 @@ package org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.primitives.Ints; -import com.google.common.primitives.Ints; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static com.google.common.base.Verify.verify; import static java.lang.Math.addExact; import static java.lang.Math.max; import static java.lang.Math.subtractExact; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.FlatHash.bytesToInt; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.FlatHash.intToBytes; import static org.apache.iotdb.commons.queryengine.execution.operator.source.relational.aggregation.grouped.hash.FlatHash.sumExact; +import static org.apache.iotdb.google.common.base.Verify.verify; import static org.apache.tsfile.utils.RamUsageEstimator.sizeOf; import static org.apache.tsfile.utils.RamUsageEstimator.sizeOfObjectArray; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/TableScanNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/TableScanNode.java index 74f462e0b8b43..e4db104ace863 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/TableScanNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/TableScanNode.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.metadata.QualifiedObjectName; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import javax.annotation.Nullable; @@ -43,9 +43,9 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.FIELD; import static org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.TIME; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public abstract class TableScanNode extends SourceNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/SingleChildProcessNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/SingleChildProcessNode.java index cf8c4587dd169..83dbd20f8086b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/SingleChildProcessNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/SingleChildProcessNode.java @@ -21,8 +21,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Objects; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/TwoChildProcessNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/TwoChildProcessNode.java index fe3432610f44d..79cfc5052234c 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/TwoChildProcessNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/planner/plan/node/process/TwoChildProcessNode.java @@ -22,8 +22,7 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Objects; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunction.java index 801728f8e3882..eb5e3a6cfc85e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/function/tvf/PatternMatchTableFunction.java @@ -25,6 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.function.tvf.match.model.Point; import org.apache.iotdb.commons.queryengine.plan.relational.function.tvf.match.model.RegexMatchState; import org.apache.iotdb.commons.queryengine.plan.relational.function.tvf.match.model.Section; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.udf.api.exception.UDFException; import org.apache.iotdb.udf.api.relational.TableFunction; import org.apache.iotdb.udf.api.relational.access.Record; @@ -42,7 +43,6 @@ import org.apache.iotdb.udf.api.relational.table.specification.TableParameterSpecification; import org.apache.iotdb.udf.api.type.Type; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.block.column.ColumnBuilder; import java.util.Arrays; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/MetadataUtil.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/MetadataUtil.java index b138d5888faae..e3eb70e7bb6ef 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/MetadataUtil.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/MetadataUtil.java @@ -23,16 +23,15 @@ import org.apache.iotdb.commons.i18n.SchemaMessages; import org.apache.iotdb.commons.queryengine.common.SessionInfo; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QualifiedName; - -import com.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Lists; import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MetadataUtil { private MetadataUtil() {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/QualifiedObjectName.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/QualifiedObjectName.java index 0e6aa7aaaffca..c46c52bde269f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/QualifiedObjectName.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/QualifiedObjectName.java @@ -27,9 +27,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.metadata.MetadataUtil.checkObjectName; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; @Immutable public class QualifiedObjectName { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/TableSchema.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/TableSchema.java index efcdbc5d4bb49..5c19600749e0d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/TableSchema.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/metadata/TableSchema.java @@ -38,7 +38,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class TableSchema { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Assignments.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Assignments.java index f7258d6bde974..2170a108fc950 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Assignments.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Assignments.java @@ -22,9 +22,8 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Maps; import java.util.Collection; import java.util.LinkedHashMap; @@ -37,9 +36,9 @@ import java.util.function.Predicate; import java.util.stream.Collector; -import static com.google.common.base.Preconditions.checkState; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; public class Assignments { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/DataOrganizationSpecification.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/DataOrganizationSpecification.java index 9b3296f3e9275..b39527f802d1d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/DataOrganizationSpecification.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/DataOrganizationSpecification.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.planner; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/OrderingScheme.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/OrderingScheme.java index bfedcb5fc772c..da0e1b9ef8014 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/OrderingScheme.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/OrderingScheme.java @@ -20,10 +20,10 @@ package org.apache.iotdb.commons.queryengine.plan.relational.planner; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -35,10 +35,10 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class OrderingScheme { private final List orderBy; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Symbol.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Symbol.java index a51787d698a93..748f4f2506ca8 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Symbol.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/Symbol.java @@ -29,8 +29,8 @@ import java.io.IOException; import java.nio.ByteBuffer; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class Symbol implements Comparable { private final String name; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/iterative/GroupReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/iterative/GroupReference.java index 3ce2dff69284e..d90f4b3961886 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/iterative/GroupReference.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/iterative/GroupReference.java @@ -24,8 +24,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AggregationNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AggregationNode.java index 4d0b83eaa2097..8e5122677058e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AggregationNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AggregationNode.java @@ -32,11 +32,11 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -51,8 +51,8 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class AggregationNode extends SingleChildProcessNode { private final Map aggregations; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ApplyNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ApplyNode.java index e5712c448beab..f773ba309d10d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ApplyNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ApplyNode.java @@ -27,8 +27,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.TwoChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; @@ -36,8 +35,8 @@ import java.util.List; import java.util.Map; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class ApplyNode extends TwoChildProcessNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AssignUniqueId.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AssignUniqueId.java index 341446d381f86..99673ea92ad43 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AssignUniqueId.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/AssignUniqueId.java @@ -27,17 +27,16 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class AssignUniqueId extends SingleChildProcessNode { private final Symbol idColumn; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CollectNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CollectNode.java index 6218f0613146e..52ddae0bd1537 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CollectNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CollectNode.java @@ -27,8 +27,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.MultiChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; -import com.google.common.base.Objects; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -37,7 +37,7 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * CollectNode output the content of children. Normally it will output the child one by one, but in diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CorrelatedJoinNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CorrelatedJoinNode.java index ef867bf263722..8d936129ca706 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CorrelatedJoinNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/CorrelatedJoinNode.java @@ -29,16 +29,15 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NullLiteral; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; /** * For every row from {@link #leftChild}(input) a {@link #rightChild}(subquery) relation is diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/EnforceSingleRowNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/EnforceSingleRowNode.java index 6447eebf32e1f..a27b9a29fe07e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/EnforceSingleRowNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/EnforceSingleRowNode.java @@ -26,8 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Iterables; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ExceptNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ExceptNode.java index 13c2d1e1c68d4..055f951edf0c4 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ExceptNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ExceptNode.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.ListMultimap; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/FilterNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/FilterNode.java index 3374f7826a7d1..2730889efc8f7 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/FilterNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/FilterNode.java @@ -27,9 +27,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; - -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterables; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GapFillNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GapFillNode.java index 1bc77eff2ad20..15d0631163a18 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GapFillNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GapFillNode.java @@ -26,8 +26,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GroupNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GroupNode.java index a607b99070c34..d41e52dc21753 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GroupNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/GroupNode.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/IntersectNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/IntersectNode.java index 37a7467a65df8..8934c024ada66 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/IntersectNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/IntersectNode.java @@ -25,8 +25,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.ListMultimap; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/JoinNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/JoinNode.java index 4db22ed5b3ddb..a2aeb1fabdcc0 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/JoinNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/JoinNode.java @@ -30,9 +30,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NullLiteral; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -44,11 +44,11 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.JoinNode.EquiJoinClause.flipBatch; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.node.JoinNode.JoinType.INNER; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class JoinNode extends TwoChildProcessNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LimitNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LimitNode.java index 1f8991a32971d..f8778720f75c5 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LimitNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LimitNode.java @@ -27,9 +27,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LinearFillNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LinearFillNode.java index 9a2434ef51c87..17df04e76646b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LinearFillNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/LinearFillNode.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MarkDistinctNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MarkDistinctNode.java index bd21b487415de..efecb96a22450 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MarkDistinctNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MarkDistinctNode.java @@ -27,9 +27,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -39,8 +39,8 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MarkDistinctNode extends SingleChildProcessNode { private final Symbol markerSymbol; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/Measure.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/Measure.java index 5b96286c0cfdc..608af71f8c0d2 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/Measure.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/Measure.java @@ -30,8 +30,8 @@ import java.nio.ByteBuffer; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Measure { private final ExpressionAndValuePointers expressionAndValuePointers; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MergeSortNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MergeSortNode.java index 731a62c5f6d9a..5c1d61ae14fbc 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MergeSortNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/MergeSortNode.java @@ -27,8 +27,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.MultiChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; -import com.google.common.base.Objects; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -37,7 +37,7 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class MergeSortNode extends MultiChildProcessNode { private final OrderingScheme orderingScheme; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/NextFillNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/NextFillNode.java index 11676253b3334..2df1b950e7448 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/NextFillNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/NextFillNode.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import org.apache.tsfile.utils.TimeDuration; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OffsetNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OffsetNode.java index 3b982191a7e07..403e8a2c4fd61 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OffsetNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OffsetNode.java @@ -26,9 +26,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OutputNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OutputNode.java index bc051fe5d4875..d354bf01a1c64 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OutputNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/OutputNode.java @@ -27,10 +27,10 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -39,7 +39,7 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class OutputNode extends SingleChildProcessNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PatternRecognitionNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PatternRecognitionNode.java index ab8ed1ddcd61e..57245c5263266 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PatternRecognitionNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PatternRecognitionNode.java @@ -31,11 +31,11 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.ExpressionAndValuePointers; import org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrLabel; import org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrRowPattern; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PreviousFillNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PreviousFillNode.java index 950425acfeb51..33fa393809695 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PreviousFillNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/PreviousFillNode.java @@ -25,8 +25,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import org.apache.tsfile.utils.TimeDuration; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ProjectNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ProjectNode.java index d31a9bfee71dc..2a9930d941559 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ProjectNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ProjectNode.java @@ -28,9 +28,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Assignments; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/RowNumberNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/RowNumberNode.java index a83f7976481fd..d56eeffe6350f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/RowNumberNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/RowNumberNode.java @@ -27,9 +27,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -38,7 +38,7 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class RowNumberNode extends SingleChildProcessNode { private final List partitionBy; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SemiJoinNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SemiJoinNode.java index 94e1619dc63dc..43a2bae9a6e1f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SemiJoinNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SemiJoinNode.java @@ -27,8 +27,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.TwoChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.io.DataOutputStream; import java.io.IOException; @@ -36,8 +35,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class SemiJoinNode extends TwoChildProcessNode { private final Symbol sourceJoinSymbol; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SetOperationNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SetOperationNode.java index 9bd3804cc4ae3..0a1d524c8e398 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SetOperationNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SetOperationNode.java @@ -25,26 +25,25 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.MultiChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SymbolReference; - -import com.google.common.base.Function; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.FluentIterable; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.ListMultimap; +import org.apache.iotdb.google.common.collect.Multimap; +import org.apache.iotdb.google.common.collect.Multimaps; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public abstract class SetOperationNode extends MultiChildProcessNode { private final ListMultimap outputToInputs; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SortNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SortNode.java index 50fcedb312f0c..7d6a6a568d4eb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SortNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/SortNode.java @@ -27,9 +27,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; - -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterables; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/StreamSortNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/StreamSortNode.java index 05591c43848ad..10e48a5684c46 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/StreamSortNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/StreamSortNode.java @@ -25,9 +25,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.base.Objects; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionNode.java index adc2d6bf5bfbb..707f58c17192e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionNode.java @@ -29,9 +29,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.function.TableFunctionFactory; import org.apache.iotdb.commons.queryengine.plan.relational.planner.DataOrganizationSpecification; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.udf.api.relational.table.TableFunctionHandle; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -42,8 +42,8 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableFunctionNode extends MultiChildProcessNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionProcessorNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionProcessorNode.java index c65097501687d..691cf3446a51a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionProcessorNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TableFunctionProcessorNode.java @@ -29,9 +29,9 @@ import org.apache.iotdb.commons.queryengine.plan.relational.function.TableFunctionFactory; import org.apache.iotdb.commons.queryengine.plan.relational.planner.DataOrganizationSpecification; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.udf.api.relational.table.TableFunctionHandle; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -42,8 +42,8 @@ import java.util.Optional; import java.util.stream.Collectors; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class TableFunctionProcessorNode extends SingleChildProcessNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKNode.java index a28e39a74cc40..948f9799f6d47 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKNode.java @@ -28,8 +28,8 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.MultiChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.OrderingScheme; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; -import com.google.common.base.Objects; import org.apache.tsfile.utils.ReadWriteIOUtils; import javax.annotation.Nullable; @@ -40,7 +40,7 @@ import java.util.ArrayList; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TopKNode extends MultiChildProcessNode { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKRankingNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKRankingNode.java index 36f775713fc0d..afaf64d81eefc 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKRankingNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/TopKRankingNode.java @@ -27,10 +27,10 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.process.SingleChildProcessNode; import org.apache.iotdb.commons.queryengine.plan.relational.planner.DataOrganizationSpecification; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/UnionNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/UnionNode.java index 28446f7e6a02d..418d96406eedb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/UnionNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/UnionNode.java @@ -25,9 +25,9 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ListMultimap; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValueFillNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValueFillNode.java index 5fe3164fd9b4e..880bd29dc68e4 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValueFillNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValueFillNode.java @@ -26,8 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeType; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Literal; - -import com.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Iterables; import java.io.DataOutputStream; import java.io.IOException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValuesNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValuesNode.java index 3a6bf729d293d..5229aea36f576 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValuesNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/ValuesNode.java @@ -30,8 +30,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Row; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -41,11 +41,11 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class ValuesNode extends SourceNode { private final List outputSymbols; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/WindowNode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/WindowNode.java index 27649533fcb7d..0b1eea9cd04a8 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/WindowNode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/node/WindowNode.java @@ -33,11 +33,11 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.FrameBound; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowFrame; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.errorprone.annotations.Immutable; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -53,12 +53,12 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Iterables.concat; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.FrameBound.Type.CURRENT_ROW; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.FrameBound.Type.UNBOUNDED_PRECEDING; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowFrame.Type.RANGE; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.Iterables.concat; public class WindowNode extends SingleChildProcessNode { private final Set prePartitionedInputs; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/ExpressionAndValuePointers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/ExpressionAndValuePointers.java index f915bdc640f32..4b9bcdf7663cd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/ExpressionAndValuePointers.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/ExpressionAndValuePointers.java @@ -22,8 +22,8 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.plan.relational.planner.Symbol; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; @@ -34,9 +34,9 @@ import java.util.Objects; import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.BooleanLiteral.TRUE_LITERAL; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public class ExpressionAndValuePointers { public static final ExpressionAndValuePointers TRUE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrAlternation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrAlternation.java index 9f0c41a38fb9a..aef114bd45800 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrAlternation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrAlternation.java @@ -30,9 +30,9 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class IrAlternation extends IrRowPattern { private final List patterns; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrConcatenation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrConcatenation.java index a5aace8665a39..06ee4a81b5abd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrConcatenation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrConcatenation.java @@ -30,9 +30,9 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class IrConcatenation extends IrRowPattern { private final List patterns; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPatternAlternationOptimizer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPatternAlternationOptimizer.java index fc2f4f24a974d..c8f9d77f695bd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPatternAlternationOptimizer.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPatternAlternationOptimizer.java @@ -20,14 +20,13 @@ package org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern; import org.apache.iotdb.commons.i18n.QueryMessages; - -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; import static org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern.IrQuantifier.zeroOrOne; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * Remove empty pattern from pattern alternation and replace it with quantification of a diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPermutation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPermutation.java index 48b56bde8eb25..af919c76a5e72 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPermutation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrPermutation.java @@ -30,9 +30,9 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class IrPermutation extends IrRowPattern { private final List patterns; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrRowPatternFlattener.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrRowPatternFlattener.java index 327afe11c2214..0cc82ef285bbf 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrRowPatternFlattener.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/IrRowPatternFlattener.java @@ -25,7 +25,7 @@ import java.util.Optional; import java.util.stream.Stream; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; /** * Optimize row pattern: - remove nested exclusions - flatten alternations and concatenations - diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/LogicalIndexPointer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/LogicalIndexPointer.java index ac1d0d7195086..3711ee9aea177 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/LogicalIndexPointer.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/LogicalIndexPointer.java @@ -30,8 +30,8 @@ import java.util.Objects; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class LogicalIndexPointer { // a set of labels to navigate over: diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/Patterns.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/Patterns.java index e362922e54623..598c254814131 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/Patterns.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/planner/rowpattern/Patterns.java @@ -19,7 +19,7 @@ package org.apache.iotdb.commons.queryengine.plan.relational.planner.rowpattern; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.Optional; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AliasedRelation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AliasedRelation.java index c1f1722ae07a1..caecc2b5c6685 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AliasedRelation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AliasedRelation.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AliasedRelation extends Relation { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllColumns.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllColumns.java index a484574c02ddf..843ee5c055782 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllColumns.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllColumns.java @@ -20,9 +20,9 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllRows.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllRows.java index fe55d2fa8a0a0..80d6397d1883d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllRows.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AllRows.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AnchorPattern.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AnchorPattern.java index a371c175790e7..d779e80dbcc0e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AnchorPattern.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/AnchorPattern.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class AnchorPattern extends RowPattern { public enum Type { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticBinaryExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticBinaryExpression.java index 3b04c385f7daf..f3844efba2b65 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticBinaryExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticBinaryExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticUnaryExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticUnaryExpression.java index 4ccaed9505f2e..d6e0fe66d658a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticUnaryExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ArithmeticUnaryExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BetweenPredicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BetweenPredicate.java index af14a7f5dc580..a1ba97d48277e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BetweenPredicate.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BetweenPredicate.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BinaryLiteral.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BinaryLiteral.java index 7da79a155441f..3ca3ab37637d8 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BinaryLiteral.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BinaryLiteral.java @@ -21,9 +21,9 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.plan.relational.sql.parser.ParsingException; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.io.BaseEncoding; -import com.google.common.base.CharMatcher; -import com.google.common.io.BaseEncoding; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BooleanLiteral.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BooleanLiteral.java index daaf811e5db84..2faf6ead94105 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BooleanLiteral.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/BooleanLiteral.java @@ -29,9 +29,9 @@ import java.nio.ByteBuffer; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class BooleanLiteral extends Literal { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Cast.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Cast.java index a118353169cc9..cfa8e3ab4fb1f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Cast.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Cast.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CoalesceExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CoalesceExpression.java index e7069f0c441b0..2aab1781361e5 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CoalesceExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CoalesceExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -34,8 +34,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public final class CoalesceExpression extends Expression { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Columns.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Columns.java index 7e10773fde6fb..7ff435437a827 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Columns.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Columns.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ComparisonExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ComparisonExpression.java index 8b63a8bb4179a..f4baa7ce21430 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ComparisonExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ComparisonExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentDatabase.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentDatabase.java index 2f1c3ac0dc6d7..285e0a77e625a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentDatabase.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentDatabase.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentTime.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentTime.java index 86250b33a2027..48e048b7e29ac 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentTime.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentTime.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentUser.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentUser.java index 89d2fecfead90..a310d181f626f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentUser.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/CurrentUser.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/DereferenceExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/DereferenceExpression.java index 8e2ec748764d4..28a0fa3502d51 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/DereferenceExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/DereferenceExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/EmptyPattern.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/EmptyPattern.java index d9c017983edfc..faecde8189a95 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/EmptyPattern.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/EmptyPattern.java @@ -19,12 +19,13 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; + import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class EmptyPattern extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Except.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Except.java index da7f224a483e8..f5992fdaf1d69 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Except.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Except.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class Except extends SetOperation { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(Except.class); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExcludedPattern.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExcludedPattern.java index 24198705c155f..2fc4497e666f5 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExcludedPattern.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExcludedPattern.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class ExcludedPattern extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExistsPredicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExistsPredicate.java index bee57c12c560d..2807103773948 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExistsPredicate.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ExistsPredicate.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Extract.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Extract.java index e81052ab654bc..dec4cecb75e33 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Extract.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Extract.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.Immutable; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FieldReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FieldReference.java index 21e5e1af0a08b..1c91b665ccb21 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FieldReference.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FieldReference.java @@ -20,13 +20,13 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class FieldReference extends Expression { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Fill.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Fill.java index 9329e10e36803..b6d388158b190 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Fill.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Fill.java @@ -21,9 +21,9 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.plan.statement.component.FillPolicy; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.MoreObjects; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.TimeDuration; @@ -31,8 +31,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Fill extends Node { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(Fill.class); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FrameBound.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FrameBound.java index 47f00e227e0ee..f96b5a744bf6a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FrameBound.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FrameBound.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -32,8 +32,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class FrameBound extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FunctionCall.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FunctionCall.java index 64fd19a14899d..d84bf1e055d9b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FunctionCall.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/FunctionCall.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GenericDataType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GenericDataType.java index da0f7f9446820..324bd22abee7f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GenericDataType.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GenericDataType.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java index a85f895f3e358..856b5a698106b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class GroupBy extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupingSets.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupingSets.java index 85abde03e23f7..3650b92ee80da 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupingSets.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupingSets.java @@ -20,18 +20,18 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; public class GroupingSets extends GroupingElement { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Identifier.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Identifier.java index 1f1ecd6d6cfc5..3ea132b659b31 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Identifier.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Identifier.java @@ -20,10 +20,10 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.Strings; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.CharMatcher; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -33,10 +33,10 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Verify.verify; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Verify.verify; public class Identifier extends Expression { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IfExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IfExpression.java index ee50c57ce7b0b..561fd00ed9d92 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IfExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IfExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InListExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InListExpression.java index 24bf8976caad2..7df2e7985421b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InListExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InListExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -32,8 +32,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class InListExpression extends Expression { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InPredicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InPredicate.java index 2199ce57a71f0..03788f56fd823 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InPredicate.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/InPredicate.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Intersect.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Intersect.java index cf64028e98072..b9a9b74791aab 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Intersect.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Intersect.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class Intersect extends SetOperation { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNotNullPredicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNotNullPredicate.java index f371d2cdf460a..cc5cc7e8bec54 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNotNullPredicate.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNotNullPredicate.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNullPredicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNullPredicate.java index 1fcbb778866fa..62151fdbdbe7b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNullPredicate.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/IsNullPredicate.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Join.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Join.java index 0acd05c60e334..05157d3431620 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Join.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Join.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -30,9 +30,9 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class Join extends Relation { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinOn.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinOn.java index 4cc5e60f1d5cf..668e1d4f5f466 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinOn.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinOn.java @@ -19,7 +19,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; + import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -27,7 +28,7 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class JoinOn extends JoinCriteria { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(JoinOn.class); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinUsing.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinUsing.java index 09e9781734a8f..b80dc71b3bc65 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinUsing.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/JoinUsing.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class JoinUsing extends JoinCriteria { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LikePredicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LikePredicate.java index cec7e2df0e5f5..ab1d4b23227ab 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LikePredicate.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LikePredicate.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Limit.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Limit.java index 70c0b0be8555f..74506edb6e6c1 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Limit.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Limit.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class Limit extends Node { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(Limit.class); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Literal.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Literal.java index 2de498b28b42c..f770f476603ca 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Literal.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Literal.java @@ -19,7 +19,7 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LogicalExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LogicalExpression.java index 3dc0326835e29..d20dfb8617f3f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LogicalExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/LogicalExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -32,8 +32,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class LogicalExpression extends Expression { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/MeasureDefinition.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/MeasureDefinition.java index 9a95c32c69b9d..f08272012dad9 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/MeasureDefinition.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/MeasureDefinition.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class MeasureDefinition extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NaturalJoin.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NaturalJoin.java index b8afddd75542f..c49e611e5a9dd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NaturalJoin.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NaturalJoin.java @@ -19,12 +19,13 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; + import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class NaturalJoin extends JoinCriteria { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NodeLocation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NodeLocation.java index 6f060ef3897e9..d43c892c55113 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NodeLocation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NodeLocation.java @@ -23,7 +23,7 @@ import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public final class NodeLocation { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NotExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NotExpression.java index 691950becb878..66bcd5b5e3eb6 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NotExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NotExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NullIfExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NullIfExpression.java index 7f45a956b5201..5e3ed0d0f18db 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NullIfExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NullIfExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NumericParameter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NumericParameter.java index 337eaaf46a53f..caf3d6f50dca9 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NumericParameter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/NumericParameter.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nonnull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Offset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Offset.java index ac018a1c22379..6b3f61a46fd93 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Offset.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Offset.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class Offset extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/OrderBy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/OrderBy.java index 43a7538271401..89c7cecc7f367 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/OrderBy.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/OrderBy.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -32,9 +32,9 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class OrderBy extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Parameter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Parameter.java index 6c511e8d7f6d8..955a1df8cd0b3 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Parameter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Parameter.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternAlternation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternAlternation.java index 9e21ad72ee27d..2e3c2c963ff31 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternAlternation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternAlternation.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PatternAlternation extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternConcatenation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternConcatenation.java index be6b80f05cd01..8a4f9f64734e8 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternConcatenation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternConcatenation.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PatternConcatenation extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternPermutation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternPermutation.java index 4b2fe7e102c7a..0e39735912748 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternPermutation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternPermutation.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PatternPermutation extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternQuantifier.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternQuantifier.java index fd30c6f64284b..4791dd934d57e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternQuantifier.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternQuantifier.java @@ -19,12 +19,12 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public abstract class PatternQuantifier extends Node { private final boolean greedy; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternRecognitionRelation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternRecognitionRelation.java index 5097245e3ed39..ec101b0b26134 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternRecognitionRelation.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternRecognitionRelation.java @@ -20,17 +20,17 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class PatternRecognitionRelation extends Relation { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternVariable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternVariable.java index 24d237fbb28c5..dea678793349f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternVariable.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/PatternVariable.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class PatternVariable extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ProcessingMode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ProcessingMode.java index fda29b6d5615c..9fbb0aee71af8 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ProcessingMode.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/ProcessingMode.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class ProcessingMode extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QualifiedName.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QualifiedName.java index ea1ae2ab2db7b..79e01b35717c5 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QualifiedName.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QualifiedName.java @@ -20,9 +20,9 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Lists; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import org.apache.tsfile.utils.Accountable; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -36,11 +36,11 @@ import java.util.List; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.isEmpty; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.isEmpty; public class QualifiedName implements Accountable { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedComparisonExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedComparisonExpression.java index 241f89ceb95d0..2250d81382582 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedComparisonExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedComparisonExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedPattern.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedPattern.java index b8aef3460ae25..6c0dbb703cffb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedPattern.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuantifiedPattern.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class QuantifiedPattern extends RowPattern { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Query.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Query.java index 6c1d0395ad9db..243c860e5f099 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Query.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Query.java @@ -21,17 +21,17 @@ import org.apache.iotdb.commons.i18n.QueryMessages; import org.apache.iotdb.commons.queryengine.utils.cte.CteDataStore; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class Query extends Statement { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuerySpecification.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuerySpecification.java index 2fcc22498e5f7..59d36262ac3c4 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuerySpecification.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/QuerySpecification.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class QuerySpecification extends QueryBody { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/RangeQuantifier.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/RangeQuantifier.java index a22ba6e916652..33d4c96d8eadf 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/RangeQuantifier.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/RangeQuantifier.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class RangeQuantifier extends PatternQuantifier { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Row.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Row.java index e7592e6ee5b81..d2d399c68413f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Row.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Row.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SearchedCaseExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SearchedCaseExpression.java index fa99a71859282..d27227040bbac 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SearchedCaseExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SearchedCaseExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Select.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Select.java index e80936f7c7a84..2da22ee35b352 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Select.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Select.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Select extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleCaseExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleCaseExpression.java index f8075d55a4b1d..c942d624e3d95 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleCaseExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleCaseExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.external.commons.lang3.Validate; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleGroupBy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleGroupBy.java index 7cfa8c4e9f845..fbecf0663eecf 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleGroupBy.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SimpleGroupBy.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public final class SimpleGroupBy extends GroupingElement { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SingleColumn.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SingleColumn.java index fd261f50a2a8a..921635e2d9a9f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SingleColumn.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SingleColumn.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SkipTo.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SkipTo.java index c835a87eb82d9..c4587a795b618 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SkipTo.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SkipTo.java @@ -20,21 +20,21 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SkipTo.Position.FIRST; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SkipTo.Position.LAST; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SkipTo.Position.NEXT; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SkipTo.Position.PAST_LAST; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class SkipTo extends Node { private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(SkipTo.class); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SortItem.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SortItem.java index eac65aaebd58b..90988d5b165fc 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SortItem.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SortItem.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -31,8 +31,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class SortItem extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubqueryExpression.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubqueryExpression.java index a79fcc7f812c5..67c6f4af3d7aa 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubqueryExpression.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubqueryExpression.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubsetDefinition.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubsetDefinition.java index 5d8764556c91f..b33f610036dfd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubsetDefinition.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SubsetDefinition.java @@ -26,9 +26,9 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class SubsetDefinition extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SymbolReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SymbolReference.java index 74fca5d8fc949..a474bd437b635 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SymbolReference.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/SymbolReference.java @@ -19,7 +19,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; + import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Table.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Table.java index e0b9951364b7b..fc4f049157af7 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Table.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Table.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Table extends QueryBody { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionArgument.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionArgument.java index 9763636aa2bdd..14b35d7b917d2 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionArgument.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionArgument.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; import java.util.Optional; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TableFunctionArgument extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionTableArgument.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionTableArgument.java index bdd69a2388fc5..d696c2bb4ad13 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionTableArgument.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableFunctionTableArgument.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableSubquery.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableSubquery.java index 84e0e9b125721..02d7212c95fae 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableSubquery.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TableSubquery.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class TableSubquery extends QueryBody { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Trim.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Trim.java index 0b356c606650f..3bbc1a28a1dbb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Trim.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Trim.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TypeParameter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TypeParameter.java index a8a55b78728ed..7648abc87ca4a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TypeParameter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/TypeParameter.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Union.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Union.java index e57bf6a9c189d..a7bf3dd4dd61b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Union.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Union.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class Union extends SetOperation { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Values.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Values.java index 612ee8285d426..b1ac3b1e448bd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Values.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/Values.java @@ -20,9 +20,9 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/VariableDefinition.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/VariableDefinition.java index 23e73d2280293..0278410b2fafe 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/VariableDefinition.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/VariableDefinition.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class VariableDefinition extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WhenClause.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WhenClause.java index f061f76b8e010..40fd613a404dd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WhenClause.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WhenClause.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowDefinition.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowDefinition.java index 064486e92da7e..a2e7fa62457ef 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowDefinition.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowDefinition.java @@ -20,15 +20,15 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class WindowDefinition extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowFrame.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowFrame.java index 2d8e04e0c0a29..61c7830d30b12 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowFrame.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowFrame.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -32,8 +32,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class WindowFrame extends Node { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowReference.java index e4224263b1e6e..fdce144244267 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowReference.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowReference.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.DataOutputStream; @@ -30,8 +30,8 @@ import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class WindowReference extends Node implements Window { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowSpecification.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowSpecification.java index ff4d3e65d3cce..ca1acb0c7e511 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowSpecification.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WindowSpecification.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import org.apache.tsfile.utils.ReadWriteIOUtils; @@ -33,8 +33,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class WindowSpecification extends Node implements Window { private static final long INSTANCE_SIZE = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/With.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/With.java index e6e68d7f3b958..539558b1acaeb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/With.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/With.java @@ -20,16 +20,16 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import java.util.List; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class With extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WithQuery.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WithQuery.java index 48dda83e42d72..9e1450258fa53 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WithQuery.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/WithQuery.java @@ -20,8 +20,8 @@ package org.apache.iotdb.commons.queryengine.plan.relational.sql.ast; import org.apache.iotdb.commons.i18n.QueryMessages; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.RamUsageEstimator; import javax.annotation.Nullable; @@ -30,8 +30,8 @@ import java.util.Objects; import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public class WithQuery extends Node { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/parser/ParsingException.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/parser/ParsingException.java index 55175096187a9..89ad56a909567 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/parser/ParsingException.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/parser/ParsingException.java @@ -24,7 +24,7 @@ import org.antlr.v4.runtime.RecognitionException; -import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class ParsingException extends RuntimeException { private final int line; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java index 139d501931b82..6e36ea394cc06 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java @@ -55,22 +55,22 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Values; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WithQuery; import org.apache.iotdb.commons.queryengine.plan.statement.component.FillPolicy; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Strings; -import com.google.common.base.Joiner; -import com.google.common.base.Strings; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Iterator; import java.util.List; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.util.ExpressionFormatter.formatOrderBy; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.util.RowPatternFormatter.formatPattern; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public class CommonQuerySqlFormatter implements CommonQueryAstVisitor { @@ -448,7 +448,9 @@ public Void visitSelect(Select node, Integer indent) { } } else { builder.append(' '); - process(com.google.common.collect.Iterables.getOnlyElement(node.getSelectItems()), indent); + process( + org.apache.iotdb.google.common.collect.Iterables.getOnlyElement(node.getSelectItems()), + indent); } builder.append('\n'); @@ -629,7 +631,7 @@ public Void visitPatternRecognitionRelation(PatternRecognitionRelation node, Int formatExpression(measure.getExpression()) + " AS " + formatExpression(measure.getName())) - .collect(com.google.common.collect.ImmutableList.toImmutableList()), + .collect(org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList()), indent + 2); } if (node.getRowsPerMatch().isPresent()) { @@ -700,7 +702,7 @@ public Void visitPatternRecognitionRelation(PatternRecognitionRelation node, Int .ExpressionFormatter ::formatExpression) .collect(joining(", ", "(", ")"))) - .collect(com.google.common.collect.ImmutableList.toImmutableList()), + .collect(org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList()), indent + 2); } append(indent + 1, "DEFINE"); @@ -711,7 +713,7 @@ public Void visitPatternRecognitionRelation(PatternRecognitionRelation node, Int formatExpression(variable.getName()) + " AS " + formatExpression(variable.getExpression())) - .collect(com.google.common.collect.ImmutableList.toImmutableList()), + .collect(org.apache.iotdb.google.common.collect.ImmutableList.toImmutableList()), indent + 2); builder.append(")"); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ExpressionFormatter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ExpressionFormatter.java index 254f015a68412..9566612825c53 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ExpressionFormatter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ExpressionFormatter.java @@ -84,9 +84,8 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowFrame; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowReference; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowSpecification; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.collect.ImmutableList; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -96,13 +95,13 @@ import java.util.Optional; import java.util.function.Function; -import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.util.CommonQuerySqlFormatter.formatName; import static org.apache.iotdb.commons.queryengine.plan.relational.sql.util.ReservedIdentifiers.reserved; import static org.apache.iotdb.commons.udf.builtin.relational.TableBuiltinScalarFunction.DATE_BIN; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; public final class ExpressionFormatter { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ReservedIdentifiers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ReservedIdentifiers.java index e7fd081dce24e..f033003a9eb95 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ReservedIdentifiers.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/ReservedIdentifiers.java @@ -34,8 +34,8 @@ import java.util.Set; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static org.apache.iotdb.db.relational.grammar.sql.RelationalSqlKeywords.sqlKeywords; +import static org.apache.iotdb.google.common.collect.ImmutableSet.toImmutableSet; public final class ReservedIdentifiers { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/TypeSignatureTranslator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/TypeSignatureTranslator.java index 38b1dcd886315..50097b896beda 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/TypeSignatureTranslator.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/type/TypeSignatureTranslator.java @@ -27,18 +27,18 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Identifier; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NumericParameter; import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.TypeParameter; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.read.common.type.Type; import java.util.Collections; import java.util.Locale; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureParameter.numericParameter; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureParameter.typeParameter; import static org.apache.iotdb.commons.queryengine.plan.relational.type.TypeSignatureParameter.typeVariable; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; public class TypeSignatureTranslator { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinScalarFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinScalarFunction.java index 881abc0d7b624..43171b138ec18 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinScalarFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinScalarFunction.java @@ -19,7 +19,7 @@ package org.apache.iotdb.commons.queryengine.plan.udf; -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Arrays; import java.util.HashSet; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinTimeSeriesGeneratingFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinTimeSeriesGeneratingFunction.java index c9cca33b8ae2b..eb6720a76774a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinTimeSeriesGeneratingFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/udf/BuiltinTimeSeriesGeneratingFunction.java @@ -72,8 +72,7 @@ import org.apache.iotdb.commons.udf.builtin.UDTFTopK; import org.apache.iotdb.commons.udf.builtin.UDTFZeroCount; import org.apache.iotdb.commons.udf.builtin.UDTFZeroDuration; - -import com.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSet; import java.util.Arrays; import java.util.HashSet; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java index 23ccb5c91db7e..4c9d83539e5cc 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java @@ -19,7 +19,8 @@ package org.apache.iotdb.commons.schema.column; -import com.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableList; + import org.apache.tsfile.enums.TSDataType; import java.util.List; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java index 645ce6d2b85cb..beb17f364c863 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java @@ -23,8 +23,8 @@ import org.apache.iotdb.commons.schema.table.column.AttributeColumnSchema; import org.apache.iotdb.commons.schema.table.column.FieldColumnSchema; import org.apache.iotdb.commons.schema.table.column.TagColumnSchema; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.enums.TSDataType; import java.util.Collections; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java index 5705336824d03..d3fc8a60d0890 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java @@ -29,8 +29,8 @@ import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema; import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchemaUtil; import org.apache.iotdb.commons.utils.CommonDateTimeUtils; +import org.apache.iotdb.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList; import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.ReadWriteIOUtils; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java index 8342c08365fd3..3dbbd8d205aa7 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java @@ -30,7 +30,7 @@ import java.util.Map; import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; public abstract class TsTableColumnSchema { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/JvmGcMonitorMetrics.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/JvmGcMonitorMetrics.java index c28f9f34a71f0..6107715378af8 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/JvmGcMonitorMetrics.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/JvmGcMonitorMetrics.java @@ -24,13 +24,13 @@ import org.apache.iotdb.commons.concurrent.ThreadName; import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil; import org.apache.iotdb.commons.i18n.ServiceMessages; +import org.apache.iotdb.google.common.base.Preconditions; import org.apache.iotdb.metrics.AbstractMetricService; import org.apache.iotdb.metrics.metricsets.IMetricSet; import org.apache.iotdb.metrics.utils.MetricLevel; import org.apache.iotdb.metrics.utils.MetricType; import org.apache.iotdb.metrics.utils.SystemMetric; -import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/VariationTableFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/VariationTableFunction.java index 0ad9e16ca5c39..e0fbe030d90cc 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/VariationTableFunction.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/relational/tvf/VariationTableFunction.java @@ -20,6 +20,7 @@ package org.apache.iotdb.commons.udf.builtin.relational.tvf; import org.apache.iotdb.commons.i18n.CommonMessages; +import org.apache.iotdb.google.common.collect.ImmutableSet; import org.apache.iotdb.udf.api.exception.UDFException; import org.apache.iotdb.udf.api.exception.UDFTypeMismatchException; import org.apache.iotdb.udf.api.relational.TableFunction; @@ -38,7 +39,6 @@ import org.apache.iotdb.udf.api.relational.table.specification.TableParameterSpecification; import org.apache.iotdb.udf.api.type.Type; -import com.google.common.collect.ImmutableSet; import org.apache.tsfile.block.column.ColumnBuilder; import java.util.Arrays; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/BlobUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/BlobUtils.java index 625d56255435e..0b81dae3cf0c3 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/BlobUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/BlobUtils.java @@ -20,8 +20,7 @@ package org.apache.iotdb.commons.utils; import org.apache.iotdb.commons.i18n.UtilMessages; - -import com.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.CharMatcher; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java index 4adebea8645ba..4e59834bbf715 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java @@ -24,9 +24,8 @@ import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.i18n.UtilMessages; import org.apache.iotdb.commons.path.PartialPath; - -import com.google.common.base.Supplier; -import com.google.common.util.concurrent.RateLimiter; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; import java.io.DataInputStream; import java.io.EOFException; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/KillPoint/KillPoint.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/KillPoint/KillPoint.java index 62dae1d139219..11f8fcce08471 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/KillPoint/KillPoint.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/KillPoint/KillPoint.java @@ -21,8 +21,8 @@ import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.i18n.UtilMessages; +import org.apache.iotdb.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java index 36b3ff84ae7d8..4d7d5d9bc29f9 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/PathUtils.java @@ -22,9 +22,9 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.tsfile.common.constant.TsFileConstant; import org.apache.tsfile.exception.PathParseException; import org.apache.tsfile.file.metadata.IDeviceID; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationFileRemoveRateLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationFileRemoveRateLimiter.java index 40f9ddf9c5448..db9b29d6a6368 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationFileRemoveRateLimiter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationFileRemoveRateLimiter.java @@ -19,7 +19,7 @@ package org.apache.iotdb.commons.utils; -import com.google.common.util.concurrent.RateLimiter; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; public class RegionMigrationFileRemoveRateLimiter { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationRateLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationRateLimiter.java index 0e3e48f75fc03..7e983b6c937f0 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationRateLimiter.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RegionMigrationRateLimiter.java @@ -19,7 +19,7 @@ package org.apache.iotdb.commons.utils; -import com.google.common.util.concurrent.RateLimiter; +import org.apache.iotdb.google.common.util.concurrent.RateLimiter; public class RegionMigrationRateLimiter { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/SingleConsumerQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/SingleConsumerQueue.java new file mode 100644 index 0000000000000..95c5572730e47 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/SingleConsumerQueue.java @@ -0,0 +1,609 @@ +/* + * 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.iotdb.github.benmanes.caffeine; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.AbstractQueue; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +/** + * A lock-free unbounded queue based on linked nodes that supports concurrent producers and is + * restricted to a single consumer. This queue orders elements FIFO (first-in-first-out). The + * head of the queue is that element that has been on the queue the longest time. The + * tail of the queue is that element that has been on the queue the shortest time. New + * elements are inserted at the tail of the queue, and the queue retrieval operations obtain + * elements at the head of the queue. Like most other concurrent collection implementations, this + * class does not permit the use of {@code null} elements. + * + *

A {@code SingleConsumerQueue} is an appropriate choice when many producer threads will share + * access to a common collection and a single consumer thread drains it. This collection is useful + * in scenarios such as implementing flat combining, actors, or lock amortization. + * + *

This implementation employs combination to transfer elements between threads that are + * producing concurrently. This approach avoids contention on the queue by combining colliding + * operations that have identical semantics. When a pair of producers collide, the task of + * performing the combined set of operations is delegated to one of the threads and the other thread + * optionally waits for its operation to be completed. This decision of whether to wait for + * completion is determined by constructing either a linearizable or optimistic + * queue. + * + *

Iterators are weakly consistent, returning elements reflecting the state of the queue + * at some point at or since the creation of the iterator. They do not throw {@link + * java.util.ConcurrentModificationException}, and may proceed concurrently with other operations. + * Elements contained in the queue since the creation of the iterator will be returned exactly once. + * + *

Beware that it is the responsibility of the caller to ensure that a consumer has exclusive + * read access to the queue. This implementation does not include fail-fast behavior to + * guard against incorrect consumer usage. + * + *

Beware that, unlike in most collections, the {@code size} method is NOT a + * constant-time operation. Because of the asynchronous nature of these queues, determining the + * current number of elements requires a traversal of the elements, and so may report inaccurate + * results if this collection is modified during traversal. + * + *

Warning: This class is scheduled for removal in version 3.0.0. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of elements held in this collection + * @deprecated Scheduled for removal in version 3.0.0 + */ +@Deprecated +@SuppressWarnings("NullAway") +public final class SingleConsumerQueue extends SCQHeader.HeadAndTailRef + implements Queue, Serializable { + + /* + * The queue is represented as a singly-linked list with an atomic head and tail reference. It is + * based on the non-intrusive multi-producer / single-consumer node queue described by + * Dmitriy Vyukov [1]. + * + * The backoff strategy of combining operations with identical semantics is based on inverting + * the elimination technique [2]. Elimination allows pairs of operations with reverse semantics, + * like pushes and pops on a stack, to complete without any central coordination and therefore + * substantially aids scalability. The approach of applying elimination and reversing its + * semantics was explored in [3, 4]. Unlike other approaches, this implementation does not use + * opcodes or a background thread. + * + * This implementation borrows optimizations from {@link java.util.concurrent.Exchanger} for + * choosing an arena location and awaiting a match [5]. + * + * [1] Non-intrusive MPSC node-based queue + * http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue + * [2] A Scalable Lock-free Stack Algorithm + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.156.8728 + * [3] Using Elimination to Implement Scalable and Lock-Free FIFO Queues + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.108.6422 + * [4] A Dynamic Elimination-Combining Stack Algorithm + * http://www.cs.bgu.ac.il/~hendlerd/papers/DECS.pdf + * [5] A Scalable Elimination-based Exchange Channel + * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.59.7396 + */ + + /** The number of CPUs */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** The number of slots in the elimination array. */ + static final int ARENA_LENGTH = ceilingPowerOfTwo((NCPU + 1) / 2); + + /** The mask value for indexing into the arena. */ + static final int ARENA_MASK = ARENA_LENGTH - 1; + + /** The factory for creating an optimistic node. */ + static final Function OPTIMISIC = Node::new; + + /** + * The number of times to spin (doing nothing except polling a memory location) before giving up + * while waiting to eliminate an operation. Should be zero on uniprocessors. On multiprocessors, + * this value should be large enough so that two threads exchanging items as fast as possible + * block only when one of them is stalled (due to GC or preemption), but not much longer, to avoid + * wasting CPU resources. Seen differently, this value is a little over half the number of cycles + * of an average context switch time on most systems. The value here is approximately the average + * of those across a range of tested systems. + */ + static final int SPINS = (NCPU == 1) ? 0 : 2000; + + /** The offset to the thread-specific probe field. */ + static final long PROBE = + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.objectFieldOffset( + Thread.class, "threadLocalRandomProbe"); + + static int ceilingPowerOfTwo(int x) { + // From Hacker's Delight, Chapter 3, Harry S. Warren Jr. + return 1 << -Integer.numberOfLeadingZeros(x - 1); + } + + final AtomicReference>[] arena; + final Function> factory; + + @SuppressWarnings({"unchecked", "rawtypes"}) + private SingleConsumerQueue(Function> factory) { + arena = new AtomicReference[ARENA_LENGTH]; + for (int i = 0; i < ARENA_LENGTH; i++) { + arena[i] = new AtomicReference<>(); + } + Node node = new Node<>(null); + this.factory = factory; + lazySetTail(node); + head = node; + } + + /** + * Creates a queue with an optimistic backoff strategy. A thread completes its operation without + * waiting after it successfully hands off the additional element(s) to another producing thread + * for batch insertion. This optimistic behavior may result in additions not appearing in FIFO + * order due to the backoff strategy trying to compensate for queue contention. + * + * @param the type of elements held in this collection + * @return a new queue where producers complete their operation immediately if combined with + * another producing thread's + */ + public static SingleConsumerQueue optimistic() { + @SuppressWarnings("unchecked") + Function> factory = (Function>) OPTIMISIC; + return new SingleConsumerQueue<>(factory); + } + + /** + * Creates a queue with a linearizable backoff strategy. A thread waits for a completion signal if + * it successfully hands off the additional element(s) to another producing thread for batch + * insertion. + * + * @param the type of elements held in this collection + * @return a new queue where producers wait for a completion signal after combining its addition + * with another producing thread's + */ + public static SingleConsumerQueue linearizable() { + return new SingleConsumerQueue<>(LinearizableNode::new); + } + + @Override + public boolean isEmpty() { + return (head == tail); + } + + @Override + public int size() { + Node cursor = head; + Node t = tail; + int size = 0; + while ((cursor != t) && (size != Integer.MAX_VALUE)) { + Node next = cursor.getNextRelaxed(); + if (next == null) { + while ((next = cursor.next) == null) {} + } + cursor = next; + size++; + } + return size; + } + + @Override + public boolean contains(Object o) { + if (o == null) { + return false; + } + for (Iterator it = iterator(); it.hasNext(); ) { + if (o.equals(it.next())) { + return true; + } + } + return false; + } + + @Override + public E peek() { + Node h = head; + Node t = tail; + if (h == t) { + return null; + } + Node next = h.getNextRelaxed(); + if (next == null) { + while ((next = h.next) == null) {} + } + return next.value; + } + + @Override + public boolean offer(E e) { + requireNonNull(e); + + Node node = factory.apply(e); + append(node, node); + return true; + } + + @Override + public E poll() { + Node h = head; + Node next = h.getNextRelaxed(); + if (next == null) { + if (h == tail) { + return null; + } else { + while ((next = h.next) == null) {} + } + } + E e = next.value; + next.value = null; + head = next; + if (factory == OPTIMISIC) { + h.next = null; // prevent nepotism + } + return e; + } + + @Override + public boolean add(E e) { + return offer(e); + } + + @Override + public boolean addAll(Collection c) { + requireNonNull(c); + + Node first = null; + Node last = null; + for (E e : c) { + requireNonNull(e); + if (first == null) { + first = factory.apply(e); + last = first; + } else { + Node newLast = new Node<>(e); + last.lazySetNext(newLast); + last = newLast; + } + } + if (first == null) { + return false; + } + append(first, last); + return true; + } + + /** Adds the linked list of nodes to the queue. */ + void append(Node first, Node last) { + for (; ; ) { + Node t = tail; + if (casTail(t, last)) { + t.lazySetNext(first); + if (factory == OPTIMISIC) { + return; + } + for (; ; ) { + first.complete(); + if (first == last) { + return; + } + Node next = first.getNextRelaxed(); + if (next == null) { + return; + } else if (next.value == null) { + first.next = null; // reduce nepotism + } + first = next; + } + } + Node node = transferOrCombine(first, last); + if (node == null) { + first.await(); + return; + } else if (node != first) { + last = node; + } + } + } + + /** + * Attempts to receive a linked list from a waiting producer or transfer the specified linked list + * to an arriving producer. + * + * @param first the first node in the linked list to try to transfer + * @param last the last node in the linked list to try to transfer + * @return either {@code null} if the element was transferred, the first node if neither a + * transfer nor receive were successful, or the received last element from a producer + */ + Node transferOrCombine(Node first, Node last) { + int index = index(); + AtomicReference> slot = arena[index]; + + for (; ; ) { + Node found = slot.get(); + if (found == null) { + if (slot.compareAndSet(null, first)) { + for (int spin = 0; spin < SPINS; spin++) { + if (slot.get() != first) { + return null; + } + } + return slot.compareAndSet(first, null) ? first : null; + } + } else if (slot.compareAndSet(found, null)) { + last.lazySetNext(found); + last = findLast(found); + for (int i = 1; i < ARENA_LENGTH; i++) { + slot = arena[(i + index) & ARENA_MASK]; + found = slot.get(); + if ((found != null) && slot.compareAndSet(found, null)) { + last.lazySetNext(found); + last = findLast(found); + } + } + return last; + } + } + } + + /** Returns the arena index for the current thread. */ + static int index() { + int probe = + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.UNSAFE.getInt( + Thread.currentThread(), PROBE); + if (probe == 0) { + ThreadLocalRandom.current(); // force initialization + probe = + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.UNSAFE.getInt( + Thread.currentThread(), PROBE); + } + return (probe & ARENA_MASK); + } + + /** Returns the last node in the linked list. */ + static Node findLast(Node node) { + Node next; + while ((next = node.getNextRelaxed()) != null) { + node = next; + } + return node; + } + + @Override + public Iterator iterator() { + return new Iterator() { + Node prev; + Node t = tail; + Node cursor = head; + boolean failOnRemoval = true; + + @Override + public boolean hasNext() { + return (cursor != t); + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + advance(); + failOnRemoval = false; + return cursor.value; + } + + private void advance() { + if ((prev == null) || !failOnRemoval) { + prev = cursor; + } + cursor = awaitNext(); + } + + @Override + public void remove() { + if (failOnRemoval) { + throw new IllegalStateException(); + } + failOnRemoval = true; + cursor.value = null; + + if (t == cursor) { + prev.lazySetNext(null); + if (casTail(t, prev)) { + return; + } + } + prev.lazySetNext(awaitNext()); + } + + Node awaitNext() { + if (cursor.getNextRelaxed() == null) { + while (cursor.next == null) {} + } + return cursor.getNextRelaxed(); + } + }; + } + + /* --------------- Serialization Support --------------- */ + + static final long serialVersionUID = 1; + + Object writeReplace() { + return new SerializationProxy<>(this); + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + /** A proxy that is serialized instead of the queue. */ + static final class SerializationProxy implements Serializable { + final boolean linearizable; + final List elements; + + SerializationProxy(SingleConsumerQueue queue) { + linearizable = (queue.factory.apply(null) instanceof LinearizableNode); + elements = new ArrayList<>(queue); + } + + Object readResolve() { + SingleConsumerQueue queue = linearizable ? linearizable() : optimistic(); + queue.addAll(elements); + return queue; + } + + static final long serialVersionUID = 1; + } + + static class Node { + static final long NEXT_OFFSET = + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.objectFieldOffset( + Node.class, "next"); + + E value; + volatile Node next; + + Node(E value) { + this.value = value; + } + + @SuppressWarnings("unchecked") + Node getNextRelaxed() { + return (Node) + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.UNSAFE.getObject( + this, NEXT_OFFSET); + } + + void lazySetNext(Node newNext) { + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.UNSAFE.putOrderedObject( + this, NEXT_OFFSET, newNext); + } + + /** A no-op notification that the element was added to the queue. */ + void complete() {} + + /** A no-op wait until the operation has completed. */ + void await() {} + + /** Always returns that the operation completed. */ + boolean isDone() { + return true; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + value + "]"; + } + } + + static final class LinearizableNode extends Node { + volatile boolean done; + + LinearizableNode(E value) { + super(value); + } + + /** A notification that the element was added to the queue. */ + @Override + void complete() { + done = true; + } + + /** A busy wait until the operation has completed. */ + @Override + void await() { + while (!done) {} + } + + /** Returns whether the operation completed. */ + @Override + boolean isDone() { + return done; + } + } +} + +/** The namespace for field padding through inheritance. */ +final class SCQHeader { + abstract static class PadHead extends AbstractQueue { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; + } + + /** Enforces a memory layout to avoid false sharing by padding the head node. */ + abstract static class HeadRef extends PadHead { + @SuppressWarnings("deprecation") + SingleConsumerQueue.Node head; + } + + abstract static class PadHeadAndTail extends HeadRef { + byte p120, p121, p122, p123, p124, p125, p126, p127; + byte p128, p129, p130, p131, p132, p133, p134, p135; + byte p136, p137, p138, p139, p140, p141, p142, p143; + byte p144, p145, p146, p147, p148, p149, p150, p151; + byte p152, p153, p154, p155, p156, p157, p158, p159; + byte p160, p161, p162, p163, p164, p165, p166, p167; + byte p168, p169, p170, p171, p172, p173, p174, p175; + byte p176, p177, p178, p179, p180, p181, p182, p183; + byte p184, p185, p186, p187, p188, p189, p190, p191; + byte p192, p193, p194, p195, p196, p197, p198, p199; + byte p200, p201, p202, p203, p204, p205, p206, p207; + byte p208, p209, p210, p211, p212, p213, p214, p215; + byte p216, p217, p218, p219, p220, p221, p222, p223; + byte p224, p225, p226, p227, p228, p229, p230, p231; + byte p232, p233, p234, p235, p236, p237, p238, p239; + } + + /** Enforces a memory layout to avoid false sharing by padding the tail node. */ + @SuppressWarnings("deprecation") + abstract static class HeadAndTailRef extends PadHeadAndTail { + static final long TAIL_OFFSET = + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.objectFieldOffset( + HeadAndTailRef.class, "tail"); + + volatile SingleConsumerQueue.Node tail; + + void lazySetTail(SingleConsumerQueue.Node next) { + org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.UNSAFE.putOrderedObject( + this, TAIL_OFFSET, next); + } + + boolean casTail(SingleConsumerQueue.Node expect, SingleConsumerQueue.Node update) { + return org.apache.iotdb.github.benmanes.caffeine.base.UnsafeAccess.UNSAFE + .compareAndSwapObject(this, TAIL_OFFSET, expect, update); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/base/UnsafeAccess.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/base/UnsafeAccess.java new file mode 100644 index 0000000000000..938de552e1549 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/base/UnsafeAccess.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.github.benmanes.caffeine.base; + +import sun.misc.Unsafe; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +/** + * Static access to {@link Unsafe} and convenient utility methods for performing low-level, unsafe + * operations. + * + *

Warning: This class is scheduled for removal in version 3.0.0. + * + * @author ben.manes@gmail.com (Ben Manes) + * @deprecated Scheduled for removal in version 3.0.0 + */ +@Deprecated +@SuppressWarnings("restriction") +public final class UnsafeAccess { + static final String ANDROID = "THE_ONE"; + static final String OPEN_JDK = "theUnsafe"; + + /** The Unsafe instance. */ + public static final Unsafe UNSAFE; + + static { + try { + UNSAFE = load(OPEN_JDK, ANDROID); + } catch (Exception e) { + throw new Error("Failed to load sun.misc.Unsafe", e); + } + } + + /** + * Returns the location of a given static field. + * + * @param clazz the class containing the field + * @param fieldName the name of the field + * @return the address offset of the field + */ + public static long objectFieldOffset(Class clazz, String fieldName) { + try { + return UNSAFE.objectFieldOffset(clazz.getDeclaredField(fieldName)); + } catch (NoSuchFieldException | SecurityException e) { + throw new Error(e); + } + } + + static Unsafe load(String openJdk, String android) + throws NoSuchMethodException, + InstantiationException, + IllegalAccessException, + InvocationTargetException { + Field field; + try { + // try OpenJDK field name + field = Unsafe.class.getDeclaredField(openJdk); + } catch (NoSuchFieldException e) { + try { + // try Android field name + field = Unsafe.class.getDeclaredField(android); + } catch (NoSuchFieldException e2) { + // try to create a new instance + Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); + unsafeConstructor.setAccessible(true); + return unsafeConstructor.newInstance(); + } + } + field.setAccessible(true); + return (Unsafe) field.get(null); + } + + private UnsafeAccess() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/base/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/base/package-info.java new file mode 100644 index 0000000000000..107243e1d1ed5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/base/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.github.benmanes.caffeine.base; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AbstractLinkedDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AbstractLinkedDeque.java new file mode 100644 index 0000000000000..8a44ac8c59289 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AbstractLinkedDeque.java @@ -0,0 +1,406 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.NoSuchElementException; + +/** + * This class provides a skeletal implementation of the {@link LinkedDeque} interface to minimize + * the effort required to implement this interface. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of elements held in this collection + */ +abstract class AbstractLinkedDeque extends AbstractCollection implements LinkedDeque { + + // This class provides a doubly-linked list that is optimized for the virtual machine. The first + // and last elements are manipulated instead of a slightly more convenient sentinel element to + // avoid the insertion of null checks with NullPointerException throws in the byte code. The links + // to a removed element are cleared to help a generational garbage collector if the discarded + // elements inhabit more than one generation. + + /** Pointer to first node. Invariant: (first == null && last == null) || (first.prev == null) */ + E first; + + /** Pointer to last node. Invariant: (first == null && last == null) || (last.next == null) */ + E last; + + /** + * Links the element to the front of the deque so that it becomes the first element. + * + * @param e the unlinked element + */ + void linkFirst(final E e) { + final E f = first; + first = e; + + if (f == null) { + last = e; + } else { + setPrevious(f, e); + setNext(e, f); + } + } + + /** + * Links the element to the back of the deque so that it becomes the last element. + * + * @param e the unlinked element + */ + void linkLast(final E e) { + final E l = last; + last = e; + + if (l == null) { + first = e; + } else { + setNext(l, e); + setPrevious(e, l); + } + } + + /** Unlinks the non-null first element. */ + @SuppressWarnings("NullAway") + E unlinkFirst() { + final E f = first; + final E next = getNext(f); + setNext(f, null); + + first = next; + if (next == null) { + last = null; + } else { + setPrevious(next, null); + } + return f; + } + + /** Unlinks the non-null last element. */ + @SuppressWarnings("NullAway") + E unlinkLast() { + final E l = last; + final E prev = getPrevious(l); + setPrevious(l, null); + last = prev; + if (prev == null) { + first = null; + } else { + setNext(prev, null); + } + return l; + } + + /** Unlinks the non-null element. */ + void unlink(E e) { + final E prev = getPrevious(e); + final E next = getNext(e); + + if (prev == null) { + first = next; + } else { + setNext(prev, next); + setPrevious(e, null); + } + + if (next == null) { + last = prev; + } else { + setPrevious(next, prev); + setNext(e, null); + } + } + + @Override + public boolean isEmpty() { + return (first == null); + } + + void checkNotEmpty() { + if (isEmpty()) { + throw new NoSuchElementException(); + } + } + + /** + * {@inheritDoc} + * + *

Beware that, unlike in most collections, this method is NOT a constant-time + * operation. + */ + @Override + public int size() { + int size = 0; + for (E e = first; e != null; e = getNext(e)) { + size++; + } + return size; + } + + @Override + @SuppressWarnings("PMD.AvoidReassigningLoopVariables") + public void clear() { + for (E e = first; e != null; ) { + E next = getNext(e); + setPrevious(e, null); + setNext(e, null); + e = next; + } + first = last = null; + } + + @Override + public abstract boolean contains(Object o); + + @Override + public boolean isFirst(E e) { + return (e != null) && (e == first); + } + + @Override + public boolean isLast(E e) { + return (e != null) && (e == last); + } + + @Override + public void moveToFront(E e) { + if (e != first) { + unlink(e); + linkFirst(e); + } + } + + @Override + public void moveToBack(E e) { + if (e != last) { + unlink(e); + linkLast(e); + } + } + + @Override + public E peek() { + return peekFirst(); + } + + @Override + public E peekFirst() { + return first; + } + + @Override + public E peekLast() { + return last; + } + + @Override + @SuppressWarnings("NullAway") + public E getFirst() { + checkNotEmpty(); + return peekFirst(); + } + + @Override + @SuppressWarnings("NullAway") + public E getLast() { + checkNotEmpty(); + return peekLast(); + } + + @Override + public E element() { + return getFirst(); + } + + @Override + public boolean offer(E e) { + return offerLast(e); + } + + @Override + public boolean offerFirst(E e) { + if (contains(e)) { + return false; + } + linkFirst(e); + return true; + } + + @Override + public boolean offerLast(E e) { + if (contains(e)) { + return false; + } + linkLast(e); + return true; + } + + @Override + public boolean add(E e) { + return offerLast(e); + } + + @Override + public void addFirst(E e) { + if (!offerFirst(e)) { + throw new IllegalArgumentException(); + } + } + + @Override + public void addLast(E e) { + if (!offerLast(e)) { + throw new IllegalArgumentException(); + } + } + + @Override + public E poll() { + return pollFirst(); + } + + @Override + public E pollFirst() { + return isEmpty() ? null : unlinkFirst(); + } + + @Override + public E pollLast() { + return isEmpty() ? null : unlinkLast(); + } + + @Override + public E remove() { + return removeFirst(); + } + + @Override + @SuppressWarnings("NullAway") + public E removeFirst() { + checkNotEmpty(); + return pollFirst(); + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return remove(o); + } + + @Override + @SuppressWarnings("NullAway") + public E removeLast() { + checkNotEmpty(); + return pollLast(); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return remove(o); + } + + @Override + public boolean removeAll(Collection c) { + boolean modified = false; + for (Object o : c) { + modified |= remove(o); + } + return modified; + } + + @Override + public void push(E e) { + addFirst(e); + } + + @Override + public E pop() { + return removeFirst(); + } + + @Override + public PeekingIterator iterator() { + return new AbstractLinkedIterator(first) { + @SuppressWarnings("NullAway") + @Override + E computeNext() { + return getNext(cursor); + } + }; + } + + @Override + public PeekingIterator descendingIterator() { + return new AbstractLinkedIterator(last) { + @SuppressWarnings("NullAway") + @Override + E computeNext() { + return getPrevious(cursor); + } + }; + } + + abstract class AbstractLinkedIterator implements PeekingIterator { + E previous; + E cursor; + + /** + * Creates an iterator that can can traverse the deque. + * + * @param start the initial element to begin traversal from + */ + AbstractLinkedIterator(E start) { + cursor = start; + } + + @Override + public boolean hasNext() { + return (cursor != null); + } + + @Override + public E peek() { + return cursor; + } + + @Override + @SuppressWarnings("NullAway") + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + previous = cursor; + cursor = computeNext(); + return previous; + } + + /** Retrieves the next element to traverse to or null if there are no more elements. */ + abstract E computeNext(); + + @Override + public void remove() { + if (previous == null) { + throw new IllegalStateException(); + } + AbstractLinkedDeque.this.remove(previous); + previous = null; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AccessOrderDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AccessOrderDeque.java new file mode 100644 index 0000000000000..09d6d0f14d89e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AccessOrderDeque.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.AccessOrderDeque.AccessOrder; + +import java.util.Deque; + +/** + * A linked deque implementation used to represent an access-order queue. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of elements held in this collection + */ +final class AccessOrderDeque> extends AbstractLinkedDeque { + + @Override + public boolean contains(Object o) { + return (o instanceof AccessOrder) && contains((AccessOrder) o); + } + + // A fast-path containment check + boolean contains(AccessOrder e) { + return (e.getPreviousInAccessOrder() != null) + || (e.getNextInAccessOrder() != null) + || (e == first); + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + return (o instanceof AccessOrder) && remove((E) o); + } + + // A fast-path removal + boolean remove(E e) { + if (contains(e)) { + unlink(e); + return true; + } + return false; + } + + @Override + public E getPrevious(E e) { + return e.getPreviousInAccessOrder(); + } + + @Override + public void setPrevious(E e, E prev) { + e.setPreviousInAccessOrder(prev); + } + + @Override + public E getNext(E e) { + return e.getNextInAccessOrder(); + } + + @Override + public void setNext(E e, E next) { + e.setNextInAccessOrder(next); + } + + /** An element that is linked on the {@link Deque}. */ + interface AccessOrder> { + + /** + * Retrieves the previous element or null if either the element is unlinked or the + * first element on the deque. + */ + T getPreviousInAccessOrder(); + + /** Sets the previous element or null if there is no link. */ + void setPreviousInAccessOrder(T prev); + + /** + * Retrieves the next element or null if either the element is unlinked or the last + * element on the deque. + */ + T getNextInAccessOrder(); + + /** Sets the next element or null if there is no link. */ + void setNextInAccessOrder(T next); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Async.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Async.java new file mode 100644 index 0000000000000..a7b6d05809fc4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Async.java @@ -0,0 +1,178 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.github.benmanes.caffeine.cache.BoundedLocalCache.MAXIMUM_EXPIRY; + +/** + * Static utility methods and classes pertaining to asynchronous operations. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +final class Async { + static final long ASYNC_EXPIRY = (Long.MAX_VALUE >> 1) + (Long.MAX_VALUE >> 2); // 220 years + + private Async() {} + + /** Returns if the future has successfully completed. */ + static boolean isReady(CompletableFuture future) { + return (future != null) + && future.isDone() + && !future.isCompletedExceptionally() + && (future.join() != null); + } + + /** Returns the current value or null if either not done or failed. */ + @SuppressWarnings("NullAway") + static V getIfReady(CompletableFuture future) { + return isReady(future) ? future.join() : null; + } + + /** Returns the value when completed successfully or null if failed. */ + static V getWhenSuccessful(CompletableFuture future) { + try { + return (future == null) ? null : future.join(); + } catch (CancellationException | CompletionException e) { + return null; + } + } + + /** + * A removal listener that asynchronously forwards the value stored in a {@link CompletableFuture} + * if successful to the user-supplied removal listener. + */ + static final class AsyncRemovalListener + implements RemovalListener>, Serializable { + private static final long serialVersionUID = 1L; + + final RemovalListener delegate; + final Executor executor; + + AsyncRemovalListener(RemovalListener delegate, Executor executor) { + this.delegate = requireNonNull(delegate); + this.executor = requireNonNull(executor); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + public void onRemoval(K key, CompletableFuture future, RemovalCause cause) { + if (future != null) { + future.thenAcceptAsync( + value -> { + if (value != null) { + delegate.onRemoval(key, value, cause); + } + }, + executor); + } + } + + Object writeReplace() { + return delegate; + } + } + + /** + * A weigher for asynchronous computations. When the value is being loaded this weigher returns + * {@code 0} to indicate that the entry should not be evicted due to a size constraint. If the + * value is computed successfully the entry must be reinserted so that the weight is updated and + * the expiration timeouts reflect the value once present. This can be done safely using {@link + * Map#replace(Object, Object, Object)}. + */ + static final class AsyncWeigher implements Weigher>, Serializable { + private static final long serialVersionUID = 1L; + + final Weigher delegate; + + AsyncWeigher(Weigher delegate) { + this.delegate = requireNonNull(delegate); + } + + @Override + public int weigh(K key, CompletableFuture future) { + return isReady(future) ? delegate.weigh(key, future.join()) : 0; + } + + Object writeReplace() { + return delegate; + } + } + + /** + * An expiry for asynchronous computations. When the value is being loaded this expiry returns + * {@code ASYNC_EXPIRY} to indicate that the entry should not be evicted due to an expiry + * constraint. If the value is computed successfully the entry must be reinserted so that the + * expiration is updated and the expiration timeouts reflect the value once present. The value + * maximum range is reserved to coordinate the asynchronous life cycle. + */ + static final class AsyncExpiry implements Expiry>, Serializable { + private static final long serialVersionUID = 1L; + + final Expiry delegate; + + AsyncExpiry(Expiry delegate) { + this.delegate = requireNonNull(delegate); + } + + @Override + public long expireAfterCreate(K key, CompletableFuture future, long currentTime) { + if (isReady(future)) { + long duration = delegate.expireAfterCreate(key, future.join(), currentTime); + return Math.min(duration, MAXIMUM_EXPIRY); + } + return ASYNC_EXPIRY; + } + + @Override + public long expireAfterUpdate( + K key, CompletableFuture future, long currentTime, long currentDuration) { + if (isReady(future)) { + long duration = + (currentDuration > MAXIMUM_EXPIRY) + ? delegate.expireAfterCreate(key, future.join(), currentTime) + : delegate.expireAfterUpdate(key, future.join(), currentTime, currentDuration); + return Math.min(duration, MAXIMUM_EXPIRY); + } + return ASYNC_EXPIRY; + } + + @Override + public long expireAfterRead( + K key, CompletableFuture future, long currentTime, long currentDuration) { + if (isReady(future)) { + long duration = delegate.expireAfterRead(key, future.join(), currentTime, currentDuration); + return Math.min(duration, MAXIMUM_EXPIRY); + } + return ASYNC_EXPIRY; + } + + Object writeReplace() { + return delegate; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncCache.java new file mode 100644 index 0000000000000..92f0715f52361 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncCache.java @@ -0,0 +1,199 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A semi-persistent mapping from keys to values. Cache entries are manually added using {@link + * #get(Object, Function)} or {@link #put(Object, CompletableFuture)}, and are stored in the cache + * until either evicted or manually invalidated. + * + *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of keys maintained by this cache + * @param the type of mapped values + */ +public interface AsyncCache { + + /** + * Returns the future associated with {@code key} in this cache, or {@code null} if there is no + * cached future for {@code key}. + * + * @param key key whose associated value is to be returned + * @return the current (existing or computed) future value to which the specified key is mapped, + * or {@code null} if this cache contains no mapping for the key + * @throws NullPointerException if the specified key is null + */ + CompletableFuture getIfPresent(Object key); + + /** + * Returns the future associated with {@code key} in this cache, obtaining that value from {@code + * mappingFunction} if necessary. This method provides a simple substitute for the conventional + * "if cached, return; otherwise create, cache and return" pattern. + * + *

If the specified key is not already associated with a value, attempts to compute its value + * asynchronously and enters it into this cache unless {@code null}. The entire method invocation + * is performed atomically, so the function is applied at most once per key. If the asynchronous + * computation fails, the entry will be automatically removed from this cache. + * + *

Warning: as with {@link CacheLoader#load}, {@code mappingFunction} must not + * attempt to update any other mappings of this cache. + * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to asynchronously compute a value + * @return the current (existing or computed) future value associated with the specified key + * @throws NullPointerException if the specified key or mappingFunction is null + */ + CompletableFuture get(K key, Function mappingFunction); + + /** + * Returns the future associated with {@code key} in this cache, obtaining that value from {@code + * mappingFunction} if necessary. This method provides a simple substitute for the conventional + * "if cached, return; otherwise create, cache and return" pattern. + * + *

If the specified key is not already associated with a value, attempts to compute its value + * asynchronously and enters it into this cache unless {@code null}. The entire method invocation + * is performed atomically, so the function is applied at most once per key. If the asynchronous + * computation fails, the entry will be automatically removed from this cache. + * + *

Warning: as with {@link CacheLoader#load}, {@code mappingFunction} must not + * attempt to update any other mappings of this cache. + * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to asynchronously compute a value + * @return the current (existing or computed) future value associated with the specified key + * @throws NullPointerException if the specified key or mappingFunction is null, or if the future + * returned by the mappingFunction is null + * @throws RuntimeException or Error if the mappingFunction does when constructing the future, in + * which case the mapping is left unestablished + */ + CompletableFuture get( + K key, BiFunction> mappingFunction); + + /** + * Returns the future of a map of the values associated with {@code keys}, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries; it will never contain null keys or values. If the any of the + * asynchronous computations fail, those entries will be automatically removed from this cache. + * + *

A single request to the {@code mappingFunction} is performed for all keys which are not + * already present in the cache. If another call to {@link #get} tries to load the value for a key + * in {@code keys}, that thread retrieves a future that is completed by this bulk computation. Any + * loaded values for keys that were not specifically requested will not be returned, but will be + * stored in the cache. Note that multiple threads can concurrently load values for distinct keys. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @param keys the keys whose associated values are to be returned + * @param mappingFunction the function to asynchronously compute the values + * @return the future containing an unmodifiable mapping of keys to values for the specified keys + * in this cache + * @throws NullPointerException if the specified collection is null or contains a null element, or + * if the future returned by the mappingFunction is null + * @throws RuntimeException or Error if the mappingFunction does so, in which case the mapping is + * left unestablished + */ + default CompletableFuture> getAll( + Iterable keys, Function, Map> mappingFunction) { + // This method was added & implemented in version 2.8.0 + throw new UnsupportedOperationException(); + } + + /** + * Returns the future of a map of the values associated with {@code keys}, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries; it will never contain null keys or values. If the any of the + * asynchronous computations fail, those entries will be automatically removed from this cache. + * + *

A single request to the {@code mappingFunction} is performed for all keys which are not + * already present in the cache. If another call to {@link #get} tries to load the value for a key + * in {@code keys}, that thread retrieves a future that is completed by this bulk computation. Any + * loaded values for keys that were not specifically requested will not be returned, but will be + * stored in the cache. Note that multiple threads can concurrently load values for distinct keys. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @param keys the keys whose associated values are to be returned + * @param mappingFunction the function to asynchronously compute the values + * @return the future containing an unmodifiable mapping of keys to values for the specified keys + * in this cache + * @throws NullPointerException if the specified collection is null or contains a null element, or + * if the future returned by the mappingFunction is null + * @throws RuntimeException or Error if the mappingFunction does so, in which case the mapping is + * left unestablished + */ + default CompletableFuture> getAll( + Iterable keys, + BiFunction, Executor, CompletableFuture>> mappingFunction) { + // This method was added & implemented in version 2.8.0 + throw new UnsupportedOperationException(); + } + + /** + * Associates {@code value} with {@code key} in this cache. If the cache previously contained a + * value associated with {@code key}, the old value is replaced by {@code value}. If the + * asynchronous computation fails, the entry will be automatically removed. + * + *

Prefer {@link #get(Object, Function)} when using the conventional "if cached, return; + * otherwise create, cache and return" pattern. + * + * @param key key with which the specified value is to be associated + * @param valueFuture value to be associated with the specified key + * @throws NullPointerException if the specified key or value is null + */ + void put(K key, CompletableFuture valueFuture); + + /** + * Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to + * the map directly affect the cache. + * + *

A computation operation, such as {@link ConcurrentMap#compute}, performs the entire method + * invocation atomically, so the function is applied at most once per key. Some attempted update + * operations by other threads may be blocked while computation is in progress. The computation + * must not attempt to update any other mappings of this cache. + * + *

Iterators from the returned map are at least weakly consistent: they are safe for + * concurrent use, but if the cache is modified (including by eviction) after the iterator is + * created, it is undefined which of the changes (if any) will be reflected in that iterator. + * + * @return a thread-safe view of this cache supporting all of the optional {@link Map} operations + */ + ConcurrentMap> asMap(); + + /** + * Returns a view of the entries stored in this cache as a synchronous {@link Cache}. A mapping is + * not present if the value is currently being loaded. Modifications made to the synchronous cache + * directly affect the asynchronous cache. If a modification is made to a mapping that is + * currently loading, the operation blocks until the computation completes. + * + * @return a thread-safe synchronous view of this cache + */ + Cache synchronous(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncCacheLoader.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncCacheLoader.java new file mode 100644 index 0000000000000..457291be843f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncCacheLoader.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * Computes or retrieves values asynchronously, based on a key, for use in populating a {@link + * AsyncLoadingCache}. + * + *

Most implementations will only need to implement {@link #asyncLoad}. Other methods may be + * overridden as desired. + * + *

Usage example: + * + *

{@code
+ * AsyncCacheLoader loader = (key, executor) ->
+ *     createExpensiveGraphAsync(key, executor);
+ * AsyncLoadingCache cache = Caffeine.newBuilder().buildAsync(loader);
+ * }
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@FunctionalInterface +public interface AsyncCacheLoader { + + /** + * Asynchronously computes or retrieves the value corresponding to {@code key}. + * + * @param key the non-null key whose value should be loaded + * @param executor the executor with which the entry is asynchronously loaded + * @return the future value associated with {@code key} + */ + CompletableFuture asyncLoad(K key, Executor executor); + + /** + * Asynchronously computes or retrieves the values corresponding to {@code keys}. This method is + * called by {@link AsyncLoadingCache#getAll}. + * + *

If the returned map doesn't contain all requested {@code keys} then the entries it does + * contain will be cached and {@code getAll} will return the partial results. If the returned map + * contains extra keys not present in {@code keys} then all returned entries will be cached, but + * only the entries for {@code keys} will be returned from {@code getAll}. + * + *

This method should be overridden when bulk retrieval is significantly more efficient than + * many individual lookups. Note that {@link AsyncLoadingCache#getAll} will defer to individual + * calls to {@link AsyncLoadingCache#get} if this method is not overridden. + * + * @param keys the unique, non-null keys whose values should be loaded + * @param executor the executor with which the entries are asynchronously loaded + * @return a future containing the map from each key in {@code keys} to the value associated with + * that key; may not contain null values + */ + default CompletableFuture> asyncLoadAll(Iterable keys, Executor executor) { + throw new UnsupportedOperationException(); + } + + /** + * Asynchronously computes or retrieves a replacement value corresponding to an already-cached + * {@code key}. If the replacement value is not found then the mapping will be removed if {@code + * null} is computed. This method is called when an existing cache entry is refreshed by {@link + * Caffeine#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}. + * + *

Note: all exceptions thrown by this method will be logged and then swallowed. + * + * @param key the non-null key whose value should be loaded + * @param oldValue the non-null old value corresponding to {@code key} + * @param executor the executor with which the entry is asynchronously loaded + * @return a future containing the new value associated with {@code key}, or containing {@code + * null} if the mapping is to be removed + */ + default CompletableFuture asyncReload(K key, V oldValue, Executor executor) { + return asyncLoad(key, executor); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncLoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncLoadingCache.java new file mode 100644 index 0000000000000..e4594e21d5f6c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/AsyncLoadingCache.java @@ -0,0 +1,111 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentMap; + +/** + * A semi-persistent mapping from keys to values. Values are automatically loaded by the cache + * asynchronously, and are stored in the cache until either evicted or manually invalidated. + * + *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of keys maintained by this cache + * @param the type of mapped values + */ +public interface AsyncLoadingCache extends AsyncCache { + + /** + * Returns the future associated with {@code key} in this cache, obtaining that value from {@link + * AsyncCacheLoader#asyncLoad} if necessary. If the asynchronous computation fails, the entry will + * be automatically removed from this cache. + * + *

If the specified key is not already associated with a value, attempts to compute its value + * asynchronously and enters it into this cache unless {@code null}. The entire method invocation + * is performed atomically, so the function is applied at most once per key. + * + * @param key key with which the specified value is to be associated + * @return the current (existing or computed) future value associated with the specified key + * @throws NullPointerException if the specified key is null or if the future returned by the + * {@link AsyncCacheLoader} is null + * @throws RuntimeException or Error if the {@link AsyncCacheLoader} does when constructing the + * future, in which case the mapping is left unestablished + */ + CompletableFuture get(K key); + + /** + * Returns the future of a map of the values associated with {@code keys}, creating or retrieving + * those values if necessary. The returned map contains entries that were already cached, combined + * with newly loaded entries; it will never contain null keys or values. If the any of the + * asynchronous computations fail, those entries will be automatically removed from this cache. + * + *

Caches loaded by a {@link AsyncCacheLoader} supporting bulk loading will issue a single + * request to {@link AsyncCacheLoader#asyncLoadAll} for all keys which are not already present in + * the cache. If another call to {@link #get} tries to load the value for a key in {@code keys}, + * that thread retrieves a future that is completed by this bulk computation. Caches that do not + * use a {@link AsyncCacheLoader} with an optimized bulk load implementation will sequentially + * load each key by making individual {@link AsyncCacheLoader#asyncLoad} calls. Note that multiple + * threads can concurrently load values for distinct keys. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @param keys the keys whose associated values are to be returned + * @return the future containing an unmodifiable mapping of keys to values for the specified keys + * in this cache + * @throws NullPointerException if the specified collection is null or contains a null element, or + * if the future returned by the {@link AsyncCacheLoader} is null + * @throws RuntimeException or Error if the {@link AsyncCacheLoader} does so, if {@link + * AsyncCacheLoader#asyncLoadAll} returns {@code null}, or fails when constructing the future, + * in which case the mapping is left unestablished + */ + CompletableFuture> getAll(Iterable keys); + + /** + * Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to + * the map directly affect the cache. + * + *

Iterators from the returned map are at least weakly consistent: they are safe for + * concurrent use, but if the cache is modified (including by eviction) after the iterator is + * created, it is undefined which of the changes (if any) will be reflected in that iterator. + * + * @return a thread-safe view of this cache supporting all of the optional {@link Map} operations + */ + @Override + default ConcurrentMap> asMap() { + // This method was added & implemented in version 2.7.0 + throw new UnsupportedOperationException(); + } + + /** + * Returns a view of the entries stored in this cache as a synchronous {@link LoadingCache}. A + * mapping is not present if the value is currently being loaded. Modifications made to the + * synchronous cache directly affect the asynchronous cache. If a modification is made to a + * mapping that is currently loading, the operation blocks until the computation completes. + * + * @return a thread-safe synchronous view of this cache + */ + @Override + LoadingCache synchronous(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/BoundedBuffer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/BoundedBuffer.java new file mode 100644 index 0000000000000..09202b93daa60 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/BoundedBuffer.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.Consumer; + +/** + * A striped, non-blocking, bounded buffer. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of elements maintained by this buffer + */ +final class BoundedBuffer extends StripedBuffer { + /* + * A circular ring buffer stores the elements being transferred by the producers to the consumer. + * The monotonically increasing count of reads and writes allow indexing sequentially to the next + * element location based upon a power-of-two sizing. + * + * The producers race to read the counts, check if there is available capacity, and if so then try + * once to CAS to the next write count. If the increment is successful then the producer lazily + * publishes the element. The producer does not retry or block when unsuccessful due to a failed + * CAS or the buffer being full. + * + * The consumer reads the counts and takes the available elements. The clearing of the elements + * and the next read count are lazily set. + * + * This implementation is striped to further increase concurrency by rehashing and dynamically + * adding new buffers when contention is detected, up to an internal maximum. When rehashing in + * order to discover an available buffer, the producer may retry adding its element to determine + * whether it found a satisfactory buffer or if resizing is necessary. + */ + + /** The maximum number of elements per buffer. */ + static final int BUFFER_SIZE = 16; + + static final int MASK = BUFFER_SIZE - 1; + + @Override + protected Buffer create(E e) { + return new RingBuffer<>(e); + } + + static final class RingBuffer extends BBHeader.ReadAndWriteCounterRef implements Buffer { + final AtomicReferenceArray buffer; + + @SuppressWarnings({"unchecked", "cast", "rawtypes"}) + public RingBuffer(E e) { + buffer = new AtomicReferenceArray<>(BUFFER_SIZE); + buffer.lazySet(0, e); + } + + @Override + public int offer(E e) { + long head = readCounter; + long tail = relaxedWriteCounter(); + long size = (tail - head); + if (size >= BUFFER_SIZE) { + return Buffer.FULL; + } + if (casWriteCounter(tail, tail + 1)) { + int index = (int) (tail & MASK); + buffer.lazySet(index, e); + return Buffer.SUCCESS; + } + return Buffer.FAILED; + } + + @Override + public void drainTo(Consumer consumer) { + long head = readCounter; + long tail = relaxedWriteCounter(); + long size = (tail - head); + if (size == 0) { + return; + } + do { + int index = (int) (head & MASK); + E e = buffer.get(index); + if (e == null) { + // not published yet + break; + } + buffer.lazySet(index, null); + consumer.accept(e); + head++; + } while (head != tail); + lazySetReadCounter(head); + } + + @Override + public int reads() { + return (int) readCounter; + } + + @Override + public int writes() { + return (int) writeCounter; + } + } +} + +/** The namespace for field padding through inheritance. */ +final class BBHeader { + + @SuppressWarnings("PMD.AbstractClassWithoutAbstractMethod") + abstract static class PadReadCounter { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; + } + + /** Enforces a memory layout to avoid false sharing by padding the read count. */ + abstract static class ReadCounterRef extends PadReadCounter { + static final long READ_OFFSET = + UnsafeAccess.objectFieldOffset(ReadCounterRef.class, "readCounter"); + + volatile long readCounter; + + void lazySetReadCounter(long count) { + UnsafeAccess.UNSAFE.putOrderedLong(this, READ_OFFSET, count); + } + } + + abstract static class PadWriteCounter extends ReadCounterRef { + byte p120, p121, p122, p123, p124, p125, p126, p127; + byte p128, p129, p130, p131, p132, p133, p134, p135; + byte p136, p137, p138, p139, p140, p141, p142, p143; + byte p144, p145, p146, p147, p148, p149, p150, p151; + byte p152, p153, p154, p155, p156, p157, p158, p159; + byte p160, p161, p162, p163, p164, p165, p166, p167; + byte p168, p169, p170, p171, p172, p173, p174, p175; + byte p176, p177, p178, p179, p180, p181, p182, p183; + byte p184, p185, p186, p187, p188, p189, p190, p191; + byte p192, p193, p194, p195, p196, p197, p198, p199; + byte p200, p201, p202, p203, p204, p205, p206, p207; + byte p208, p209, p210, p211, p212, p213, p214, p215; + byte p216, p217, p218, p219, p220, p221, p222, p223; + byte p224, p225, p226, p227, p228, p229, p230, p231; + byte p232, p233, p234, p235, p236, p237, p238, p239; + } + + /** Enforces a memory layout to avoid false sharing by padding the write count. */ + abstract static class ReadAndWriteCounterRef extends PadWriteCounter { + static final long WRITE_OFFSET = + UnsafeAccess.objectFieldOffset(ReadAndWriteCounterRef.class, "writeCounter"); + + volatile long writeCounter; + + ReadAndWriteCounterRef() { + UnsafeAccess.UNSAFE.putOrderedLong(this, WRITE_OFFSET, 1); + } + + long relaxedWriteCounter() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_OFFSET); + } + + boolean casWriteCounter(long expect, long update) { + return UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_OFFSET, expect, update); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/BoundedLocalCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/BoundedLocalCache.java new file mode 100644 index 0000000000000..69846f976f6b5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/BoundedLocalCache.java @@ -0,0 +1,4178 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Async.AsyncExpiry; +import org.apache.iotdb.github.benmanes.caffeine.cache.LinkedDeque.PeekingIterator; +import org.apache.iotdb.github.benmanes.caffeine.cache.References.InternalReference; +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.Set; +import java.util.Spliterator; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Async.ASYNC_EXPIRY; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.ceilingPowerOfTwo; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.requireArgument; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.requireState; +import static org.apache.iotdb.github.benmanes.caffeine.cache.LocalLoadingCache.newBulkMappingFunction; +import static org.apache.iotdb.github.benmanes.caffeine.cache.LocalLoadingCache.newMappingFunction; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Node.PROBATION; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Node.PROTECTED; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Node.WINDOW; + +/** + * An in-memory cache implementation that supports full concurrency of retrievals, a high expected + * concurrency for updates, and multiple ways to bound the cache. + * + *

This class is abstract and code generated subclasses provide the complete implementation for a + * particular configuration. This is to ensure that only the fields and execution paths necessary + * for a given configuration are used. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of keys maintained by this cache + * @param the type of mapped values + */ +@SuppressWarnings("deprecation") +abstract class BoundedLocalCache extends BLCHeader.DrainStatusRef + implements LocalCache { + + /* + * This class performs a best-effort bounding of a ConcurrentHashMap using a page-replacement + * algorithm to determine which entries to evict when the capacity is exceeded. + * + * Concurrency: + * ------------ + * The page replacement algorithms are kept eventually consistent with the map. An update to the + * map and recording of reads may not be immediately reflected on the policy's data structures. + * These structures are guarded by a lock and operations are applied in batches to avoid lock + * contention. The penalty of applying the batches is spread across threads so that the amortized + * cost is slightly higher than performing just the ConcurrentHashMap operation [1]. + * + * A memento of the reads and writes that were performed on the map are recorded in buffers. These + * buffers are drained at the first opportunity after a write or when a read buffer is full. The + * reads are offered to a buffer that will reject additions if contended on or if it is full. Due + * to the concurrent nature of the read and write operations a strict policy ordering is not + * possible, but may be observably strict when single threaded. The buffers are drained + * asynchronously to minimize the request latency and uses a state machine to determine when to + * schedule this work on an executor. + * + * Due to a lack of a strict ordering guarantee, a task can be executed out-of-order, such as a + * removal followed by its addition. The state of the entry is encoded using the key field to + * avoid additional memory. An entry is "alive" if it is in both the hash table and the page + * replacement policy. It is "retired" if it is not in the hash table and is pending removal from + * the page replacement policy. Finally an entry transitions to the "dead" state when it is not in + * the hash table nor the page replacement policy. Both the retired and dead states are + * represented by a sentinel key that should not be used for map operations. + * + * Eviction: + * --------- + * Maximum size is implemented using the Window TinyLfu policy [2] due to its high hit rate, O(1) + * time complexity, and small footprint. A new entry starts in the admission window and remains + * there as long as it has high temporal locality (recency). Eventually an entry will slip from + * the window into the main space. If the main space is already full, then a historic frequency + * filter determines whether to evict the newly admitted entry or the victim entry chosen by the + * eviction policy. This process ensures that the entries in the window were very recently used + * and entries in the main space are accessed very frequently and are moderately recent. The + * windowing allows the policy to have a high hit rate when entries exhibit bursty access pattern + * while the filter ensures that popular items are retained. The admission window uses LRU and + * the main space uses Segmented LRU. + * + * The optimal size of the window vs main spaces is workload dependent [3]. A large admission + * window is favored by recency-biased workloads while a small one favors frequency-biased + * workloads. When the window is too small then recent arrivals are prematurely evicted, but when + * too large then they pollute the cache and force the eviction of more popular entries. The + * configuration is dynamically determined using hill climbing to walk the hit rate curve. This is + * done by sampling the hit rate and adjusting the window size in the direction that is improving + * (making positive or negative steps). At each interval the step size is decreased until the + * climber converges at the optimal setting. The process is restarted when the hit rate changes + * over a threshold, indicating that the workload altered and a new setting may be required. + * + * The historic usage is retained in a compact popularity sketch, which uses hashing to + * probabilistically estimate an item's frequency. This exposes a flaw where an adversary could + * use hash flooding [4] to artificially raise the frequency of the main space's victim and cause + * all candidates to be rejected. In the worst case, by exploiting hash collisions an attacker + * could cause the cache to never hit and hold only worthless items, resulting in a + * denial-of-service attack against the underlying resource. This is protected against by + * introducing jitter so that candidates which are at least moderately popular have a small, + * random chance of being admitted. This causes the victim to be evicted, but in a way that + * marginally impacts the hit rate. + * + * Expiration: + * ----------- + * Expiration is implemented in O(1) time complexity. The time-to-idle policy uses an access-order + * queue, the time-to-live policy uses a write-order queue, and variable expiration uses a + * hierarchical timer wheel [5]. The queuing policies allow for peeking at the oldest entry to + * see if it has expired and, if it has not, then the younger entries must not have too. If a + * maximum size is set then expiration will share the queues so that the per-entry footprint is + * minimized. The timer wheel based policy uses hashing and cascading in a manner that amortizes + * the penalty of sorting to achieve a similar algorithmic cost. + * + * The expiration updates are applied in a best effort fashion. The reordering of variable or + * access-order expiration may be discarded by the read buffer if full or contended on. Similarly + * the reordering of write expiration may be ignored for an entry if the last update was within a + * short time window in order to avoid overwhelming the write buffer. + * + * [1] BP-Wrapper: A Framework Making Any Replacement Algorithms (Almost) Lock Contention Free + * http://web.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-09-1.pdf + * [2] TinyLFU: A Highly Efficient Cache Admission Policy + * https://dl.acm.org/citation.cfm?id=3149371 + * [3] Adaptive Software Cache Management + * https://dl.acm.org/citation.cfm?id=3274816 + * [4] Denial of Service via Algorithmic Complexity Attack + * https://www.usenix.org/legacy/events/sec03/tech/full_papers/crosby/crosby.pdf + * [5] Hashed and Hierarchical Timing Wheels + * http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf + */ + + static final Logger logger = Logger.getLogger(BoundedLocalCache.class.getName()); + + /** The number of CPUs */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** The initial capacity of the write buffer. */ + static final int WRITE_BUFFER_MIN = 4; + + /** The maximum capacity of the write buffer. */ + static final int WRITE_BUFFER_MAX = 128 * ceilingPowerOfTwo(NCPU); + + /** The number of attempts to insert into the write buffer before yielding. */ + static final int WRITE_BUFFER_RETRIES = 100; + + /** The maximum weighted capacity of the map. */ + static final long MAXIMUM_CAPACITY = Long.MAX_VALUE - Integer.MAX_VALUE; + + /** The initial percent of the maximum weighted capacity dedicated to the main space. */ + static final double PERCENT_MAIN = 0.99d; + + /** The percent of the maximum weighted capacity dedicated to the main's protected space. */ + static final double PERCENT_MAIN_PROTECTED = 0.80d; + + /** The difference in hit rates that restarts the climber. */ + static final double HILL_CLIMBER_RESTART_THRESHOLD = 0.05d; + + /** The percent of the total size to adapt the window by. */ + static final double HILL_CLIMBER_STEP_PERCENT = 0.0625d; + + /** The rate to decrease the step size to adapt by. */ + static final double HILL_CLIMBER_STEP_DECAY_RATE = 0.98d; + + /** The maximum number of entries that can be transfered between queues. */ + static final int QUEUE_TRANSFER_THRESHOLD = 1_000; + + /** The maximum time window between entry updates before the expiration must be reordered. */ + static final long EXPIRE_WRITE_TOLERANCE = TimeUnit.SECONDS.toNanos(1); + + /** The maximum duration before an entry expires. */ + static final long MAXIMUM_EXPIRY = (Long.MAX_VALUE >> 1); // 150 years + + final MpscGrowableArrayQueue writeBuffer; + final ConcurrentHashMap> data; + final CacheLoader cacheLoader; + final PerformCleanupTask drainBuffersTask; + final Consumer> accessPolicy; + final Buffer> readBuffer; + final NodeFactory nodeFactory; + final ReentrantLock evictionLock; + final CacheWriter writer; + final Weigher weigher; + final Executor executor; + final boolean isAsync; + + // The collection views + transient Set keySet; + transient Collection values; + transient Set> entrySet; + + /** Creates an instance based on the builder's configuration. */ + protected BoundedLocalCache( + Caffeine builder, CacheLoader cacheLoader, boolean isAsync) { + this.isAsync = isAsync; + this.cacheLoader = cacheLoader; + executor = builder.getExecutor(); + evictionLock = new ReentrantLock(); + weigher = builder.getWeigher(isAsync); + writer = builder.getCacheWriter(isAsync); + drainBuffersTask = new PerformCleanupTask(this); + nodeFactory = NodeFactory.newFactory(builder, isAsync); + data = new ConcurrentHashMap<>(builder.getInitialCapacity()); + readBuffer = + evicts() || collectKeys() || collectValues() || expiresAfterAccess() + ? new BoundedBuffer<>() + : Buffer.disabled(); + accessPolicy = (evicts() || expiresAfterAccess()) ? this::onAccess : e -> {}; + writeBuffer = new MpscGrowableArrayQueue<>(WRITE_BUFFER_MIN, WRITE_BUFFER_MAX); + + if (evicts()) { + setMaximumSize(builder.getMaximum()); + } + } + + /* --------------- Shared --------------- */ + + /** Returns if the node's value is currently being computed, asynchronously. */ + final boolean isComputingAsync(Node node) { + return isAsync && !Async.isReady((CompletableFuture) node.getValue()); + } + + protected AccessOrderDeque> accessOrderWindowDeque() { + throw new UnsupportedOperationException(); + } + + protected AccessOrderDeque> accessOrderProbationDeque() { + throw new UnsupportedOperationException(); + } + + protected AccessOrderDeque> accessOrderProtectedDeque() { + throw new UnsupportedOperationException(); + } + + protected WriteOrderDeque> writeOrderDeque() { + throw new UnsupportedOperationException(); + } + + @Override + public final Executor executor() { + return executor; + } + + /** Returns whether this cache notifies a writer when an entry is modified. */ + protected boolean hasWriter() { + return (writer != CacheWriter.disabledWriter()); + } + + /* --------------- Stats Support --------------- */ + + @Override + public boolean isRecordingStats() { + return false; + } + + @Override + public StatsCounter statsCounter() { + return StatsCounter.disabledStatsCounter(); + } + + @Override + public Ticker statsTicker() { + return Ticker.disabledTicker(); + } + + /* --------------- Removal Listener Support --------------- */ + + @Override + @SuppressWarnings("NullAway") + public RemovalListener removalListener() { + return null; + } + + @Override + public boolean hasRemovalListener() { + return false; + } + + @Override + public void notifyRemoval(K key, V value, RemovalCause cause) { + requireState(hasRemovalListener(), "Notification should be guarded with a check"); + Runnable task = + () -> { + try { + removalListener().onRemoval(key, value, cause); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by removal listener", t); + } + }; + try { + executor.execute(task); + } catch (Throwable t) { + logger.log(Level.SEVERE, "Exception thrown when submitting removal listener", t); + task.run(); + } + } + + /* --------------- Reference Support --------------- */ + + /** Returns if the keys are weak reference garbage collected. */ + protected boolean collectKeys() { + return false; + } + + /** Returns if the values are weak or soft reference garbage collected. */ + protected boolean collectValues() { + return false; + } + + @SuppressWarnings("NullAway") + protected ReferenceQueue keyReferenceQueue() { + return null; + } + + @SuppressWarnings("NullAway") + protected ReferenceQueue valueReferenceQueue() { + return null; + } + + /* --------------- Expiration Support --------------- */ + + /** Returns the {@link Pacer} used to schedule the maintenance task. */ + protected Pacer pacer() { + return null; + } + + /** Returns if the cache expires entries after a variable time threshold. */ + protected boolean expiresVariable() { + return false; + } + + /** Returns if the cache expires entries after an access time threshold. */ + protected boolean expiresAfterAccess() { + return false; + } + + /** Returns how long after the last access to an entry the map will retain that entry. */ + protected long expiresAfterAccessNanos() { + throw new UnsupportedOperationException(); + } + + protected void setExpiresAfterAccessNanos(long expireAfterAccessNanos) { + throw new UnsupportedOperationException(); + } + + /** Returns if the cache expires entries after an write time threshold. */ + protected boolean expiresAfterWrite() { + return false; + } + + /** Returns how long after the last write to an entry the map will retain that entry. */ + protected long expiresAfterWriteNanos() { + throw new UnsupportedOperationException(); + } + + protected void setExpiresAfterWriteNanos(long expireAfterWriteNanos) { + throw new UnsupportedOperationException(); + } + + /** Returns if the cache refreshes entries after an write time threshold. */ + protected boolean refreshAfterWrite() { + return false; + } + + /** Returns how long after the last write an entry becomes a candidate for refresh. */ + protected long refreshAfterWriteNanos() { + throw new UnsupportedOperationException(); + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasWriteTime() { + return expiresAfterWrite() || refreshAfterWrite(); + } + + @SuppressWarnings("NullAway") + protected Expiry expiry() { + return null; + } + + @Override + public Ticker expirationTicker() { + return Ticker.disabledTicker(); + } + + protected TimerWheel timerWheel() { + throw new UnsupportedOperationException(); + } + + /* --------------- Eviction Support --------------- */ + + /** Returns if the cache evicts entries due to a maximum size or weight threshold. */ + protected boolean evicts() { + return false; + } + + /** Returns if entries may be assigned different weights. */ + protected boolean isWeighted() { + return (weigher != Weigher.singletonWeigher()); + } + + protected FrequencySketch frequencySketch() { + throw new UnsupportedOperationException(); + } + + /** Returns if an access to an entry can skip notifying the eviction policy. */ + protected boolean fastpath() { + return false; + } + + /** Returns the maximum weighted size. */ + protected long maximum() { + throw new UnsupportedOperationException(); + } + + /** Returns the maximum weighted size of the window space. */ + protected long windowMaximum() { + throw new UnsupportedOperationException(); + } + + /** Returns the maximum weighted size of the main's protected space. */ + protected long mainProtectedMaximum() { + throw new UnsupportedOperationException(); + } + + protected void setMaximum(long maximum) { + throw new UnsupportedOperationException(); + } + + protected void setWindowMaximum(long maximum) { + throw new UnsupportedOperationException(); + } + + protected void setMainProtectedMaximum(long maximum) { + throw new UnsupportedOperationException(); + } + + /** Returns the combined weight of the values in the cache (may be negative). */ + protected long weightedSize() { + throw new UnsupportedOperationException(); + } + + /** Returns the uncorrected combined weight of the values in the window space. */ + protected long windowWeightedSize() { + throw new UnsupportedOperationException(); + } + + /** Returns the uncorrected combined weight of the values in the main's protected space. */ + protected long mainProtectedWeightedSize() { + throw new UnsupportedOperationException(); + } + + protected void setWeightedSize(long weightedSize) { + throw new UnsupportedOperationException(); + } + + protected void setWindowWeightedSize(long weightedSize) { + throw new UnsupportedOperationException(); + } + + protected void setMainProtectedWeightedSize(long weightedSize) { + throw new UnsupportedOperationException(); + } + + protected int hitsInSample() { + throw new UnsupportedOperationException(); + } + + protected int missesInSample() { + throw new UnsupportedOperationException(); + } + + protected int sampleCount() { + throw new UnsupportedOperationException(); + } + + protected double stepSize() { + throw new UnsupportedOperationException(); + } + + protected double previousSampleHitRate() { + throw new UnsupportedOperationException(); + } + + protected long adjustment() { + throw new UnsupportedOperationException(); + } + + protected void setHitsInSample(int hitCount) { + throw new UnsupportedOperationException(); + } + + protected void setMissesInSample(int missCount) { + throw new UnsupportedOperationException(); + } + + protected void setSampleCount(int sampleCount) { + throw new UnsupportedOperationException(); + } + + protected void setStepSize(double stepSize) { + throw new UnsupportedOperationException(); + } + + protected void setPreviousSampleHitRate(double hitRate) { + throw new UnsupportedOperationException(); + } + + protected void setAdjustment(long amount) { + throw new UnsupportedOperationException(); + } + + /** + * Sets the maximum weighted size of the cache. The caller may need to perform a maintenance cycle + * to eagerly evicts entries until the cache shrinks to the appropriate size. + */ + void setMaximumSize(long maximum) { + requireArgument(maximum >= 0, "maximum must not be negative"); + if (maximum == maximum()) { + return; + } + + long max = Math.min(maximum, MAXIMUM_CAPACITY); + long window = max - (long) (PERCENT_MAIN * max); + long mainProtected = (long) (PERCENT_MAIN_PROTECTED * (max - window)); + + setMaximum(max); + setWindowMaximum(window); + setMainProtectedMaximum(mainProtected); + + setHitsInSample(0); + setMissesInSample(0); + setStepSize(-HILL_CLIMBER_STEP_PERCENT * max); + + if ((frequencySketch() != null) && !isWeighted() && (weightedSize() >= (max >>> 1))) { + // Lazily initialize when close to the maximum size + frequencySketch().ensureCapacity(max); + } + } + + /** Evicts entries if the cache exceeds the maximum. */ + void evictEntries() { + if (!evicts()) { + return; + } + int candidates = evictFromWindow(); + evictFromMain(candidates); + } + + /** + * Evicts entries from the window space into the main space while the window size exceeds a + * maximum. + * + * @return the number of candidate entries evicted from the window space + */ + int evictFromWindow() { + int candidates = 0; + Node node = accessOrderWindowDeque().peek(); + while (windowWeightedSize() > windowMaximum()) { + // The pending operations will adjust the size to reflect the correct weight + if (node == null) { + break; + } + + Node next = node.getNextInAccessOrder(); + if (node.getPolicyWeight() != 0) { + node.makeMainProbation(); + accessOrderWindowDeque().remove(node); + accessOrderProbationDeque().add(node); + candidates++; + + setWindowWeightedSize(windowWeightedSize() - node.getPolicyWeight()); + } + node = next; + } + + return candidates; + } + + /** + * Evicts entries from the main space if the cache exceeds the maximum capacity. The main space + * determines whether admitting an entry (coming from the window space) is preferable to retaining + * the eviction policy's victim. This decision is made using a frequency filter so that the least + * frequently used entry is removed. + * + *

The window space's candidates were previously placed in the MRU position and the eviction + * policy's victim is at the LRU position. The two ends of the queue are evaluated while an + * eviction is required. The number of remaining candidates is provided and decremented on + * eviction, so that when there are no more candidates the victim is evicted. + * + * @param candidates the number of candidate entries evicted from the window space + */ + void evictFromMain(int candidates) { + int victimQueue = PROBATION; + Node victim = accessOrderProbationDeque().peekFirst(); + Node candidate = accessOrderProbationDeque().peekLast(); + while (weightedSize() > maximum()) { + // Search the admission window for additional candidates + if (candidates == 0) { + candidate = accessOrderWindowDeque().peekLast(); + } + + // Try evicting from the protected and window queues + if ((candidate == null) && (victim == null)) { + if (victimQueue == PROBATION) { + victim = accessOrderProtectedDeque().peekFirst(); + victimQueue = PROTECTED; + continue; + } else if (victimQueue == PROTECTED) { + victim = accessOrderWindowDeque().peekFirst(); + victimQueue = WINDOW; + continue; + } + + // The pending operations will adjust the size to reflect the correct weight + break; + } + + // Skip over entries with zero weight + if ((victim != null) && (victim.getPolicyWeight() == 0)) { + victim = victim.getNextInAccessOrder(); + continue; + } else if ((candidate != null) && (candidate.getPolicyWeight() == 0)) { + candidate = + (candidates > 0) + ? candidate.getPreviousInAccessOrder() + : candidate.getNextInAccessOrder(); + candidates--; + continue; + } + + // Evict immediately if only one of the entries is present + if (victim == null) { + @SuppressWarnings("NullAway") + Node previous = candidate.getPreviousInAccessOrder(); + Node evict = candidate; + candidate = previous; + candidates--; + evictEntry(evict, RemovalCause.SIZE, 0L); + continue; + } else if (candidate == null) { + Node evict = victim; + victim = victim.getNextInAccessOrder(); + evictEntry(evict, RemovalCause.SIZE, 0L); + continue; + } + + // Evict immediately if an entry was collected + K victimKey = victim.getKey(); + K candidateKey = candidate.getKey(); + if (victimKey == null) { + Node evict = victim; + victim = victim.getNextInAccessOrder(); + evictEntry(evict, RemovalCause.COLLECTED, 0L); + continue; + } else if (candidateKey == null) { + Node evict = candidate; + candidate = + (candidates > 0) + ? candidate.getPreviousInAccessOrder() + : candidate.getNextInAccessOrder(); + candidates--; + evictEntry(evict, RemovalCause.COLLECTED, 0L); + continue; + } + + // Evict immediately if the candidate's weight exceeds the maximum + if (candidate.getPolicyWeight() > maximum()) { + Node evict = candidate; + candidate = + (candidates > 0) + ? candidate.getPreviousInAccessOrder() + : candidate.getNextInAccessOrder(); + candidates--; + evictEntry(evict, RemovalCause.SIZE, 0L); + continue; + } + + // Evict the entry with the lowest frequency + candidates--; + if (admit(candidateKey, victimKey)) { + Node evict = victim; + victim = victim.getNextInAccessOrder(); + evictEntry(evict, RemovalCause.SIZE, 0L); + candidate = candidate.getPreviousInAccessOrder(); + } else { + Node evict = candidate; + candidate = + (candidates > 0) + ? candidate.getPreviousInAccessOrder() + : candidate.getNextInAccessOrder(); + evictEntry(evict, RemovalCause.SIZE, 0L); + } + } + } + + /** + * Determines if the candidate should be accepted into the main space, as determined by its + * frequency relative to the victim. A small amount of randomness is used to protect against hash + * collision attacks, where the victim's frequency is artificially raised so that no new entries + * are admitted. + * + * @param candidateKey the key for the entry being proposed for long term retention + * @param victimKey the key for the entry chosen by the eviction policy for replacement + * @return if the candidate should be admitted and the victim ejected + */ + boolean admit(K candidateKey, K victimKey) { + int victimFreq = frequencySketch().frequency(victimKey); + int candidateFreq = frequencySketch().frequency(candidateKey); + if (candidateFreq > victimFreq) { + return true; + } else if (candidateFreq <= 5) { + // The maximum frequency is 15 and halved to 7 after a reset to age the history. An attack + // exploits that a hot candidate is rejected in favor of a hot victim. The threshold of a warm + // candidate reduces the number of random acceptances to minimize the impact on the hit rate. + return false; + } + int random = ThreadLocalRandom.current().nextInt(); + return ((random & 127) == 0); + } + + /** Expires entries that have expired by access, write, or variable. */ + void expireEntries() { + long now = expirationTicker().read(); + expireAfterAccessEntries(now); + expireAfterWriteEntries(now); + expireVariableEntries(now); + + Pacer pacer = pacer(); + if (pacer != null) { + long delay = getExpirationDelay(now); + if (delay == Long.MAX_VALUE) { + pacer.cancel(); + } else { + pacer.schedule(executor, drainBuffersTask, now, delay); + } + } + } + + /** Expires entries in the access-order queue. */ + void expireAfterAccessEntries(long now) { + if (!expiresAfterAccess()) { + return; + } + + expireAfterAccessEntries(accessOrderWindowDeque(), now); + if (evicts()) { + expireAfterAccessEntries(accessOrderProbationDeque(), now); + expireAfterAccessEntries(accessOrderProtectedDeque(), now); + } + } + + /** Expires entries in an access-order queue. */ + void expireAfterAccessEntries(AccessOrderDeque> accessOrderDeque, long now) { + long duration = expiresAfterAccessNanos(); + for (; ; ) { + Node node = accessOrderDeque.peekFirst(); + if ((node == null) + || ((now - node.getAccessTime()) < duration) + || !evictEntry(node, RemovalCause.EXPIRED, now)) { + return; + } + } + } + + /** Expires entries on the write-order queue. */ + void expireAfterWriteEntries(long now) { + if (!expiresAfterWrite()) { + return; + } + long duration = expiresAfterWriteNanos(); + for (; ; ) { + Node node = writeOrderDeque().peekFirst(); + if ((node == null) + || ((now - node.getWriteTime()) < duration) + || !evictEntry(node, RemovalCause.EXPIRED, now)) { + break; + } + } + } + + /** Expires entries in the timer wheel. */ + void expireVariableEntries(long now) { + if (expiresVariable()) { + timerWheel().advance(now); + } + } + + /** Returns the duration until the next item expires, or {@link Long.MAX_VALUE} if none. */ + private long getExpirationDelay(long now) { + long delay = Long.MAX_VALUE; + if (expiresAfterAccess()) { + Node node = accessOrderWindowDeque().peekFirst(); + if (node != null) { + delay = Math.min(delay, expiresAfterAccessNanos() - (now - node.getAccessTime())); + } + if (evicts()) { + node = accessOrderProbationDeque().peekFirst(); + if (node != null) { + delay = Math.min(delay, expiresAfterAccessNanos() - (now - node.getAccessTime())); + } + node = accessOrderProtectedDeque().peekFirst(); + if (node != null) { + delay = Math.min(delay, expiresAfterAccessNanos() - (now - node.getAccessTime())); + } + } + } + if (expiresAfterWrite()) { + Node node = writeOrderDeque().peekFirst(); + if (node != null) { + delay = Math.min(delay, expiresAfterWriteNanos() - (now - node.getWriteTime())); + } + } + if (expiresVariable()) { + delay = Math.min(delay, timerWheel().getExpirationDelay()); + } + return delay; + } + + /** Returns if the entry has expired. */ + @SuppressWarnings("ShortCircuitBoolean") + boolean hasExpired(Node node, long now) { + if (isComputingAsync(node)) { + return false; + } + return (expiresAfterAccess() && (now - node.getAccessTime() >= expiresAfterAccessNanos())) + | (expiresAfterWrite() && (now - node.getWriteTime() >= expiresAfterWriteNanos())) + | (expiresVariable() && (now - node.getVariableTime() >= 0)); + } + + /** + * Attempts to evict the entry based on the given removal cause. A removal due to may be ignored + * if the entry was updated and is no longer eligible for eviction. + * + * @param node the entry to evict + * @param cause the reason to evict + * @param now the current time, used only if expiring + * @return if the entry was evicted + */ + @SuppressWarnings({"PMD.CollapsibleIfStatements", "GuardedByChecker", "NullAway"}) + boolean evictEntry(Node node, RemovalCause cause, long now) { + K key = node.getKey(); + @SuppressWarnings("unchecked") + V[] value = (V[]) new Object[1]; + boolean[] removed = new boolean[1]; + boolean[] resurrect = new boolean[1]; + RemovalCause[] actualCause = new RemovalCause[1]; + + data.computeIfPresent( + node.getKeyReference(), + (k, n) -> { + if (n != node) { + return n; + } + synchronized (n) { + value[0] = n.getValue(); + + if ((key == null) || (value[0] == null)) { + actualCause[0] = RemovalCause.COLLECTED; + } else if (cause == RemovalCause.COLLECTED) { + resurrect[0] = true; + return n; + } else { + actualCause[0] = cause; + } + + if (actualCause[0] == RemovalCause.EXPIRED) { + boolean expired = false; + if (expiresAfterAccess()) { + expired |= ((now - n.getAccessTime()) >= expiresAfterAccessNanos()); + } + if (expiresAfterWrite()) { + expired |= ((now - n.getWriteTime()) >= expiresAfterWriteNanos()); + } + if (expiresVariable()) { + expired |= (n.getVariableTime() <= now); + } + if (!expired) { + resurrect[0] = true; + return n; + } + } else if (actualCause[0] == RemovalCause.SIZE) { + int weight = node.getWeight(); + if (weight == 0) { + resurrect[0] = true; + return n; + } + } + + writer.delete(key, value[0], actualCause[0]); + makeDead(n); + } + removed[0] = true; + return null; + }); + + // The entry is no longer eligible for eviction + if (resurrect[0]) { + return false; + } + + // If the eviction fails due to a concurrent removal of the victim, that removal may cancel out + // the addition that triggered this eviction. The victim is eagerly unlinked before the removal + // task so that if an eviction is still required then a new victim will be chosen for removal. + if (node.inWindow() && (evicts() || expiresAfterAccess())) { + accessOrderWindowDeque().remove(node); + } else if (evicts()) { + if (node.inMainProbation()) { + accessOrderProbationDeque().remove(node); + } else { + accessOrderProtectedDeque().remove(node); + } + } + if (expiresAfterWrite()) { + writeOrderDeque().remove(node); + } else if (expiresVariable()) { + timerWheel().deschedule(node); + } + + if (removed[0]) { + statsCounter().recordEviction(node.getWeight(), actualCause[0]); + if (hasRemovalListener()) { + // Notify the listener only if the entry was evicted. This must be performed as the last + // step during eviction to safe guard against the executor rejecting the notification task. + notifyRemoval(key, value[0], actualCause[0]); + } + } else { + // Eagerly decrement the size to potentially avoid an additional eviction, rather than wait + // for the removal task to do it on the next maintenance cycle. + makeDead(node); + } + + return true; + } + + /** Adapts the eviction policy to towards the optimal recency / frequency configuration. */ + void climb() { + if (!evicts()) { + return; + } + + determineAdjustment(); + demoteFromMainProtected(); + long amount = adjustment(); + if (amount == 0) { + return; + } else if (amount > 0) { + increaseWindow(); + } else { + decreaseWindow(); + } + } + + /** Calculates the amount to adapt the window by and sets {@link #adjustment()} accordingly. */ + void determineAdjustment() { + if (frequencySketch().isNotInitialized()) { + setPreviousSampleHitRate(0.0); + setMissesInSample(0); + setHitsInSample(0); + return; + } + + int requestCount = hitsInSample() + missesInSample(); + if (requestCount < frequencySketch().sampleSize) { + return; + } + + double hitRate = (double) hitsInSample() / requestCount; + double hitRateChange = hitRate - previousSampleHitRate(); + double amount = (hitRateChange >= 0) ? stepSize() : -stepSize(); + double nextStepSize = + (Math.abs(hitRateChange) >= HILL_CLIMBER_RESTART_THRESHOLD) + ? HILL_CLIMBER_STEP_PERCENT * maximum() * (amount >= 0 ? 1 : -1) + : HILL_CLIMBER_STEP_DECAY_RATE * amount; + setPreviousSampleHitRate(hitRate); + setAdjustment((long) amount); + setStepSize(nextStepSize); + setMissesInSample(0); + setHitsInSample(0); + } + + /** + * Increases the size of the admission window by shrinking the portion allocated to the main + * space. As the main space is partitioned into probation and protected regions (80% / 20%), for + * simplicity only the protected is reduced. If the regions exceed their maximums, this may cause + * protected items to be demoted to the probation region and probation items to be demoted to the + * admission window. + */ + void increaseWindow() { + if (mainProtectedMaximum() == 0) { + return; + } + + long quota = Math.min(adjustment(), mainProtectedMaximum()); + setMainProtectedMaximum(mainProtectedMaximum() - quota); + setWindowMaximum(windowMaximum() + quota); + demoteFromMainProtected(); + + for (int i = 0; i < QUEUE_TRANSFER_THRESHOLD; i++) { + Node candidate = accessOrderProbationDeque().peek(); + boolean probation = true; + if ((candidate == null) || (quota < candidate.getPolicyWeight())) { + candidate = accessOrderProtectedDeque().peek(); + probation = false; + } + if (candidate == null) { + break; + } + + int weight = candidate.getPolicyWeight(); + if (quota < weight) { + break; + } + + quota -= weight; + if (probation) { + accessOrderProbationDeque().remove(candidate); + } else { + setMainProtectedWeightedSize(mainProtectedWeightedSize() - weight); + accessOrderProtectedDeque().remove(candidate); + } + setWindowWeightedSize(windowWeightedSize() + weight); + accessOrderWindowDeque().add(candidate); + candidate.makeWindow(); + } + + setMainProtectedMaximum(mainProtectedMaximum() + quota); + setWindowMaximum(windowMaximum() - quota); + setAdjustment(quota); + } + + /** Decreases the size of the admission window and increases the main's protected region. */ + void decreaseWindow() { + if (windowMaximum() <= 1) { + return; + } + + long quota = Math.min(-adjustment(), Math.max(0, windowMaximum() - 1)); + setMainProtectedMaximum(mainProtectedMaximum() + quota); + setWindowMaximum(windowMaximum() - quota); + + for (int i = 0; i < QUEUE_TRANSFER_THRESHOLD; i++) { + Node candidate = accessOrderWindowDeque().peek(); + if (candidate == null) { + break; + } + + int weight = candidate.getPolicyWeight(); + if (quota < weight) { + break; + } + + quota -= weight; + setWindowWeightedSize(windowWeightedSize() - weight); + accessOrderWindowDeque().remove(candidate); + accessOrderProbationDeque().add(candidate); + candidate.makeMainProbation(); + } + + setMainProtectedMaximum(mainProtectedMaximum() - quota); + setWindowMaximum(windowMaximum() + quota); + setAdjustment(-quota); + } + + /** Transfers the nodes from the protected to the probation region if it exceeds the maximum. */ + void demoteFromMainProtected() { + long mainProtectedMaximum = mainProtectedMaximum(); + long mainProtectedWeightedSize = mainProtectedWeightedSize(); + if (mainProtectedWeightedSize <= mainProtectedMaximum) { + return; + } + + for (int i = 0; i < QUEUE_TRANSFER_THRESHOLD; i++) { + if (mainProtectedWeightedSize <= mainProtectedMaximum) { + break; + } + + Node demoted = accessOrderProtectedDeque().poll(); + if (demoted == null) { + break; + } + demoted.makeMainProbation(); + accessOrderProbationDeque().add(demoted); + mainProtectedWeightedSize -= demoted.getPolicyWeight(); + } + setMainProtectedWeightedSize(mainProtectedWeightedSize); + } + + /** + * Performs the post-processing work required after a read. + * + * @param node the entry in the page replacement policy + * @param now the current time, in nanoseconds + * @param recordHit if the hit count should be incremented + */ + void afterRead(Node node, long now, boolean recordHit) { + if (recordHit) { + statsCounter().recordHits(1); + } + + boolean delayable = skipReadBuffer() || (readBuffer.offer(node) != Buffer.FULL); + if (shouldDrainBuffers(delayable)) { + scheduleDrainBuffers(); + } + refreshIfNeeded(node, now); + } + + /** Returns if the cache should bypass the read buffer. */ + boolean skipReadBuffer() { + return fastpath() && frequencySketch().isNotInitialized(); + } + + /** + * Asynchronously refreshes the entry if eligible. + * + * @param node the entry in the cache to refresh + * @param now the current time, in nanoseconds + */ + @SuppressWarnings("FutureReturnValueIgnored") + void refreshIfNeeded(Node node, long now) { + if (!refreshAfterWrite()) { + return; + } + K key; + V oldValue; + long oldWriteTime = node.getWriteTime(); + long refreshWriteTime = (now + ASYNC_EXPIRY); + if (((now - oldWriteTime) > refreshAfterWriteNanos()) + && ((key = node.getKey()) != null) + && ((oldValue = node.getValue()) != null) + && node.casWriteTime(oldWriteTime, refreshWriteTime)) { + try { + CompletableFuture refreshFuture; + long startTime = statsTicker().read(); + if (isAsync) { + @SuppressWarnings("unchecked") + CompletableFuture future = (CompletableFuture) oldValue; + if (Async.isReady(future)) { + @SuppressWarnings("NullAway") + CompletableFuture refresh = + future.thenCompose(value -> cacheLoader.asyncReload(key, value, executor)); + refreshFuture = refresh; + } else { + // no-op if load is pending + node.casWriteTime(refreshWriteTime, oldWriteTime); + return; + } + } else { + @SuppressWarnings("NullAway") + CompletableFuture refresh = cacheLoader.asyncReload(key, oldValue, executor); + refreshFuture = refresh; + } + refreshFuture.whenComplete( + (newValue, error) -> { + long loadTime = statsTicker().read() - startTime; + if (error != null) { + if (!(error instanceof CancellationException) + && !(error instanceof TimeoutException)) { + logger.log(Level.WARNING, "Exception thrown during refresh", error); + } + node.casWriteTime(refreshWriteTime, oldWriteTime); + statsCounter().recordLoadFailure(loadTime); + return; + } + + @SuppressWarnings("unchecked") + V value = (isAsync && (newValue != null)) ? (V) refreshFuture : newValue; + + boolean[] discard = new boolean[1]; + compute( + key, + (k, currentValue) -> { + if (currentValue == null) { + return value; + } else if ((currentValue == oldValue) + && (node.getWriteTime() == refreshWriteTime)) { + return value; + } + discard[0] = true; + return currentValue; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + true); + + if (discard[0] && hasRemovalListener()) { + notifyRemoval(key, value, RemovalCause.REPLACED); + } + if (newValue == null) { + statsCounter().recordLoadFailure(loadTime); + } else { + statsCounter().recordLoadSuccess(loadTime); + } + }); + } catch (Throwable t) { + node.casWriteTime(refreshWriteTime, oldWriteTime); + logger.log(Level.SEVERE, "Exception thrown when submitting refresh task", t); + } + } + } + + /** + * Returns the expiration time for the entry after being created. + * + * @param key the key of the entry that was created + * @param value the value of the entry that was created + * @param expiry the calculator for the expiration time + * @param now the current time, in nanoseconds + * @return the expiration time + */ + long expireAfterCreate(K key, V value, Expiry expiry, long now) { + if (expiresVariable() && (key != null) && (value != null)) { + long duration = expiry.expireAfterCreate(key, value, now); + return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); + } + return 0L; + } + + /** + * Returns the expiration time for the entry after being updated. + * + * @param node the entry in the page replacement policy + * @param key the key of the entry that was updated + * @param value the value of the entry that was updated + * @param expiry the calculator for the expiration time + * @param now the current time, in nanoseconds + * @return the expiration time + */ + long expireAfterUpdate(Node node, K key, V value, Expiry expiry, long now) { + if (expiresVariable() && (key != null) && (value != null)) { + long currentDuration = Math.max(1, node.getVariableTime() - now); + long duration = expiry.expireAfterUpdate(key, value, now, currentDuration); + return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); + } + return 0L; + } + + /** + * Returns the access time for the entry after a read. + * + * @param node the entry in the page replacement policy + * @param key the key of the entry that was read + * @param value the value of the entry that was read + * @param expiry the calculator for the expiration time + * @param now the current time, in nanoseconds + * @return the expiration time + */ + long expireAfterRead(Node node, K key, V value, Expiry expiry, long now) { + if (expiresVariable() && (key != null) && (value != null)) { + long currentDuration = Math.max(1, node.getVariableTime() - now); + long duration = expiry.expireAfterRead(key, value, now, currentDuration); + return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); + } + return 0L; + } + + /** + * Attempts to update the access time for the entry after a read. + * + * @param node the entry in the page replacement policy + * @param key the key of the entry that was read + * @param value the value of the entry that was read + * @param expiry the calculator for the expiration time + * @param now the current time, in nanoseconds + */ + void tryExpireAfterRead(Node node, K key, V value, Expiry expiry, long now) { + if (!expiresVariable() || (key == null) || (value == null)) { + return; + } + + long variableTime = node.getVariableTime(); + long currentDuration = Math.max(1, variableTime - now); + if (isAsync && (currentDuration > MAXIMUM_EXPIRY)) { + // expireAfterCreate has not yet set the duration after completion + return; + } + + long duration = expiry.expireAfterRead(key, value, now, currentDuration); + if (duration != currentDuration) { + long expirationTime = isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); + node.casVariableTime(variableTime, expirationTime); + } + } + + void setVariableTime(Node node, long expirationTime) { + if (expiresVariable()) { + node.setVariableTime(expirationTime); + } + } + + void setWriteTime(Node node, long now) { + if (expiresAfterWrite() || refreshAfterWrite()) { + node.setWriteTime(now); + } + } + + void setAccessTime(Node node, long now) { + if (expiresAfterAccess()) { + node.setAccessTime(now); + } + } + + /** + * Performs the post-processing work required after a write. + * + * @param task the pending operation to be applied + */ + void afterWrite(Runnable task) { + for (int i = 0; i < WRITE_BUFFER_RETRIES; i++) { + if (writeBuffer.offer(task)) { + scheduleAfterWrite(); + return; + } + scheduleDrainBuffers(); + } + + // The maintenance task may be scheduled but not running. This might occur due to all of the + // executor's threads being busy (perhaps writing into this cache), the write rate greatly + // exceeds the consuming rate, priority inversion, or if the executor silently discarded the + // maintenance task. In these scenarios then the writing threads cannot make progress and + // instead writers provide assistance by performing this work directly. + try { + performCleanUp(task); + } catch (RuntimeException e) { + logger.log(Level.SEVERE, "Exception thrown when performing the maintenance task", e); + } + } + + /** + * Conditionally schedules the asynchronous maintenance task after a write operation. If the task + * status was IDLE or REQUIRED then the maintenance task is scheduled immediately. If it is + * already processing then it is set to transition to REQUIRED upon completion so that a new + * execution is triggered by the next operation. + */ + void scheduleAfterWrite() { + for (; ; ) { + switch (drainStatus()) { + case IDLE: + casDrainStatus(IDLE, REQUIRED); + scheduleDrainBuffers(); + return; + case REQUIRED: + scheduleDrainBuffers(); + return; + case PROCESSING_TO_IDLE: + if (casDrainStatus(PROCESSING_TO_IDLE, PROCESSING_TO_REQUIRED)) { + return; + } + continue; + case PROCESSING_TO_REQUIRED: + return; + default: + throw new IllegalStateException(); + } + } + } + + /** + * Attempts to schedule an asynchronous task to apply the pending operations to the page + * replacement policy. If the executor rejects the task then it is run directly. + */ + void scheduleDrainBuffers() { + if (drainStatus() >= PROCESSING_TO_IDLE) { + return; + } + if (evictionLock.tryLock()) { + try { + int drainStatus = drainStatus(); + if (drainStatus >= PROCESSING_TO_IDLE) { + return; + } + lazySetDrainStatus(PROCESSING_TO_IDLE); + executor.execute(drainBuffersTask); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown when submitting maintenance task", t); + maintenance(/* ignored */ null); + } finally { + evictionLock.unlock(); + } + } + } + + @Override + public void cleanUp() { + try { + performCleanUp(/* ignored */ null); + } catch (RuntimeException e) { + logger.log(Level.SEVERE, "Exception thrown when performing the maintenance task", e); + } + } + + /** + * Performs the maintenance work, blocking until the lock is acquired. Any exception thrown, such + * as by {@link CacheWriter#delete}, is propagated to the caller. + * + * @param task an additional pending task to run, or {@code null} if not present + */ + void performCleanUp(Runnable task) { + evictionLock.lock(); + try { + maintenance(task); + } finally { + evictionLock.unlock(); + } + if ((drainStatus() == REQUIRED) && (executor == ForkJoinPool.commonPool())) { + scheduleDrainBuffers(); + } + } + + /** + * Performs the pending maintenance work and sets the state flags during processing to avoid + * excess scheduling attempts. The read buffer, write buffer, and reference queues are drained, + * followed by expiration, and size-based eviction. + * + * @param task an additional pending task to run, or {@code null} if not present + */ + void maintenance(Runnable task) { + lazySetDrainStatus(PROCESSING_TO_IDLE); + + try { + drainReadBuffer(); + + drainWriteBuffer(); + if (task != null) { + task.run(); + } + + drainKeyReferences(); + drainValueReferences(); + + expireEntries(); + evictEntries(); + + climb(); + } finally { + if ((drainStatus() != PROCESSING_TO_IDLE) || !casDrainStatus(PROCESSING_TO_IDLE, IDLE)) { + lazySetDrainStatus(REQUIRED); + } + } + } + + /** Drains the weak key references queue. */ + void drainKeyReferences() { + if (!collectKeys()) { + return; + } + Reference keyRef; + while ((keyRef = keyReferenceQueue().poll()) != null) { + Node node = data.get(keyRef); + if (node != null) { + evictEntry(node, RemovalCause.COLLECTED, 0L); + } + } + } + + /** Drains the weak / soft value references queue. */ + void drainValueReferences() { + if (!collectValues()) { + return; + } + Reference valueRef; + while ((valueRef = valueReferenceQueue().poll()) != null) { + @SuppressWarnings("unchecked") + InternalReference ref = (InternalReference) valueRef; + Node node = data.get(ref.getKeyReference()); + if ((node != null) && (valueRef == node.getValueReference())) { + evictEntry(node, RemovalCause.COLLECTED, 0L); + } + } + } + + /** Drains the read buffer. */ + void drainReadBuffer() { + if (!skipReadBuffer()) { + readBuffer.drainTo(accessPolicy); + } + } + + /** Updates the node's location in the page replacement policy. */ + void onAccess(Node node) { + if (evicts()) { + K key = node.getKey(); + if (key == null) { + return; + } + frequencySketch().increment(key); + if (node.inWindow()) { + reorder(accessOrderWindowDeque(), node); + } else if (node.inMainProbation()) { + reorderProbation(node); + } else { + reorder(accessOrderProtectedDeque(), node); + } + setHitsInSample(hitsInSample() + 1); + } else if (expiresAfterAccess()) { + reorder(accessOrderWindowDeque(), node); + } + if (expiresVariable()) { + timerWheel().reschedule(node); + } + } + + /** Promote the node from probation to protected on an access. */ + void reorderProbation(Node node) { + if (!accessOrderProbationDeque().contains(node)) { + // Ignore stale accesses for an entry that is no longer present + return; + } else if (node.getPolicyWeight() > mainProtectedMaximum()) { + return; + } + + // If the protected space exceeds its maximum, the LRU items are demoted to the probation space. + // This is deferred to the adaption phase at the end of the maintenance cycle. + setMainProtectedWeightedSize(mainProtectedWeightedSize() + node.getPolicyWeight()); + accessOrderProbationDeque().remove(node); + accessOrderProtectedDeque().add(node); + node.makeMainProtected(); + } + + /** Updates the node's location in the policy's deque. */ + static void reorder(LinkedDeque> deque, Node node) { + // An entry may be scheduled for reordering despite having been removed. This can occur when the + // entry was concurrently read while a writer was removing it. If the entry is no longer linked + // then it does not need to be processed. + if (deque.contains(node)) { + deque.moveToBack(node); + } + } + + /** Drains the write buffer. */ + void drainWriteBuffer() { + for (int i = 0; i <= WRITE_BUFFER_MAX; i++) { + Runnable task = writeBuffer.poll(); + if (task == null) { + return; + } + task.run(); + } + lazySetDrainStatus(PROCESSING_TO_REQUIRED); + } + + /** + * Atomically transitions the node to the dead state and decrements the + * weightedSize. + * + * @param node the entry in the page replacement policy + */ + void makeDead(Node node) { + synchronized (node) { + if (node.isDead()) { + return; + } + if (evicts()) { + // The node's policy weight may be out of sync due to a pending update waiting to be + // processed. At this point the node's weight is finalized, so the weight can be safely + // taken from the node's perspective and the sizes will be adjusted correctly. + if (node.inWindow()) { + setWindowWeightedSize(windowWeightedSize() - node.getWeight()); + } else if (node.inMainProtected()) { + setMainProtectedWeightedSize(mainProtectedWeightedSize() - node.getWeight()); + } + setWeightedSize(weightedSize() - node.getWeight()); + } + node.die(); + } + } + + /** Adds the node to the page replacement policy. */ + final class AddTask implements Runnable { + final Node node; + final int weight; + + AddTask(Node node, int weight) { + this.weight = weight; + this.node = node; + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + public void run() { + if (evicts()) { + long weightedSize = weightedSize(); + setWeightedSize(weightedSize + weight); + setWindowWeightedSize(windowWeightedSize() + weight); + node.setPolicyWeight(node.getPolicyWeight() + weight); + + long maximum = maximum(); + if (weightedSize >= (maximum >>> 1)) { + // Lazily initialize when close to the maximum + long capacity = isWeighted() ? data.mappingCount() : maximum; + frequencySketch().ensureCapacity(capacity); + } + + K key = node.getKey(); + if (key != null) { + frequencySketch().increment(key); + } + + setMissesInSample(missesInSample() + 1); + } + + // ignore out-of-order write operations + boolean isAlive; + synchronized (node) { + isAlive = node.isAlive(); + } + if (isAlive) { + if (expiresAfterWrite()) { + writeOrderDeque().add(node); + } + if (evicts() && (weight > windowMaximum())) { + accessOrderWindowDeque().offerFirst(node); + } else if (evicts() || expiresAfterAccess()) { + accessOrderWindowDeque().offerLast(node); + } + if (expiresVariable()) { + timerWheel().schedule(node); + } + } + + // Ensure that in-flight async computation cannot expire (reset on a completion callback) + if (isComputingAsync(node)) { + synchronized (node) { + if (!Async.isReady((CompletableFuture) node.getValue())) { + long expirationTime = expirationTicker().read() + ASYNC_EXPIRY; + setVariableTime(node, expirationTime); + setAccessTime(node, expirationTime); + setWriteTime(node, expirationTime); + } + } + } + } + } + + /** Removes a node from the page replacement policy. */ + final class RemovalTask implements Runnable { + final Node node; + + RemovalTask(Node node) { + this.node = node; + } + + @Override + public void run() { + // add may not have been processed yet + if (node.inWindow() && (evicts() || expiresAfterAccess())) { + accessOrderWindowDeque().remove(node); + } else if (evicts()) { + if (node.inMainProbation()) { + accessOrderProbationDeque().remove(node); + } else { + accessOrderProtectedDeque().remove(node); + } + } + if (expiresAfterWrite()) { + writeOrderDeque().remove(node); + } else if (expiresVariable()) { + timerWheel().deschedule(node); + } + makeDead(node); + } + } + + /** Updates the weighted size. */ + final class UpdateTask implements Runnable { + final int weightDifference; + final Node node; + + public UpdateTask(Node node, int weightDifference) { + this.weightDifference = weightDifference; + this.node = node; + } + + @Override + public void run() { + if (evicts()) { + int oldWeightedSize = node.getPolicyWeight(); + node.setPolicyWeight(oldWeightedSize + weightDifference); + if (node.inWindow()) { + if (node.getPolicyWeight() <= windowMaximum()) { + onAccess(node); + } else if (accessOrderWindowDeque().contains(node)) { + accessOrderWindowDeque().moveToFront(node); + } + setWindowWeightedSize(windowWeightedSize() + weightDifference); + } else if (node.inMainProbation()) { + if (node.getPolicyWeight() <= maximum()) { + onAccess(node); + } else if (accessOrderProbationDeque().remove(node)) { + accessOrderWindowDeque().addFirst(node); + setWindowWeightedSize(windowWeightedSize() + node.getPolicyWeight()); + } + } else if (node.inMainProtected()) { + if (node.getPolicyWeight() <= maximum()) { + onAccess(node); + setMainProtectedWeightedSize(mainProtectedWeightedSize() + weightDifference); + } else if (accessOrderProtectedDeque().remove(node)) { + accessOrderWindowDeque().addFirst(node); + setWindowWeightedSize(windowWeightedSize() + node.getPolicyWeight()); + setMainProtectedWeightedSize(mainProtectedWeightedSize() - oldWeightedSize); + } else { + setMainProtectedWeightedSize(mainProtectedWeightedSize() - oldWeightedSize); + } + } + setWeightedSize(weightedSize() + weightDifference); + } else if (expiresAfterAccess()) { + onAccess(node); + } + if (expiresAfterWrite()) { + reorder(writeOrderDeque(), node); + } else if (expiresVariable()) { + timerWheel().reschedule(node); + } + } + } + + /* --------------- Concurrent Map Support --------------- */ + + @Override + public boolean isEmpty() { + return data.isEmpty(); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public long estimatedSize() { + return data.mappingCount(); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + public void clear() { + evictionLock.lock(); + try { + long now = expirationTicker().read(); + + // Apply all pending writes + Runnable task; + while ((task = writeBuffer.poll()) != null) { + task.run(); + } + + // Discard all entries + for (Node node : data.values()) { + removeNode(node, now); + } + + // Cancel the scheduled cleanup + Pacer pacer = pacer(); + if (pacer != null) { + pacer.cancel(); + } + + // Discard all pending reads + readBuffer.drainTo(e -> {}); + } finally { + evictionLock.unlock(); + } + } + + @SuppressWarnings("GuardedByChecker") + void removeNode(Node node, long now) { + K key = node.getKey(); + @SuppressWarnings("unchecked") + V[] value = (V[]) new Object[1]; + RemovalCause[] cause = new RemovalCause[1]; + + data.computeIfPresent( + node.getKeyReference(), + (k, n) -> { + if (n != node) { + return n; + } + synchronized (n) { + value[0] = n.getValue(); + + if ((key == null) || (value[0] == null)) { + cause[0] = RemovalCause.COLLECTED; + } else if (hasExpired(n, now)) { + cause[0] = RemovalCause.EXPIRED; + } else { + cause[0] = RemovalCause.EXPLICIT; + } + + if (key != null) { + writer.delete(key, value[0], cause[0]); + } + makeDead(n); + return null; + } + }); + + if (node.inWindow() && (evicts() || expiresAfterAccess())) { + accessOrderWindowDeque().remove(node); + } else if (evicts()) { + if (node.inMainProbation()) { + accessOrderProbationDeque().remove(node); + } else { + accessOrderProtectedDeque().remove(node); + } + } + if (expiresAfterWrite()) { + writeOrderDeque().remove(node); + } else if (expiresVariable()) { + timerWheel().deschedule(node); + } + + if ((cause[0] != null) && hasRemovalListener()) { + notifyRemoval(key, value[0], cause[0]); + } + } + + @Override + public boolean containsKey(Object key) { + Node node = data.get(nodeFactory.newLookupKey(key)); + return (node != null) + && (node.getValue() != null) + && !hasExpired(node, expirationTicker().read()); + } + + @Override + public boolean containsValue(Object value) { + requireNonNull(value); + + long now = expirationTicker().read(); + for (Node node : data.values()) { + if (node.containsValue(value) && !hasExpired(node, now) && (node.getKey() != null)) { + return true; + } + } + return false; + } + + @Override + public V get(Object key) { + return getIfPresent(key, /* recordStats */ false); + } + + @Override + public V getIfPresent(Object key, boolean recordStats) { + Node node = data.get(nodeFactory.newLookupKey(key)); + if (node == null) { + if (recordStats) { + statsCounter().recordMisses(1); + } + if (drainStatus() == REQUIRED) { + scheduleDrainBuffers(); + } + return null; + } + + V value = node.getValue(); + long now = expirationTicker().read(); + if (hasExpired(node, now) || (collectValues() && (value == null))) { + if (recordStats) { + statsCounter().recordMisses(1); + } + scheduleDrainBuffers(); + return null; + } + + if (!isComputingAsync(node)) { + @SuppressWarnings("unchecked") + K castedKey = (K) key; + setAccessTime(node, now); + tryExpireAfterRead(node, castedKey, value, expiry(), now); + } + afterRead(node, now, recordStats); + return value; + } + + @Override + public V getIfPresentQuietly(Object key, long[ /* 1 */] writeTime) { + V value; + Node node = data.get(nodeFactory.newLookupKey(key)); + if ((node == null) + || ((value = node.getValue()) == null) + || hasExpired(node, expirationTicker().read())) { + return null; + } + writeTime[0] = node.getWriteTime(); + return value; + } + + @Override + public Map getAllPresent(Iterable keys) { + Set uniqueKeys = new LinkedHashSet<>(); + for (Object key : keys) { + uniqueKeys.add(key); + } + + int misses = 0; + long now = expirationTicker().read(); + Map result = new LinkedHashMap<>(uniqueKeys.size()); + for (Object key : uniqueKeys) { + V value; + Node node = data.get(nodeFactory.newLookupKey(key)); + if ((node == null) || ((value = node.getValue()) == null) || hasExpired(node, now)) { + misses++; + } else { + result.put(key, value); + + if (!isComputingAsync(node)) { + @SuppressWarnings("unchecked") + K castedKey = (K) key; + tryExpireAfterRead(node, castedKey, value, expiry(), now); + setAccessTime(node, now); + } + afterRead(node, now, /* recordHit */ false); + } + } + statsCounter().recordMisses(misses); + statsCounter().recordHits(result.size()); + + @SuppressWarnings("unchecked") + Map castedResult = (Map) result; + return Collections.unmodifiableMap(castedResult); + } + + @Override + public V put(K key, V value) { + return put(key, value, expiry(), /* notifyWriter */ true, /* onlyIfAbsent */ false); + } + + @Override + public V put(K key, V value, boolean notifyWriter) { + return put(key, value, expiry(), notifyWriter, /* onlyIfAbsent */ false); + } + + @Override + public V putIfAbsent(K key, V value) { + return put(key, value, expiry(), /* notifyWriter */ true, /* onlyIfAbsent */ true); + } + + /** + * Adds a node to the policy and the data store. If an existing node is found, then its value is + * updated if allowed. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @param expiry the calculator for the expiration time + * @param notifyWriter if the writer should be notified for an inserted or updated entry + * @param onlyIfAbsent a write is performed only if the key is not already associated with a value + * @return the prior value in or null if no mapping was found + */ + V put(K key, V value, Expiry expiry, boolean notifyWriter, boolean onlyIfAbsent) { + requireNonNull(key); + requireNonNull(value); + + Node node = null; + long now = expirationTicker().read(); + int newWeight = weigher.weigh(key, value); + for (; ; ) { + Node prior = data.get(nodeFactory.newLookupKey(key)); + if (prior == null) { + if (node == null) { + node = + nodeFactory.newNode( + key, keyReferenceQueue(), value, valueReferenceQueue(), newWeight, now); + setVariableTime(node, expireAfterCreate(key, value, expiry, now)); + } + if (notifyWriter && hasWriter()) { + Node computed = node; + prior = + data.computeIfAbsent( + node.getKeyReference(), + k -> { + writer.write(key, value); + return computed; + }); + if (prior == node) { + afterWrite(new AddTask(node, newWeight)); + return null; + } else if (onlyIfAbsent) { + // An optimistic fast path to avoid unnecessary locking + V currentValue = prior.getValue(); + if ((currentValue != null) && !hasExpired(prior, now)) { + if (!isComputingAsync(prior)) { + tryExpireAfterRead(prior, key, currentValue, expiry(), now); + setAccessTime(prior, now); + } + afterRead(prior, now, /* recordHit */ false); + return currentValue; + } + } + } else { + prior = data.putIfAbsent(node.getKeyReference(), node); + if (prior == null) { + afterWrite(new AddTask(node, newWeight)); + return null; + } else if (onlyIfAbsent) { + // An optimistic fast path to avoid unnecessary locking + V currentValue = prior.getValue(); + if ((currentValue != null) && !hasExpired(prior, now)) { + if (!isComputingAsync(prior)) { + tryExpireAfterRead(prior, key, currentValue, expiry(), now); + setAccessTime(prior, now); + } + afterRead(prior, now, /* recordHit */ false); + return currentValue; + } + } + } + } else if (onlyIfAbsent) { + // An optimistic fast path to avoid unnecessary locking + V currentValue = prior.getValue(); + if ((currentValue != null) && !hasExpired(prior, now)) { + if (!isComputingAsync(prior)) { + tryExpireAfterRead(prior, key, currentValue, expiry(), now); + setAccessTime(prior, now); + } + afterRead(prior, now, /* recordHit */ false); + return currentValue; + } + } + + V oldValue; + long varTime; + int oldWeight; + boolean expired = false; + boolean mayUpdate = true; + boolean exceedsTolerance = false; + synchronized (prior) { + if (!prior.isAlive()) { + continue; + } + oldValue = prior.getValue(); + oldWeight = prior.getWeight(); + if (oldValue == null) { + varTime = expireAfterCreate(key, value, expiry, now); + writer.delete(key, null, RemovalCause.COLLECTED); + } else if (hasExpired(prior, now)) { + expired = true; + varTime = expireAfterCreate(key, value, expiry, now); + writer.delete(key, oldValue, RemovalCause.EXPIRED); + } else if (onlyIfAbsent) { + mayUpdate = false; + varTime = expireAfterRead(prior, key, value, expiry, now); + } else { + varTime = expireAfterUpdate(prior, key, value, expiry, now); + } + + if (notifyWriter && (expired || (mayUpdate && (value != oldValue)))) { + writer.write(key, value); + } + if (mayUpdate) { + exceedsTolerance = + (expiresAfterWrite() && (now - prior.getWriteTime()) > EXPIRE_WRITE_TOLERANCE) + || (expiresVariable() + && Math.abs(varTime - prior.getVariableTime()) > EXPIRE_WRITE_TOLERANCE); + + setWriteTime(prior, now); + prior.setWeight(newWeight); + prior.setValue(value, valueReferenceQueue()); + } + + setVariableTime(prior, varTime); + setAccessTime(prior, now); + } + + if (hasRemovalListener()) { + if (expired) { + notifyRemoval(key, oldValue, RemovalCause.EXPIRED); + } else if (oldValue == null) { + notifyRemoval(key, /* oldValue */ null, RemovalCause.COLLECTED); + } else if (mayUpdate && (value != oldValue)) { + notifyRemoval(key, oldValue, RemovalCause.REPLACED); + } + } + + int weightedDifference = mayUpdate ? (newWeight - oldWeight) : 0; + if ((oldValue == null) || (weightedDifference != 0) || expired) { + afterWrite(new UpdateTask(prior, weightedDifference)); + } else if (!onlyIfAbsent && exceedsTolerance) { + afterWrite(new UpdateTask(prior, weightedDifference)); + } else { + if (mayUpdate) { + setWriteTime(prior, now); + } + afterRead(prior, now, /* recordHit */ false); + } + + return expired ? null : oldValue; + } + } + + @Override + public V remove(Object key) { + @SuppressWarnings("unchecked") + K castKey = (K) key; + @SuppressWarnings({"unchecked", "rawtypes"}) + Node[] node = new Node[1]; + @SuppressWarnings("unchecked") + V[] oldValue = (V[]) new Object[1]; + RemovalCause[] cause = new RemovalCause[1]; + + data.computeIfPresent( + nodeFactory.newLookupKey(key), + (k, n) -> { + synchronized (n) { + oldValue[0] = n.getValue(); + if (oldValue[0] == null) { + cause[0] = RemovalCause.COLLECTED; + } else if (hasExpired(n, expirationTicker().read())) { + cause[0] = RemovalCause.EXPIRED; + } else { + cause[0] = RemovalCause.EXPLICIT; + } + writer.delete(castKey, oldValue[0], cause[0]); + n.retire(); + } + node[0] = n; + return null; + }); + + if (cause[0] != null) { + afterWrite(new RemovalTask(node[0])); + if (hasRemovalListener()) { + notifyRemoval(castKey, oldValue[0], cause[0]); + } + } + return (cause[0] == RemovalCause.EXPLICIT) ? oldValue[0] : null; + } + + @Override + public boolean remove(Object key, Object value) { + requireNonNull(key); + if (value == null) { + return false; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + Node[] removed = new Node[1]; + @SuppressWarnings("unchecked") + K[] oldKey = (K[]) new Object[1]; + @SuppressWarnings("unchecked") + V[] oldValue = (V[]) new Object[1]; + RemovalCause[] cause = new RemovalCause[1]; + + data.computeIfPresent( + nodeFactory.newLookupKey(key), + (kR, node) -> { + synchronized (node) { + oldKey[0] = node.getKey(); + oldValue[0] = node.getValue(); + if (oldKey[0] == null) { + cause[0] = RemovalCause.COLLECTED; + } else if (hasExpired(node, expirationTicker().read())) { + cause[0] = RemovalCause.EXPIRED; + } else if (node.containsValue(value)) { + cause[0] = RemovalCause.EXPLICIT; + } else { + return node; + } + writer.delete(oldKey[0], oldValue[0], cause[0]); + removed[0] = node; + node.retire(); + return null; + } + }); + + if (removed[0] == null) { + return false; + } else if (hasRemovalListener()) { + notifyRemoval(oldKey[0], oldValue[0], cause[0]); + } + afterWrite(new RemovalTask(removed[0])); + return (cause[0] == RemovalCause.EXPLICIT); + } + + @Override + public V replace(K key, V value) { + requireNonNull(key); + requireNonNull(value); + + int[] oldWeight = new int[1]; + @SuppressWarnings("unchecked") + K[] nodeKey = (K[]) new Object[1]; + @SuppressWarnings("unchecked") + V[] oldValue = (V[]) new Object[1]; + long[] now = new long[1]; + int weight = weigher.weigh(key, value); + Node node = + data.computeIfPresent( + nodeFactory.newLookupKey(key), + (k, n) -> { + synchronized (n) { + nodeKey[0] = n.getKey(); + oldValue[0] = n.getValue(); + oldWeight[0] = n.getWeight(); + if ((nodeKey[0] == null) + || (oldValue[0] == null) + || hasExpired(n, now[0] = expirationTicker().read())) { + oldValue[0] = null; + return n; + } + + long varTime = expireAfterUpdate(n, key, value, expiry(), now[0]); + if (value != oldValue[0]) { + writer.write(nodeKey[0], value); + } + n.setValue(value, valueReferenceQueue()); + n.setWeight(weight); + + setVariableTime(n, varTime); + setAccessTime(n, now[0]); + setWriteTime(n, now[0]); + return n; + } + }); + + if (oldValue[0] == null) { + return null; + } + + int weightedDifference = (weight - oldWeight[0]); + if (expiresAfterWrite() || (weightedDifference != 0)) { + afterWrite(new UpdateTask(node, weightedDifference)); + } else { + afterRead(node, now[0], /* recordHit */ false); + } + + if (hasRemovalListener() && (value != oldValue[0])) { + notifyRemoval(nodeKey[0], oldValue[0], RemovalCause.REPLACED); + } + return oldValue[0]; + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + requireNonNull(key); + requireNonNull(oldValue); + requireNonNull(newValue); + + int weight = weigher.weigh(key, newValue); + boolean[] replaced = new boolean[1]; + @SuppressWarnings("unchecked") + K[] nodeKey = (K[]) new Object[1]; + @SuppressWarnings("unchecked") + V[] prevValue = (V[]) new Object[1]; + int[] oldWeight = new int[1]; + long[] now = new long[1]; + Node node = + data.computeIfPresent( + nodeFactory.newLookupKey(key), + (k, n) -> { + synchronized (n) { + nodeKey[0] = n.getKey(); + prevValue[0] = n.getValue(); + oldWeight[0] = n.getWeight(); + if ((nodeKey[0] == null) + || (prevValue[0] == null) + || !n.containsValue(oldValue) + || hasExpired(n, now[0] = expirationTicker().read())) { + return n; + } + + long varTime = expireAfterUpdate(n, key, newValue, expiry(), now[0]); + if (newValue != prevValue[0]) { + writer.write(key, newValue); + } + n.setValue(newValue, valueReferenceQueue()); + n.setWeight(weight); + + setVariableTime(n, varTime); + setAccessTime(n, now[0]); + setWriteTime(n, now[0]); + replaced[0] = true; + } + return n; + }); + + if (!replaced[0]) { + return false; + } + + int weightedDifference = (weight - oldWeight[0]); + if (expiresAfterWrite() || (weightedDifference != 0)) { + afterWrite(new UpdateTask(node, weightedDifference)); + } else { + afterRead(node, now[0], /* recordHit */ false); + } + + if (hasRemovalListener() && (oldValue != newValue)) { + notifyRemoval(nodeKey[0], prevValue[0], RemovalCause.REPLACED); + } + return true; + } + + @Override + public void replaceAll(BiFunction function) { + requireNonNull(function); + + BiFunction remappingFunction = + (key, oldValue) -> { + V newValue = requireNonNull(function.apply(key, oldValue)); + if (oldValue != newValue) { + writer.write(key, newValue); + } + return newValue; + }; + for (K key : keySet()) { + long[] now = {expirationTicker().read()}; + Object lookupKey = nodeFactory.newLookupKey(key); + remap(key, lookupKey, remappingFunction, now, /* computeIfAbsent */ false); + } + } + + @Override + public V computeIfAbsent( + K key, + Function mappingFunction, + boolean recordStats, + boolean recordLoad) { + requireNonNull(key); + requireNonNull(mappingFunction); + long now = expirationTicker().read(); + + // An optimistic fast path to avoid unnecessary locking + Node node = data.get(nodeFactory.newLookupKey(key)); + if (node != null) { + V value = node.getValue(); + if ((value != null) && !hasExpired(node, now)) { + if (!isComputingAsync(node)) { + tryExpireAfterRead(node, key, value, expiry(), now); + setAccessTime(node, now); + } + + afterRead(node, now, /* recordHit */ recordStats); + return value; + } + } + if (recordStats) { + mappingFunction = statsAware(mappingFunction, recordLoad); + } + Object keyRef = nodeFactory.newReferenceKey(key, keyReferenceQueue()); + return doComputeIfAbsent(key, keyRef, mappingFunction, new long[] {now}, recordStats); + } + + /** Returns the current value from a computeIfAbsent invocation. */ + V doComputeIfAbsent( + K key, + Object keyRef, + Function mappingFunction, + long[ /* 1 */] now, + boolean recordStats) { + @SuppressWarnings("unchecked") + V[] oldValue = (V[]) new Object[1]; + @SuppressWarnings("unchecked") + V[] newValue = (V[]) new Object[1]; + @SuppressWarnings("unchecked") + K[] nodeKey = (K[]) new Object[1]; + @SuppressWarnings({"unchecked", "rawtypes"}) + Node[] removed = new Node[1]; + + int[] weight = new int[2]; // old, new + RemovalCause[] cause = new RemovalCause[1]; + Node node = + data.compute( + keyRef, + (k, n) -> { + if (n == null) { + newValue[0] = mappingFunction.apply(key); + if (newValue[0] == null) { + return null; + } + now[0] = expirationTicker().read(); + weight[1] = weigher.weigh(key, newValue[0]); + n = + nodeFactory.newNode( + key, + keyReferenceQueue(), + newValue[0], + valueReferenceQueue(), + weight[1], + now[0]); + setVariableTime(n, expireAfterCreate(key, newValue[0], expiry(), now[0])); + return n; + } + + synchronized (n) { + nodeKey[0] = n.getKey(); + weight[0] = n.getWeight(); + oldValue[0] = n.getValue(); + if ((nodeKey[0] == null) || (oldValue[0] == null)) { + cause[0] = RemovalCause.COLLECTED; + } else if (hasExpired(n, now[0])) { + cause[0] = RemovalCause.EXPIRED; + } else { + return n; + } + + writer.delete(nodeKey[0], oldValue[0], cause[0]); + newValue[0] = mappingFunction.apply(key); + if (newValue[0] == null) { + removed[0] = n; + n.retire(); + return null; + } + weight[1] = weigher.weigh(key, newValue[0]); + n.setValue(newValue[0], valueReferenceQueue()); + n.setWeight(weight[1]); + + now[0] = expirationTicker().read(); + setVariableTime(n, expireAfterCreate(key, newValue[0], expiry(), now[0])); + setAccessTime(n, now[0]); + setWriteTime(n, now[0]); + return n; + } + }); + + if (node == null) { + if (removed[0] != null) { + afterWrite(new RemovalTask(removed[0])); + } + return null; + } + if (cause[0] != null) { + if (hasRemovalListener()) { + notifyRemoval(nodeKey[0], oldValue[0], cause[0]); + } + statsCounter().recordEviction(weight[0], cause[0]); + } + if (newValue[0] == null) { + if (!isComputingAsync(node)) { + tryExpireAfterRead(node, key, oldValue[0], expiry(), now[0]); + setAccessTime(node, now[0]); + } + + afterRead(node, now[0], /* recordHit */ recordStats); + return oldValue[0]; + } + if ((oldValue[0] == null) && (cause[0] == null)) { + afterWrite(new AddTask(node, weight[1])); + } else { + int weightedDifference = (weight[1] - weight[0]); + afterWrite(new UpdateTask(node, weightedDifference)); + } + + return newValue[0]; + } + + @Override + public V computeIfPresent( + K key, BiFunction remappingFunction) { + requireNonNull(key); + requireNonNull(remappingFunction); + + // A optimistic fast path to avoid unnecessary locking + Object lookupKey = nodeFactory.newLookupKey(key); + Node node = data.get(lookupKey); + long now; + if (node == null) { + return null; + } else if ((node.getValue() == null) || hasExpired(node, (now = expirationTicker().read()))) { + scheduleDrainBuffers(); + return null; + } + + BiFunction statsAwareRemappingFunction = + statsAware( + remappingFunction, /* recordMiss */ + false, + /* recordLoad */ true, /* recordLoadFailure */ + true); + return remap( + key, lookupKey, statsAwareRemappingFunction, new long[] {now}, /* computeIfAbsent */ false); + } + + @Override + public V compute( + K key, + BiFunction remappingFunction, + boolean recordMiss, + boolean recordLoad, + boolean recordLoadFailure) { + requireNonNull(key); + requireNonNull(remappingFunction); + + long[] now = {expirationTicker().read()}; + Object keyRef = nodeFactory.newReferenceKey(key, keyReferenceQueue()); + BiFunction statsAwareRemappingFunction = + statsAware(remappingFunction, recordMiss, recordLoad, recordLoadFailure); + return remap(key, keyRef, statsAwareRemappingFunction, now, /* computeIfAbsent */ true); + } + + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + requireNonNull(key); + requireNonNull(value); + requireNonNull(remappingFunction); + + long[] now = {expirationTicker().read()}; + Object keyRef = nodeFactory.newReferenceKey(key, keyReferenceQueue()); + BiFunction mergeFunction = + (k, oldValue) -> + (oldValue == null) ? value : statsAware(remappingFunction).apply(oldValue, value); + return remap(key, keyRef, mergeFunction, now, /* computeIfAbsent */ true); + } + + /** + * Attempts to compute a mapping for the specified key and its current mapped value (or {@code + * null} if there is no current mapping). + * + *

An entry that has expired or been reference collected is evicted and the computation + * continues as if the entry had not been present. This method does not pre-screen and does not + * wrap the remappingFunction to be statistics aware. + * + * @param key key with which the specified value is to be associated + * @param keyRef the key to associate with or a lookup only key if not computeIfAbsent + * @param remappingFunction the function to compute a value + * @param now the current time, according to the ticker + * @param computeIfAbsent if an absent entry can be computed + * @return the new value associated with the specified key, or null if none + */ + @SuppressWarnings("PMD.EmptyIfStmt") + V remap( + K key, + Object keyRef, + BiFunction remappingFunction, + long[ /* 1 */] now, + boolean computeIfAbsent) { + @SuppressWarnings("unchecked") + K[] nodeKey = (K[]) new Object[1]; + @SuppressWarnings("unchecked") + V[] oldValue = (V[]) new Object[1]; + @SuppressWarnings("unchecked") + V[] newValue = (V[]) new Object[1]; + @SuppressWarnings({"unchecked", "rawtypes"}) + Node[] removed = new Node[1]; + + int[] weight = new int[2]; // old, new + RemovalCause[] cause = new RemovalCause[1]; + + Node node = + data.compute( + keyRef, + (kr, n) -> { + if (n == null) { + if (!computeIfAbsent) { + return null; + } + newValue[0] = remappingFunction.apply(key, null); + if (newValue[0] == null) { + return null; + } + now[0] = expirationTicker().read(); + weight[1] = weigher.weigh(key, newValue[0]); + n = + nodeFactory.newNode( + keyRef, newValue[0], valueReferenceQueue(), weight[1], now[0]); + setVariableTime(n, expireAfterCreate(key, newValue[0], expiry(), now[0])); + return n; + } + + synchronized (n) { + nodeKey[0] = n.getKey(); + oldValue[0] = n.getValue(); + if ((nodeKey[0] == null) || (oldValue[0] == null)) { + cause[0] = RemovalCause.COLLECTED; + } else if (hasExpired(n, now[0])) { + cause[0] = RemovalCause.EXPIRED; + } + if (cause[0] != null) { + writer.delete(nodeKey[0], oldValue[0], cause[0]); + if (!computeIfAbsent) { + removed[0] = n; + n.retire(); + return null; + } + } + + newValue[0] = + remappingFunction.apply(nodeKey[0], (cause[0] == null) ? oldValue[0] : null); + if (newValue[0] == null) { + if (cause[0] == null) { + cause[0] = RemovalCause.EXPLICIT; + } + removed[0] = n; + n.retire(); + return null; + } + + weight[0] = n.getWeight(); + weight[1] = weigher.weigh(key, newValue[0]); + now[0] = expirationTicker().read(); + if (cause[0] == null) { + if (newValue[0] != oldValue[0]) { + cause[0] = RemovalCause.REPLACED; + } + setVariableTime(n, expireAfterUpdate(n, key, newValue[0], expiry(), now[0])); + } else { + setVariableTime(n, expireAfterCreate(key, newValue[0], expiry(), now[0])); + } + n.setValue(newValue[0], valueReferenceQueue()); + n.setWeight(weight[1]); + setAccessTime(n, now[0]); + setWriteTime(n, now[0]); + return n; + } + }); + + if (cause[0] != null) { + if (cause[0].wasEvicted()) { + statsCounter().recordEviction(weight[0], cause[0]); + } + if (hasRemovalListener()) { + notifyRemoval(nodeKey[0], oldValue[0], cause[0]); + } + } + + if (removed[0] != null) { + afterWrite(new RemovalTask(removed[0])); + } else if (node == null) { + // absent and not computable + } else if ((oldValue[0] == null) && (cause[0] == null)) { + afterWrite(new AddTask(node, weight[1])); + } else { + int weightedDifference = weight[1] - weight[0]; + if (expiresAfterWrite() || (weightedDifference != 0)) { + afterWrite(new UpdateTask(node, weightedDifference)); + } else { + if (cause[0] == null) { + if (!isComputingAsync(node)) { + tryExpireAfterRead(node, key, newValue[0], expiry(), now[0]); + setAccessTime(node, now[0]); + } + } else if (cause[0] == RemovalCause.COLLECTED) { + scheduleDrainBuffers(); + } + afterRead(node, now[0], /* recordHit */ false); + } + } + + return newValue[0]; + } + + @Override + public Set keySet() { + final Set ks = keySet; + return (ks == null) ? (keySet = new KeySetView<>(this)) : ks; + } + + @Override + public Collection values() { + final Collection vs = values; + return (vs == null) ? (values = new ValuesView<>(this)) : vs; + } + + @Override + public Set> entrySet() { + final Set> es = entrySet; + return (es == null) ? (entrySet = new EntrySetView<>(this)) : es; + } + + /** + * Returns an unmodifiable snapshot map ordered in eviction order, either ascending or descending. + * Beware that obtaining the mappings is NOT a constant-time operation. + * + * @param limit the maximum number of entries + * @param transformer a function that unwraps the value + * @param hottest the iteration order + * @return an unmodifiable snapshot in a specified order + */ + @SuppressWarnings("GuardedByChecker") + Map evictionOrder(int limit, Function transformer, boolean hottest) { + Supplier>> iteratorSupplier = + () -> { + Comparator> comparator = + Comparator.comparingInt( + node -> { + K key = node.getKey(); + return (key == null) ? 0 : frequencySketch().frequency(key); + }); + if (hottest) { + PeekingIterator> secondary = + PeekingIterator.comparing( + accessOrderProbationDeque().descendingIterator(), + accessOrderWindowDeque().descendingIterator(), + comparator); + return PeekingIterator.concat( + accessOrderProtectedDeque().descendingIterator(), secondary); + } else { + PeekingIterator> primary = + PeekingIterator.comparing( + accessOrderWindowDeque().iterator(), + accessOrderProbationDeque().iterator(), + comparator.reversed()); + return PeekingIterator.concat(primary, accessOrderProtectedDeque().iterator()); + } + }; + return fixedSnapshot(iteratorSupplier, limit, transformer); + } + + /** + * Returns an unmodifiable snapshot map ordered in access expiration order, either ascending or + * descending. Beware that obtaining the mappings is NOT a constant-time operation. + * + * @param limit the maximum number of entries + * @param transformer a function that unwraps the value + * @param oldest the iteration order + * @return an unmodifiable snapshot in a specified order + */ + @SuppressWarnings("GuardedByChecker") + Map expireAfterAccessOrder(int limit, Function transformer, boolean oldest) { + if (!evicts()) { + Supplier>> iteratorSupplier = + () -> + oldest + ? accessOrderWindowDeque().iterator() + : accessOrderWindowDeque().descendingIterator(); + return fixedSnapshot(iteratorSupplier, limit, transformer); + } + + Supplier>> iteratorSupplier = + () -> { + Comparator> comparator = Comparator.comparingLong(Node::getAccessTime); + PeekingIterator> first, second, third; + if (oldest) { + first = accessOrderWindowDeque().iterator(); + second = accessOrderProbationDeque().iterator(); + third = accessOrderProtectedDeque().iterator(); + } else { + comparator = comparator.reversed(); + first = accessOrderWindowDeque().descendingIterator(); + second = accessOrderProbationDeque().descendingIterator(); + third = accessOrderProtectedDeque().descendingIterator(); + } + return PeekingIterator.comparing( + PeekingIterator.comparing(first, second, comparator), third, comparator); + }; + return fixedSnapshot(iteratorSupplier, limit, transformer); + } + + /** + * Returns an unmodifiable snapshot map ordered in write expiration order, either ascending or + * descending. Beware that obtaining the mappings is NOT a constant-time operation. + * + * @param limit the maximum number of entries + * @param transformer a function that unwraps the value + * @param oldest the iteration order + * @return an unmodifiable snapshot in a specified order + */ + @SuppressWarnings("GuardedByChecker") + Map expireAfterWriteOrder(int limit, Function transformer, boolean oldest) { + Supplier>> iteratorSupplier = + () -> oldest ? writeOrderDeque().iterator() : writeOrderDeque().descendingIterator(); + return fixedSnapshot(iteratorSupplier, limit, transformer); + } + + /** + * Returns an unmodifiable snapshot map ordered by the provided iterator. Beware that obtaining + * the mappings is NOT a constant-time operation. + * + * @param iteratorSupplier the iterator + * @param limit the maximum number of entries + * @param transformer a function that unwraps the value + * @return an unmodifiable snapshot in the iterator's order + */ + Map fixedSnapshot( + Supplier>> iteratorSupplier, int limit, Function transformer) { + requireArgument(limit >= 0); + evictionLock.lock(); + try { + maintenance(/* ignored */ null); + + int initialCapacity = Math.min(limit, size()); + Iterator> iterator = iteratorSupplier.get(); + Map map = new LinkedHashMap<>(initialCapacity); + while ((map.size() < limit) && iterator.hasNext()) { + Node node = iterator.next(); + K key = node.getKey(); + V value = transformer.apply(node.getValue()); + if ((key != null) && (value != null) && node.isAlive()) { + map.put(key, value); + } + } + return Collections.unmodifiableMap(map); + } finally { + evictionLock.unlock(); + } + } + + /** + * Returns an unmodifiable snapshot map roughly ordered by the expiration time. The wheels are + * evaluated in order, but the timers that fall within the bucket's range are not sorted. Beware + * that obtaining the mappings is NOT a constant-time operation. + * + * @param ascending the direction + * @param limit the maximum number of entries + * @param transformer a function that unwraps the value + * @return an unmodifiable snapshot in the desired order + */ + Map variableSnapshot(boolean ascending, int limit, Function transformer) { + evictionLock.lock(); + try { + maintenance(/* ignored */ null); + return timerWheel().snapshot(ascending, limit, transformer); + } finally { + evictionLock.unlock(); + } + } + + /** An adapter to safely externalize the keys. */ + static final class KeySetView extends AbstractSet { + final BoundedLocalCache cache; + + KeySetView(BoundedLocalCache cache) { + this.cache = requireNonNull(cache); + } + + @Override + public int size() { + return cache.size(); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean contains(Object obj) { + return cache.containsKey(obj); + } + + @Override + public boolean remove(Object obj) { + return (cache.remove(obj) != null); + } + + @Override + public Iterator iterator() { + return new KeyIterator<>(cache); + } + + @Override + public Spliterator spliterator() { + return new KeySpliterator<>(cache); + } + + @Override + public Object[] toArray() { + List keys = new ArrayList<>(size()); + for (Object key : this) { + keys.add(key); + } + return keys.toArray(); + } + + @Override + public T[] toArray(T[] array) { + List keys = new ArrayList<>(size()); + for (Object key : this) { + keys.add(key); + } + return keys.toArray(array); + } + } + + /** An adapter to safely externalize the key iterator. */ + static final class KeyIterator implements Iterator { + final EntryIterator iterator; + + KeyIterator(BoundedLocalCache cache) { + this.iterator = new EntryIterator<>(cache); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public K next() { + return iterator.nextKey(); + } + + @Override + public void remove() { + iterator.remove(); + } + } + + /** An adapter to safely externalize the key spliterator. */ + static final class KeySpliterator implements Spliterator { + final Spliterator> spliterator; + final BoundedLocalCache cache; + + KeySpliterator(BoundedLocalCache cache) { + this(cache, cache.data.values().spliterator()); + } + + KeySpliterator(BoundedLocalCache cache, Spliterator> spliterator) { + this.spliterator = requireNonNull(spliterator); + this.cache = requireNonNull(cache); + } + + @Override + public void forEachRemaining(Consumer action) { + requireNonNull(action); + Consumer> consumer = + node -> { + K key = node.getKey(); + V value = node.getValue(); + long now = cache.expirationTicker().read(); + if ((key != null) + && (value != null) + && node.isAlive() + && !cache.hasExpired(node, now)) { + action.accept(key); + } + }; + spliterator.forEachRemaining(consumer); + } + + @Override + public boolean tryAdvance(Consumer action) { + requireNonNull(action); + boolean[] advanced = {false}; + Consumer> consumer = + node -> { + K key = node.getKey(); + V value = node.getValue(); + long now = cache.expirationTicker().read(); + if ((key != null) + && (value != null) + && node.isAlive() + && !cache.hasExpired(node, now)) { + action.accept(key); + advanced[0] = true; + } + }; + for (; ; ) { + if (spliterator.tryAdvance(consumer)) { + if (advanced[0]) { + return true; + } + continue; + } + return false; + } + } + + @Override + public Spliterator trySplit() { + Spliterator> split = spliterator.trySplit(); + return (split == null) ? null : new KeySpliterator<>(cache, split); + } + + @Override + public long estimateSize() { + return spliterator.estimateSize(); + } + + @Override + public int characteristics() { + return Spliterator.DISTINCT | Spliterator.CONCURRENT | Spliterator.NONNULL; + } + } + + /** An adapter to safely externalize the values. */ + static final class ValuesView extends AbstractCollection { + final BoundedLocalCache cache; + + ValuesView(BoundedLocalCache cache) { + this.cache = requireNonNull(cache); + } + + @Override + public int size() { + return cache.size(); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean contains(Object o) { + return cache.containsValue(o); + } + + @Override + public boolean removeIf(Predicate filter) { + requireNonNull(filter); + boolean removed = false; + for (Entry entry : cache.entrySet()) { + if (filter.test(entry.getValue())) { + removed |= cache.remove(entry.getKey(), entry.getValue()); + } + } + return removed; + } + + @Override + public Iterator iterator() { + return new ValueIterator<>(cache); + } + + @Override + public Spliterator spliterator() { + return new ValueSpliterator<>(cache); + } + } + + /** An adapter to safely externalize the value iterator. */ + static final class ValueIterator implements Iterator { + final EntryIterator iterator; + + ValueIterator(BoundedLocalCache cache) { + this.iterator = new EntryIterator<>(cache); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public V next() { + return iterator.nextValue(); + } + + @Override + public void remove() { + iterator.remove(); + } + } + + /** An adapter to safely externalize the value spliterator. */ + static final class ValueSpliterator implements Spliterator { + final Spliterator> spliterator; + final BoundedLocalCache cache; + + ValueSpliterator(BoundedLocalCache cache) { + this(cache, cache.data.values().spliterator()); + } + + ValueSpliterator(BoundedLocalCache cache, Spliterator> spliterator) { + this.spliterator = requireNonNull(spliterator); + this.cache = requireNonNull(cache); + } + + @Override + public void forEachRemaining(Consumer action) { + requireNonNull(action); + Consumer> consumer = + node -> { + K key = node.getKey(); + V value = node.getValue(); + long now = cache.expirationTicker().read(); + if ((key != null) + && (value != null) + && node.isAlive() + && !cache.hasExpired(node, now)) { + action.accept(value); + } + }; + spliterator.forEachRemaining(consumer); + } + + @Override + public boolean tryAdvance(Consumer action) { + requireNonNull(action); + boolean[] advanced = {false}; + long now = cache.expirationTicker().read(); + Consumer> consumer = + node -> { + K key = node.getKey(); + V value = node.getValue(); + if ((key != null) + && (value != null) + && !cache.hasExpired(node, now) + && node.isAlive()) { + action.accept(value); + advanced[0] = true; + } + }; + for (; ; ) { + if (spliterator.tryAdvance(consumer)) { + if (advanced[0]) { + return true; + } + continue; + } + return false; + } + } + + @Override + public Spliterator trySplit() { + Spliterator> split = spliterator.trySplit(); + return (split == null) ? null : new ValueSpliterator<>(cache, split); + } + + @Override + public long estimateSize() { + return spliterator.estimateSize(); + } + + @Override + public int characteristics() { + return Spliterator.CONCURRENT | Spliterator.NONNULL; + } + } + + /** An adapter to safely externalize the entries. */ + static final class EntrySetView extends AbstractSet> { + final BoundedLocalCache cache; + + EntrySetView(BoundedLocalCache cache) { + this.cache = requireNonNull(cache); + } + + @Override + public int size() { + return cache.size(); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean contains(Object obj) { + if (!(obj instanceof Entry)) { + return false; + } + Entry entry = (Entry) obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + if ((key == null) || (value == null)) { + return false; + } + Node node = cache.data.get(cache.nodeFactory.newLookupKey(key)); + return (node != null) && node.containsValue(value); + } + + @Override + public boolean remove(Object obj) { + if (!(obj instanceof Entry)) { + return false; + } + Entry entry = (Entry) obj; + return cache.remove(entry.getKey(), entry.getValue()); + } + + @Override + public boolean removeIf(Predicate> filter) { + requireNonNull(filter); + boolean removed = false; + for (Entry entry : this) { + if (filter.test(entry)) { + removed |= cache.remove(entry.getKey(), entry.getValue()); + } + } + return removed; + } + + @Override + public Iterator> iterator() { + return new EntryIterator<>(cache); + } + + @Override + public Spliterator> spliterator() { + return new EntrySpliterator<>(cache); + } + } + + /** An adapter to safely externalize the entry iterator. */ + static final class EntryIterator implements Iterator> { + final BoundedLocalCache cache; + final Iterator> iterator; + final long now; + + K key; + V value; + K removalKey; + Node next; + + EntryIterator(BoundedLocalCache cache) { + this.iterator = cache.data.values().iterator(); + this.now = cache.expirationTicker().read(); + this.cache = cache; + } + + @Override + public boolean hasNext() { + if (next != null) { + return true; + } + for (; ; ) { + if (iterator.hasNext()) { + next = iterator.next(); + value = next.getValue(); + key = next.getKey(); + + boolean evictable = cache.hasExpired(next, now) || (key == null) || (value == null); + if (evictable || !next.isAlive()) { + if (evictable) { + cache.scheduleDrainBuffers(); + } + value = null; + next = null; + key = null; + continue; + } + return true; + } + return false; + } + } + + @SuppressWarnings("NullAway") + K nextKey() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + removalKey = key; + value = null; + next = null; + key = null; + return removalKey; + } + + @SuppressWarnings("NullAway") + V nextValue() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + removalKey = key; + V val = value; + value = null; + next = null; + key = null; + return val; + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + @SuppressWarnings("NullAway") + Entry entry = new WriteThroughEntry<>(cache, key, value); + removalKey = key; + value = null; + next = null; + key = null; + return entry; + } + + @Override + public void remove() { + if (removalKey == null) { + throw new IllegalStateException(); + } + cache.remove(removalKey); + removalKey = null; + } + } + + /** An adapter to safely externalize the entry spliterator. */ + static final class EntrySpliterator implements Spliterator> { + final Spliterator> spliterator; + final BoundedLocalCache cache; + + EntrySpliterator(BoundedLocalCache cache) { + this(cache, cache.data.values().spliterator()); + } + + EntrySpliterator(BoundedLocalCache cache, Spliterator> spliterator) { + this.spliterator = requireNonNull(spliterator); + this.cache = requireNonNull(cache); + } + + @Override + public void forEachRemaining(Consumer> action) { + requireNonNull(action); + Consumer> consumer = + node -> { + K key = node.getKey(); + V value = node.getValue(); + long now = cache.expirationTicker().read(); + if ((key != null) + && (value != null) + && node.isAlive() + && !cache.hasExpired(node, now)) { + action.accept(new WriteThroughEntry<>(cache, key, value)); + } + }; + spliterator.forEachRemaining(consumer); + } + + @Override + public boolean tryAdvance(Consumer> action) { + requireNonNull(action); + boolean[] advanced = {false}; + Consumer> consumer = + node -> { + K key = node.getKey(); + V value = node.getValue(); + long now = cache.expirationTicker().read(); + if ((key != null) + && (value != null) + && node.isAlive() + && !cache.hasExpired(node, now)) { + action.accept(new WriteThroughEntry<>(cache, key, value)); + advanced[0] = true; + } + }; + for (; ; ) { + if (spliterator.tryAdvance(consumer)) { + if (advanced[0]) { + return true; + } + continue; + } + return false; + } + } + + @Override + public Spliterator> trySplit() { + Spliterator> split = spliterator.trySplit(); + return (split == null) ? null : new EntrySpliterator<>(cache, split); + } + + @Override + public long estimateSize() { + return spliterator.estimateSize(); + } + + @Override + public int characteristics() { + return Spliterator.DISTINCT | Spliterator.CONCURRENT | Spliterator.NONNULL; + } + } + + /** A reusable task that performs the maintenance work; used to avoid wrapping by ForkJoinPool. */ + static final class PerformCleanupTask extends ForkJoinTask implements Runnable { + private static final long serialVersionUID = 1L; + + final WeakReference> reference; + + PerformCleanupTask(BoundedLocalCache cache) { + reference = new WeakReference>(cache); + } + + @Override + public boolean exec() { + try { + run(); + } catch (Throwable t) { + logger.log(Level.SEVERE, "Exception thrown when performing the maintenance task", t); + } + + // Indicates that the task has not completed to allow subsequent submissions to execute + return false; + } + + @Override + public void run() { + BoundedLocalCache cache = reference.get(); + if (cache != null) { + cache.performCleanUp(/* ignored */ null); + } + } + + /** + * This method cannot be ignored due to being final, so a hostile user supplied Executor could + * forcibly complete the task and halt future executions. There are easier ways to intentionally + * harm a system, so this is assumed to not happen in practice. + */ + // public final void quietlyComplete() {} + + @Override + public Void getRawResult() { + return null; + } + + @Override + public void setRawResult(Void v) {} + + @Override + public void complete(Void value) {} + + @Override + public void completeExceptionally(Throwable ex) {} + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + } + + /** Creates a serialization proxy based on the common configuration shared by all cache types. */ + static SerializationProxy makeSerializationProxy( + BoundedLocalCache cache, boolean isWeighted) { + SerializationProxy proxy = new SerializationProxy<>(); + proxy.weakKeys = cache.collectKeys(); + proxy.weakValues = cache.nodeFactory.weakValues(); + proxy.softValues = cache.nodeFactory.softValues(); + proxy.isRecordingStats = cache.isRecordingStats(); + proxy.removalListener = cache.removalListener(); + proxy.ticker = cache.expirationTicker(); + proxy.writer = cache.writer; + if (cache.expiresAfterAccess()) { + proxy.expiresAfterAccessNanos = cache.expiresAfterAccessNanos(); + } + if (cache.expiresAfterWrite()) { + proxy.expiresAfterWriteNanos = cache.expiresAfterWriteNanos(); + } + if (cache.expiresVariable()) { + proxy.expiry = cache.expiry(); + } + if (cache.evicts()) { + if (isWeighted) { + proxy.weigher = cache.weigher; + proxy.maximumWeight = cache.maximum(); + } else { + proxy.maximumSize = cache.maximum(); + } + } + return proxy; + } + + /* --------------- Manual Cache --------------- */ + + static class BoundedLocalManualCache implements LocalManualCache, Serializable { + private static final long serialVersionUID = 1; + + final BoundedLocalCache cache; + final boolean isWeighted; + + Policy policy; + + BoundedLocalManualCache(Caffeine builder) { + this(builder, null); + } + + BoundedLocalManualCache(Caffeine builder, CacheLoader loader) { + cache = LocalCacheFactory.newBoundedLocalCache(builder, loader, /* async */ false); + isWeighted = builder.isWeighted(); + } + + @Override + public BoundedLocalCache cache() { + return cache; + } + + @Override + public Policy policy() { + return (policy == null) + ? (policy = new BoundedPolicy<>(cache, Function.identity(), isWeighted)) + : policy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + Object writeReplace() { + return makeSerializationProxy(cache, isWeighted); + } + } + + static final class BoundedPolicy implements Policy { + final BoundedLocalCache cache; + final Function transformer; + final boolean isWeighted; + + Optional> eviction; + Optional> refreshes; + Optional> afterWrite; + Optional> afterAccess; + Optional> variable; + + BoundedPolicy(BoundedLocalCache cache, Function transformer, boolean isWeighted) { + this.transformer = transformer; + this.isWeighted = isWeighted; + this.cache = cache; + } + + @Override + public boolean isRecordingStats() { + return cache.isRecordingStats(); + } + + @Override + public V getIfPresentQuietly(Object key) { + Node node = cache.data.get(cache.nodeFactory.newLookupKey(key)); + if ((node == null) || cache.hasExpired(node, cache.expirationTicker().read())) { + return null; + } + return transformer.apply(node.getValue()); + } + + @Override + public Optional> eviction() { + return cache.evicts() + ? (eviction == null) ? (eviction = Optional.of(new BoundedEviction())) : eviction + : Optional.empty(); + } + + @Override + public Optional> expireAfterAccess() { + if (!cache.expiresAfterAccess()) { + return Optional.empty(); + } + return (afterAccess == null) + ? (afterAccess = Optional.of(new BoundedExpireAfterAccess())) + : afterAccess; + } + + @Override + public Optional> expireAfterWrite() { + if (!cache.expiresAfterWrite()) { + return Optional.empty(); + } + return (afterWrite == null) + ? (afterWrite = Optional.of(new BoundedExpireAfterWrite())) + : afterWrite; + } + + @Override + public Optional> expireVariably() { + if (!cache.expiresVariable()) { + return Optional.empty(); + } + return (variable == null) ? (variable = Optional.of(new BoundedVarExpiration())) : variable; + } + + @Override + public Optional> refreshAfterWrite() { + if (!cache.refreshAfterWrite()) { + return Optional.empty(); + } + return (refreshes == null) + ? (refreshes = Optional.of(new BoundedRefreshAfterWrite())) + : refreshes; + } + + final class BoundedEviction implements Eviction { + @Override + public boolean isWeighted() { + return isWeighted; + } + + @Override + public OptionalInt weightOf(K key) { + requireNonNull(key); + if (!isWeighted) { + return OptionalInt.empty(); + } + Node node = cache.data.get(cache.nodeFactory.newLookupKey(key)); + if (node == null) { + return OptionalInt.empty(); + } + synchronized (node) { + return OptionalInt.of(node.getWeight()); + } + } + + @Override + public OptionalLong weightedSize() { + if (cache.evicts() && isWeighted()) { + cache.evictionLock.lock(); + try { + return OptionalLong.of(Math.max(0, cache.weightedSize())); + } finally { + cache.evictionLock.unlock(); + } + } + return OptionalLong.empty(); + } + + @Override + public long getMaximum() { + cache.evictionLock.lock(); + try { + return cache.maximum(); + } finally { + cache.evictionLock.unlock(); + } + } + + @Override + public void setMaximum(long maximum) { + cache.evictionLock.lock(); + try { + cache.setMaximumSize(maximum); + cache.maintenance(/* ignored */ null); + } finally { + cache.evictionLock.unlock(); + } + } + + @Override + public Map coldest(int limit) { + return cache.evictionOrder(limit, transformer, /* hottest */ false); + } + + @Override + public Map hottest(int limit) { + return cache.evictionOrder(limit, transformer, /* hottest */ true); + } + } + + @SuppressWarnings("PreferJavaTimeOverload") + final class BoundedExpireAfterAccess implements Expiration { + @Override + public OptionalLong ageOf(K key, TimeUnit unit) { + requireNonNull(key); + requireNonNull(unit); + Object lookupKey = cache.nodeFactory.newLookupKey(key); + Node node = cache.data.get(lookupKey); + if (node == null) { + return OptionalLong.empty(); + } + long age = cache.expirationTicker().read() - node.getAccessTime(); + return (age > cache.expiresAfterAccessNanos()) + ? OptionalLong.empty() + : OptionalLong.of(unit.convert(age, TimeUnit.NANOSECONDS)); + } + + @Override + public long getExpiresAfter(TimeUnit unit) { + return unit.convert(cache.expiresAfterAccessNanos(), TimeUnit.NANOSECONDS); + } + + @Override + public void setExpiresAfter(long duration, TimeUnit unit) { + requireArgument(duration >= 0); + cache.setExpiresAfterAccessNanos(unit.toNanos(duration)); + cache.scheduleAfterWrite(); + } + + @Override + public Map oldest(int limit) { + return cache.expireAfterAccessOrder(limit, transformer, /* oldest */ true); + } + + @Override + public Map youngest(int limit) { + return cache.expireAfterAccessOrder(limit, transformer, /* oldest */ false); + } + } + + @SuppressWarnings("PreferJavaTimeOverload") + final class BoundedExpireAfterWrite implements Expiration { + @Override + public OptionalLong ageOf(K key, TimeUnit unit) { + requireNonNull(key); + requireNonNull(unit); + Object lookupKey = cache.nodeFactory.newLookupKey(key); + Node node = cache.data.get(lookupKey); + if (node == null) { + return OptionalLong.empty(); + } + long age = cache.expirationTicker().read() - node.getWriteTime(); + return (age > cache.expiresAfterWriteNanos()) + ? OptionalLong.empty() + : OptionalLong.of(unit.convert(age, TimeUnit.NANOSECONDS)); + } + + @Override + public long getExpiresAfter(TimeUnit unit) { + return unit.convert(cache.expiresAfterWriteNanos(), TimeUnit.NANOSECONDS); + } + + @Override + public void setExpiresAfter(long duration, TimeUnit unit) { + requireArgument(duration >= 0); + cache.setExpiresAfterWriteNanos(unit.toNanos(duration)); + cache.scheduleAfterWrite(); + } + + @Override + public Map oldest(int limit) { + return cache.expireAfterWriteOrder(limit, transformer, /* oldest */ true); + } + + @Override + public Map youngest(int limit) { + return cache.expireAfterWriteOrder(limit, transformer, /* oldest */ false); + } + } + + @SuppressWarnings("PreferJavaTimeOverload") + final class BoundedVarExpiration implements VarExpiration { + @Override + public OptionalLong getExpiresAfter(K key, TimeUnit unit) { + requireNonNull(key); + requireNonNull(unit); + Object lookupKey = cache.nodeFactory.newLookupKey(key); + Node node = cache.data.get(lookupKey); + if (node == null) { + return OptionalLong.empty(); + } + long duration = node.getVariableTime() - cache.expirationTicker().read(); + return (duration <= 0) + ? OptionalLong.empty() + : OptionalLong.of(unit.convert(duration, TimeUnit.NANOSECONDS)); + } + + @Override + public void setExpiresAfter(K key, long duration, TimeUnit unit) { + requireNonNull(key); + requireNonNull(unit); + requireArgument(duration >= 0); + Object lookupKey = cache.nodeFactory.newLookupKey(key); + Node node = cache.data.get(lookupKey); + if (node != null) { + long now; + long durationNanos = TimeUnit.NANOSECONDS.convert(duration, unit); + synchronized (node) { + now = cache.expirationTicker().read(); + node.setVariableTime(now + Math.min(durationNanos, MAXIMUM_EXPIRY)); + } + cache.afterRead(node, now, /* recordHit */ false); + } + } + + @Override + public void put(K key, V value, long duration, TimeUnit unit) { + put(key, value, duration, unit, /* onlyIfAbsent */ false); + } + + @Override + public boolean putIfAbsent(K key, V value, long duration, TimeUnit unit) { + V previous = put(key, value, duration, unit, /* onlyIfAbsent */ true); + return (previous == null); + } + + V put(K key, V value, long duration, TimeUnit unit, boolean onlyIfAbsent) { + requireNonNull(unit); + requireNonNull(value); + requireArgument(duration >= 0); + Expiry expiry = + new Expiry() { + @Override + public long expireAfterCreate(K key, V value, long currentTime) { + return unit.toNanos(duration); + } + + @Override + public long expireAfterUpdate( + K key, V value, long currentTime, long currentDuration) { + return unit.toNanos(duration); + } + + @Override + public long expireAfterRead(K key, V value, long currentTime, long currentDuration) { + return currentDuration; + } + }; + if (cache.isAsync) { + @SuppressWarnings("unchecked") + Expiry asyncExpiry = (Expiry) new AsyncExpiry<>(expiry); + expiry = asyncExpiry; + + @SuppressWarnings("unchecked") + V asyncValue = (V) CompletableFuture.completedFuture(value); + value = asyncValue; + } + return cache.put(key, value, expiry, /* notifyWriter */ true, onlyIfAbsent); + } + + @Override + public Map oldest(int limit) { + return cache.variableSnapshot(/* ascending */ true, limit, transformer); + } + + @Override + public Map youngest(int limit) { + return cache.variableSnapshot(/* ascending */ false, limit, transformer); + } + } + + @SuppressWarnings("PreferJavaTimeOverload") + final class BoundedRefreshAfterWrite implements Expiration { + @Override + public OptionalLong ageOf(K key, TimeUnit unit) { + requireNonNull(key); + requireNonNull(unit); + Object lookupKey = cache.nodeFactory.newLookupKey(key); + Node node = cache.data.get(lookupKey); + if (node == null) { + return OptionalLong.empty(); + } + long age = cache.expirationTicker().read() - node.getWriteTime(); + return (age > cache.refreshAfterWriteNanos()) + ? OptionalLong.empty() + : OptionalLong.of(unit.convert(age, TimeUnit.NANOSECONDS)); + } + + @Override + public long getExpiresAfter(TimeUnit unit) { + return unit.convert(cache.refreshAfterWriteNanos(), TimeUnit.NANOSECONDS); + } + + @Override + public void setExpiresAfter(long duration, TimeUnit unit) { + requireArgument(duration >= 0); + cache.setRefreshAfterWriteNanos(unit.toNanos(duration)); + cache.scheduleAfterWrite(); + } + + @SuppressWarnings("PMD.SimplifiedTernary") // false positive (#1424) + @Override + public Map oldest(int limit) { + return cache.expiresAfterWrite() + ? expireAfterWrite().get().oldest(limit) + : sortedByWriteTime(limit, /* ascending */ true); + } + + @SuppressWarnings("PMD.SimplifiedTernary") // false positive (#1424) + @Override + public Map youngest(int limit) { + return cache.expiresAfterWrite() + ? expireAfterWrite().get().youngest(limit) + : sortedByWriteTime(limit, /* ascending */ false); + } + + Map sortedByWriteTime(int limit, boolean ascending) { + Comparator> comparator = Comparator.comparingLong(Node::getWriteTime); + Iterator> iterator = + cache.data.values().stream() + .parallel() + .sorted(ascending ? comparator : comparator.reversed()) + .limit(limit) + .iterator(); + return cache.fixedSnapshot(() -> iterator, limit, transformer); + } + } + } + + /* --------------- Loading Cache --------------- */ + + static final class BoundedLocalLoadingCache extends BoundedLocalManualCache + implements LocalLoadingCache { + private static final long serialVersionUID = 1; + + final Function mappingFunction; + final Function, Map> bulkMappingFunction; + + BoundedLocalLoadingCache(Caffeine builder, CacheLoader loader) { + super(builder, loader); + requireNonNull(loader); + mappingFunction = newMappingFunction(loader); + bulkMappingFunction = newBulkMappingFunction(loader); + } + + @Override + @SuppressWarnings("NullAway") + public CacheLoader cacheLoader() { + return cache.cacheLoader; + } + + @Override + public Function mappingFunction() { + return mappingFunction; + } + + @Override + public Function, Map> bulkMappingFunction() { + return bulkMappingFunction; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + @Override + Object writeReplace() { + @SuppressWarnings("unchecked") + SerializationProxy proxy = (SerializationProxy) super.writeReplace(); + if (cache.refreshAfterWrite()) { + proxy.refreshAfterWriteNanos = cache.refreshAfterWriteNanos(); + } + proxy.loader = cache.cacheLoader; + return proxy; + } + } + + /* --------------- Async Cache --------------- */ + + static final class BoundedLocalAsyncCache implements LocalAsyncCache, Serializable { + private static final long serialVersionUID = 1; + + final BoundedLocalCache> cache; + final boolean isWeighted; + + ConcurrentMap> mapView; + CacheView cacheView; + Policy policy; + + @SuppressWarnings("unchecked") + BoundedLocalAsyncCache(Caffeine builder) { + cache = + (BoundedLocalCache>) + LocalCacheFactory.newBoundedLocalCache(builder, /* loader */ null, /* async */ true); + isWeighted = builder.isWeighted(); + } + + @Override + public BoundedLocalCache> cache() { + return cache; + } + + @Override + public ConcurrentMap> asMap() { + return (mapView == null) ? (mapView = new AsyncAsMapView<>(this)) : mapView; + } + + @Override + public Cache synchronous() { + return (cacheView == null) ? (cacheView = new CacheView<>(this)) : cacheView; + } + + @Override + public Policy policy() { + if (policy == null) { + @SuppressWarnings("unchecked") + BoundedLocalCache castCache = (BoundedLocalCache) cache; + Function, V> transformer = Async::getIfReady; + @SuppressWarnings("unchecked") + Function castTransformer = (Function) transformer; + policy = new BoundedPolicy<>(castCache, castTransformer, isWeighted); + } + return policy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + Object writeReplace() { + SerializationProxy proxy = makeSerializationProxy(cache, isWeighted); + if (cache.refreshAfterWrite()) { + proxy.refreshAfterWriteNanos = cache.refreshAfterWriteNanos(); + } + proxy.async = true; + return proxy; + } + } + + /* --------------- Async Loading Cache --------------- */ + + static final class BoundedLocalAsyncLoadingCache extends LocalAsyncLoadingCache + implements Serializable { + private static final long serialVersionUID = 1; + + final BoundedLocalCache> cache; + final boolean isWeighted; + + ConcurrentMap> mapView; + Policy policy; + + @SuppressWarnings("unchecked") + BoundedLocalAsyncLoadingCache(Caffeine builder, AsyncCacheLoader loader) { + super(loader); + isWeighted = builder.isWeighted(); + cache = + (BoundedLocalCache>) + LocalCacheFactory.newBoundedLocalCache( + builder, new AsyncLoader<>(loader, builder), /* async */ true); + } + + @Override + public BoundedLocalCache> cache() { + return cache; + } + + @Override + public ConcurrentMap> asMap() { + return (mapView == null) ? (mapView = new AsyncAsMapView<>(this)) : mapView; + } + + @Override + public Policy policy() { + if (policy == null) { + @SuppressWarnings("unchecked") + BoundedLocalCache castCache = (BoundedLocalCache) cache; + Function, V> transformer = Async::getIfReady; + @SuppressWarnings("unchecked") + Function castTransformer = (Function) transformer; + policy = new BoundedPolicy<>(castCache, castTransformer, isWeighted); + } + return policy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + Object writeReplace() { + SerializationProxy proxy = makeSerializationProxy(cache, isWeighted); + if (cache.refreshAfterWrite()) { + proxy.refreshAfterWriteNanos = cache.refreshAfterWriteNanos(); + } + proxy.loader = loader; + proxy.async = true; + return proxy; + } + + static final class AsyncLoader implements CacheLoader { + final AsyncCacheLoader loader; + final Executor executor; + + AsyncLoader(AsyncCacheLoader loader, Caffeine builder) { + this.executor = requireNonNull(builder.getExecutor()); + this.loader = requireNonNull(loader); + } + + @Override + public V load(K key) { + @SuppressWarnings("unchecked") + V newValue = (V) loader.asyncLoad(key, executor); + return newValue; + } + + @Override + public V reload(K key, V oldValue) { + @SuppressWarnings("unchecked") + V newValue = (V) loader.asyncReload(key, oldValue, executor); + return newValue; + } + + @Override + public CompletableFuture asyncReload(K key, V oldValue, Executor executor) { + return loader.asyncReload(key, oldValue, executor); + } + } + } +} + +/** The namespace for field padding through inheritance. */ +final class BLCHeader { + + abstract static class PadDrainStatus extends AbstractMap { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; + } + + /** Enforces a memory layout to avoid false sharing by padding the drain status. */ + abstract static class DrainStatusRef extends PadDrainStatus { + static final long DRAIN_STATUS_OFFSET = + UnsafeAccess.objectFieldOffset(DrainStatusRef.class, "drainStatus"); + + /** A drain is not taking place. */ + static final int IDLE = 0; + + /** A drain is required due to a pending write modification. */ + static final int REQUIRED = 1; + + /** A drain is in progress and will transition to idle. */ + static final int PROCESSING_TO_IDLE = 2; + + /** A drain is in progress and will transition to required. */ + static final int PROCESSING_TO_REQUIRED = 3; + + /** The draining status of the buffers. */ + volatile int drainStatus = IDLE; + + /** + * Returns whether maintenance work is needed. + * + * @param delayable if draining the read buffer can be delayed + */ + boolean shouldDrainBuffers(boolean delayable) { + switch (drainStatus()) { + case IDLE: + return !delayable; + case REQUIRED: + return true; + case PROCESSING_TO_IDLE: + case PROCESSING_TO_REQUIRED: + return false; + default: + throw new IllegalStateException(); + } + } + + int drainStatus() { + return UnsafeAccess.UNSAFE.getInt(this, DRAIN_STATUS_OFFSET); + } + + void lazySetDrainStatus(int drainStatus) { + UnsafeAccess.UNSAFE.putOrderedInt(this, DRAIN_STATUS_OFFSET, drainStatus); + } + + boolean casDrainStatus(int expect, int update) { + return UnsafeAccess.UNSAFE.compareAndSwapInt(this, DRAIN_STATUS_OFFSET, expect, update); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Buffer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Buffer.java new file mode 100644 index 0000000000000..a042081d11401 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Buffer.java @@ -0,0 +1,114 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.function.Consumer; + +/** + * A multiple-producer / single-consumer buffer that rejects new elements if it is full or fails + * spuriously due to contention. Unlike a queue and stack, a buffer does not guarantee an ordering + * of elements in either FIFO or LIFO order. + * + *

Beware that it is the responsibility of the caller to ensure that a consumer has exclusive + * read access to the buffer. This implementation does not include fail-fast behavior to + * guard against incorrect consumer usage. + * + * @param the type of elements maintained by this buffer + * @author ben.manes@gmail.com (Ben Manes) + */ +interface Buffer { + int FULL = 1; + int SUCCESS = 0; + int FAILED = -1; + + /** Returns a no-op implementation. */ + @SuppressWarnings("unchecked") + static Buffer disabled() { + return (Buffer) DisabledBuffer.INSTANCE; + } + + /** + * Inserts the specified element into this buffer if it is possible to do so immediately without + * violating capacity restrictions. The addition is allowed to fail spuriously if multiple threads + * insert concurrently. + * + * @param e the element to add + * @return {@code 1} if the buffer is full, {@code -1} if the CAS failed, or {@code 0} if added + */ + int offer(E e); + + /** + * Drains the buffer, sending each element to the consumer for processing. The caller must ensure + * that a consumer has exclusive read access to the buffer. + * + * @param consumer the action to perform on each element + */ + void drainTo(Consumer consumer); + + /** + * Returns the number of elements residing in the buffer. + * + * @return the number of elements in this buffer + */ + default int size() { + return writes() - reads(); + } + + /** + * Returns the number of elements that have been read from the buffer. + * + * @return the number of elements read from this buffer + */ + int reads(); + + /** + * Returns the number of elements that have been written to the buffer. + * + * @return the number of elements written to this buffer + */ + int writes(); +} + +enum DisabledBuffer implements Buffer { + INSTANCE; + + @Override + public int offer(Object e) { + return Buffer.SUCCESS; + } + + @Override + public void drainTo(Consumer consumer) {} + + @Override + public int size() { + return 0; + } + + @Override + public int reads() { + return 0; + } + + @Override + public int writes() { + return 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Cache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Cache.java new file mode 100644 index 0000000000000..0a4b879423685 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Cache.java @@ -0,0 +1,227 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.CacheStats; + +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; + +/** + * A semi-persistent mapping from keys to values. Cache entries are manually added using {@link + * #get(Object, Function)} or {@link #put(Object, Object)}, and are stored in the cache until either + * evicted or manually invalidated. + * + *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of keys maintained by this cache + * @param the type of mapped values + */ +public interface Cache { + + /** + * Returns the value associated with the {@code key} in this cache, or {@code null} if there is no + * cached value for the {@code key}. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or {@code null} if this cache contains + * no mapping for the key + * @throws NullPointerException if the specified key is null + */ + V getIfPresent(Object key); + + /** + * Returns the value associated with the {@code key} in this cache, obtaining that value from the + * {@code mappingFunction} if necessary. This method provides a simple substitute for the + * conventional "if cached, return; otherwise create, cache and return" pattern. + * + *

If the specified key is not already associated with a value, attempts to compute its value + * using the given mapping function and enters it into this cache unless {@code null}. The entire + * method invocation is performed atomically, so the function is applied at most once per key. + * Some attempted update operations on this cache by other threads may be blocked while the + * computation is in progress, so the computation should be short and simple, and must not attempt + * to update any other mappings of this cache. + * + *

Warning: as with {@link CacheLoader#load}, {@code mappingFunction} must not + * attempt to update any other mappings of this cache. + * + * @param key the key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with the specified key, or null if + * the computed value is null + * @throws NullPointerException if the specified key or mappingFunction is null + * @throws IllegalStateException if the computation detectably attempts a recursive update to this + * cache that would otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, in which case the mapping is + * left unestablished + */ + V get(K key, Function mappingFunction); + + /** + * Returns a map of the values associated with the {@code keys} in this cache. The returned map + * will only contain entries which are already present in the cache. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @param keys the keys whose associated values are to be returned + * @return the unmodifiable mapping of keys to values for the specified keys found in this cache + * @throws NullPointerException if the specified collection is null or contains a null element + */ + Map getAllPresent(Iterable keys); + + /** + * Returns a map of the values associated with the {@code keys}, creating or retrieving those + * values if necessary. The returned map contains entries that were already cached, combined with + * the newly loaded entries; it will never contain null keys or values. + * + *

A single request to the {@code mappingFunction} is performed for all keys which are not + * already present in the cache. All entries returned by {@code mappingFunction} will be stored in + * the cache, over-writing any previously cached values. If another call to {@link #get} tries to + * load the value for a key in {@code keys}, implementations may either have that thread load the + * entry or simply wait for this thread to finish and return the loaded value. In the case of + * overlapping non-blocking loads, the last load to complete will replace the existing entry. Note + * that multiple threads can concurrently load values for distinct keys. Any loaded values for + * keys that were not specifically requested will not be returned, but will be stored in the + * cache. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @param keys the keys whose associated values are to be returned + * @param mappingFunction the function to compute the values + * @return an unmodifiable mapping of keys to values for the specified keys in this cache + * @throws NullPointerException if the specified collection is null or contains a null element, or + * if the map returned by the mappingFunction is null + * @throws RuntimeException or Error if the mappingFunction does so, in which case the mapping is + * left unestablished + */ + default Map getAll( + Iterable keys, Function, Map> mappingFunction) { + // This method was added & implemented in version 2.8.0 + throw new UnsupportedOperationException(); + } + + /** + * Associates the {@code value} with the {@code key} in this cache. If the cache previously + * contained a value associated with the {@code key}, the old value is replaced by the new {@code + * value}. + * + *

Prefer {@link #get(Object, Function)} when using the conventional "if cached, return; + * otherwise create, cache and return" pattern. + * + * @param key the key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @throws NullPointerException if the specified key or value is null + */ + void put(K key, V value); + + /** + * Copies all of the mappings from the specified map to the cache. The effect of this call is + * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key + * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined + * if the specified map is modified while the operation is in progress. + * + * @param map the mappings to be stored in this cache + * @throws NullPointerException if the specified map is null or the specified map contains null + * keys or values + */ + void putAll(Map map); + + /** + * Discards any cached value for the {@code key}. The behavior of this operation is undefined for + * an entry that is being loaded (or reloaded) and is otherwise not present. + * + * @param key the key whose mapping is to be removed from the cache + * @throws NullPointerException if the specified key is null + */ + void invalidate(Object key); + + /** + * Discards any cached values for the {@code keys}. The behavior of this operation is undefined + * for an entry that is being loaded (or reloaded) and is otherwise not present. + * + * @param keys the keys whose associated values are to be removed + * @throws NullPointerException if the specified collection is null or contains a null element + */ + void invalidateAll(Iterable keys); + + /** + * Discards all entries in the cache. The behavior of this operation is undefined for an entry + * that is being loaded (or reloaded) and is otherwise not present. + */ + void invalidateAll(); + + /** + * Returns the approximate number of entries in this cache. The value returned is an estimate; the + * actual count may differ if there are concurrent insertions or removals, or if some entries are + * pending removal due to expiration or weak/soft reference collection. In the case of stale + * entries this inaccuracy can be mitigated by performing a {@link #cleanUp()} first. + * + * @return the estimated number of mappings + */ + long estimatedSize(); + + /** + * Returns a current snapshot of this cache's cumulative statistics. All statistics are + * initialized to zero, and are monotonically increasing over the lifetime of the cache. + * + *

Due to the performance penalty of maintaining statistics, some implementations may not + * record the usage history immediately or at all. + * + * @return the current snapshot of the statistics of this cache + */ + CacheStats stats(); + + /** + * Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to + * the map directly affect the cache. + * + *

A computation operation, such as {@link ConcurrentMap#compute}, performs the entire method + * invocation atomically, so the function is applied at most once per key. Some attempted update + * operations by other threads may be blocked while computation is in progress. The computation + * must not attempt to update any other mappings of this cache. + * + *

Iterators from the returned map are at least weakly consistent: they are safe for + * concurrent use, but if the cache is modified (including by eviction) after the iterator is + * created, it is undefined which of the changes (if any) will be reflected in that iterator. + * + * @return a thread-safe view of this cache supporting all of the optional {@link Map} operations + */ + ConcurrentMap asMap(); + + /** + * Performs any pending maintenance operations needed by the cache. Exactly which activities are + * performed -- if any -- is implementation-dependent. + */ + void cleanUp(); + + /** + * Returns access to inspect and perform low-level operations on this cache based on its runtime + * characteristics. These operations are optional and dependent on how the cache was constructed + * and what abilities the implementation exposes. + * + * @return access to inspect and perform advanced operations based on the cache's characteristics + */ + Policy policy(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CacheLoader.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CacheLoader.java new file mode 100644 index 0000000000000..1ca0e96548393 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CacheLoader.java @@ -0,0 +1,202 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; + +import static java.util.Objects.requireNonNull; + +/** + * Computes or retrieves values, based on a key, for use in populating a {@link LoadingCache} or + * {@link AsyncLoadingCache}. + * + *

Most implementations will only need to implement {@link #load}. Other methods may be + * overridden as desired. + * + *

Usage example: + * + *

{@code
+ * CacheLoader loader = key -> createExpensiveGraph(key);
+ * LoadingCache cache = Caffeine.newBuilder().build(loader);
+ * }
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@FunctionalInterface +@SuppressWarnings({"PMD.SignatureDeclareThrowsException", "FunctionalInterfaceMethodChanged"}) +public interface CacheLoader extends AsyncCacheLoader { + + /** + * Computes or retrieves the value corresponding to {@code key}. + * + *

Warning: loading must not attempt to update any mappings of this cache + * directly. + * + * @param key the non-null key whose value should be loaded + * @return the value associated with {@code key} or {@code null} if not found + * @throws Exception or Error, in which case the mapping is unchanged + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + */ + V load(K key) throws Exception; + + /** + * Computes or retrieves the values corresponding to {@code keys}. This method is called by {@link + * LoadingCache#getAll}. + * + *

If the returned map doesn't contain all requested {@code keys} then the entries it does + * contain will be cached and {@code getAll} will return the partial results. If the returned map + * contains extra keys not present in {@code keys} then all returned entries will be cached, but + * only the entries for {@code keys} will be returned from {@code getAll}. + * + *

This method should be overridden when bulk retrieval is significantly more efficient than + * many individual lookups. Note that {@link LoadingCache#getAll} will defer to individual calls + * to {@link LoadingCache#get} if this method is not overridden. + * + *

Warning: loading must not attempt to update any mappings of this cache + * directly. + * + * @param keys the unique, non-null keys whose values should be loaded + * @return a map from each key in {@code keys} to the value associated with that key; may not + * contain null values + * @throws Exception or Error, in which case the mappings are unchanged + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + */ + default Map loadAll(Iterable keys) throws Exception { + throw new UnsupportedOperationException(); + } + + /** + * Asynchronously computes or retrieves the value corresponding to {@code key}. + * + * @param key the non-null key whose value should be loaded + * @param executor the executor that asynchronously loads the entry + * @return the future value associated with {@code key} + */ + @Override + default CompletableFuture asyncLoad(K key, Executor executor) { + requireNonNull(key); + requireNonNull(executor); + return CompletableFuture.supplyAsync( + () -> { + try { + return load(key); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CompletionException(e); + } + }, + executor); + } + + /** + * Asynchronously computes or retrieves the values corresponding to {@code keys}. This method is + * called by {@link AsyncLoadingCache#getAll}. + * + *

If the returned map doesn't contain all requested {@code keys} then the entries it does + * contain will be cached and {@code getAll} will return the partial results. If the returned map + * contains extra keys not present in {@code keys} then all returned entries will be cached, but + * only the entries for {@code keys} will be returned from {@code getAll}. + * + *

This method should be overridden when bulk retrieval is significantly more efficient than + * many individual lookups. Note that {@link AsyncLoadingCache#getAll} will defer to individual + * calls to {@link AsyncLoadingCache#get} if this method is not overridden. + * + * @param keys the unique, non-null keys whose values should be loaded + * @param executor the executor that with asynchronously loads the entries + * @return a future containing the map from each key in {@code keys} to the value associated with + * that key; may not contain null values + */ + @Override + default CompletableFuture> asyncLoadAll(Iterable keys, Executor executor) { + requireNonNull(keys); + requireNonNull(executor); + return CompletableFuture.supplyAsync( + () -> { + try { + return loadAll(keys); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CompletionException(e); + } + }, + executor); + } + + /** + * Computes or retrieves a replacement value corresponding to an already-cached {@code key}. If + * the replacement value is not found then the mapping will be removed if {@code null} is + * returned. This method is called when an existing cache entry is refreshed by {@link + * Caffeine#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}. + * + *

Note: all exceptions thrown by this method will be logged and then swallowed. + * + * @param key the non-null key whose value should be loaded + * @param oldValue the non-null old value corresponding to {@code key} + * @return the new value associated with {@code key}, or {@code null} if the mapping is to be + * removed + * @throws Exception or Error, in which case the mapping is unchanged + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + */ + default V reload(K key, V oldValue) throws Exception { + return load(key); + } + + /** + * Asynchronously computes or retrieves a replacement value corresponding to an already-cached + * {@code key}. If the replacement value is not found then the mapping will be removed if {@code + * null} is computed. This method is called when an existing cache entry is refreshed by {@link + * Caffeine#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}. + * + *

Note: all exceptions thrown by this method will be logged and then swallowed. + * + * @param key the non-null key whose value should be loaded + * @param oldValue the non-null old value corresponding to {@code key} + * @param executor the executor with which the entry is asynchronously loaded + * @return a future containing the new value associated with {@code key}, or containing {@code + * null} if the mapping is to be removed + */ + @Override + default CompletableFuture asyncReload(K key, V oldValue, Executor executor) { + requireNonNull(key); + requireNonNull(executor); + return CompletableFuture.supplyAsync( + () -> { + try { + return reload(key, oldValue); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CompletionException(e); + } + }, + executor); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CacheWriter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CacheWriter.java new file mode 100644 index 0000000000000..16ef15b2eaeeb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CacheWriter.java @@ -0,0 +1,86 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Map; + +/** + * Communicates the write or deletion of a value, based on a key, to an external resource. A writer + * is notified by the cache each time an entry is explicitly created or modified, or removed for any + * {@link RemovalCause reason}. The writer is not notified when an entry is loaded or computed. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the most general type of keys this writer can write; for example {@code Object} if any + * key is acceptable + * @param the most general type of values this write can write; for example {@code Object} if + * any value is acceptable + * @deprecated Scheduled for removal in version 3.0.0. Consider instead using {@link Map} compute + * methods for extending manual write and remove operations, and using {@link + * Caffeine#evictionListener(RemovalListener)} for extending removals due to eviction. + */ +@Deprecated +public interface CacheWriter { + + /*** + * Writes the value corresponding to the {@code key} to the external resource. The cache will + * communicate a write when an entry in the cache is created or modified, except when that was + * due to a load or computation. + * + * @param key the non-null key whose value should be written + * @param value the value associated with {@code key} that should be written + * @throws RuntimeException or Error, in which case the mapping is unchanged + */ + void write(K key, V value); + + /** + * Deletes the value corresponding to the {@code key} from the external resource. The cache will + * communicate a delete when the entry is explicitly removed or evicted. + * + * @param key the non-null key whose value was removed + * @param value the value associated with {@code key}, or {@code null} if collected + * @param cause the reason for which the entry was removed + * @throws RuntimeException or Error, in which case the mapping is unchanged + */ + void delete(K key, V value, RemovalCause cause); + + /** + * Returns a writer that does nothing. + * + * @param the type of keys + * @param the type of values + * @return a writer that performs no operations + */ + static CacheWriter disabledWriter() { + @SuppressWarnings("unchecked") + CacheWriter writer = (CacheWriter) DisabledWriter.INSTANCE; + return writer; + } +} + +@SuppressWarnings("deprecation") +enum DisabledWriter implements CacheWriter { + INSTANCE; + + @Override + public void write(Object key, Object value) {} + + @Override + public void delete(Object key, Object value, RemovalCause cause) {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Caffeine.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Caffeine.java new file mode 100644 index 0000000000000..eb6e7402caea2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Caffeine.java @@ -0,0 +1,1312 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Async.AsyncExpiry; +import org.apache.iotdb.github.benmanes.caffeine.cache.Async.AsyncRemovalListener; +import org.apache.iotdb.github.benmanes.caffeine.cache.Async.AsyncWeigher; +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.CacheStats; +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.ConcurrentStatsCounter; +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +import java.io.Serializable; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.time.Duration; +import java.util.ConcurrentModificationException; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Locale.US; +import static java.util.Objects.requireNonNull; + +/** + * A builder of {@link Cache}, {@link LoadingCache}, {@link AsyncCache}, and {@link + * AsyncLoadingCache} instances having a combination of the following features: + * + *

    + *
  • automatic loading of entries into the cache, optionally asynchronously + *
  • size-based eviction when a maximum is exceeded based on frequency and recency + *
  • time-based expiration of entries, measured since last access or last write + *
  • asynchronously refresh when the first stale request for an entry occurs + *
  • keys automatically wrapped in {@linkplain WeakReference weak} references + *
  • values automatically wrapped in {@linkplain WeakReference weak} or {@linkplain + * SoftReference soft} references + *
  • writes propagated to an external resource + *
  • notification of evicted (or otherwise removed) entries + *
  • accumulation of cache access statistics + *
+ * + *

These features are all optional; caches can be created using all or none of them. By default + * cache instances created by {@code Caffeine} will not perform any type of eviction. + * + *

Usage example: + * + *

{@code
+ * LoadingCache graphs = Caffeine.newBuilder()
+ *     .maximumSize(10_000)
+ *     .expireAfterWrite(Duration.ofMinutes(10))
+ *     .removalListener((Key key, Graph graph, RemovalCause cause) ->
+ *         System.out.printf("Key %s was removed (%s)%n", key, cause))
+ *     .build(key -> createExpensiveGraph(key));
+ * }
+ * + *

The returned cache is implemented as a hash table with similar performance characteristics to + * {@link ConcurrentHashMap}. The {@code asMap} view (and its collection views) have weakly + * consistent iterators. This means that they are safe for concurrent use, but if other threads + * modify the cache after the iterator is created, it is undefined which of these changes, if any, + * are reflected in that iterator. These iterators never throw {@link + * ConcurrentModificationException}. + * + *

Note: by default, the returned cache uses equality comparisons (the {@link + * Object#equals equals} method) to determine equality for keys or values. However, if {@link + * #weakKeys} was specified, the cache uses identity ({@code ==}) comparisons instead for keys. + * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the cache uses identity + * comparisons for values. + * + *

Entries are automatically evicted from the cache when any of {@linkplain #maximumSize(long) + * maximumSize}, {@linkplain #maximumWeight(long) maximumWeight}, {@linkplain #expireAfter(Expiry) + * expireAfter}, {@linkplain #expireAfterWrite expireAfterWrite}, {@linkplain #expireAfterAccess + * expireAfterAccess}, {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues weakValues}, or + * {@linkplain #softValues softValues} are requested. + * + *

If {@linkplain #maximumSize(long) maximumSize} or {@linkplain #maximumWeight(long) + * maximumWeight} is requested entries may be evicted on each cache modification. + * + *

If {@linkplain #expireAfter(Expiry) expireAfter}, {@linkplain #expireAfterWrite + * expireAfterWrite}, or {@linkplain #expireAfterAccess expireAfterAccess} is requested then entries + * may be evicted on each cache modification, on occasional cache accesses, or on calls to {@link + * Cache#cleanUp}. A {@linkplain #scheduler(Scheduler)} may be specified to provide prompt removal + * of expired entries rather than waiting until activity triggers the periodic maintenance. Expired + * entries may be counted by {@link Cache#estimatedSize()}, but will never be visible to read or + * write operations. + * + *

If {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues weakValues}, or {@linkplain + * #softValues softValues} are requested, it is possible for a key or value present in the cache to + * be reclaimed by the garbage collector. Entries with reclaimed keys or values may be removed from + * the cache on each cache modification, on occasional cache accesses, or on calls to {@link + * Cache#cleanUp}; such entries may be counted in {@link Cache#estimatedSize()}, but will never be + * visible to read or write operations. + * + *

Certain cache configurations will result in the accrual of periodic maintenance tasks which + * will be performed during write operations, or during occasional read operations in the absence of + * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but + * calling it should not be necessary with a high throughput cache. Only caches built with + * {@linkplain #maximumSize maximumSize}, {@linkplain #maximumWeight maximumWeight}, {@linkplain + * #expireAfter(Expiry) expireAfter}, {@linkplain #expireAfterWrite expireAfterWrite}, {@linkplain + * #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues + * weakValues}, or {@linkplain #softValues softValues} perform periodic maintenance. + * + *

The caches produced by {@code Caffeine} are serializable, and the deserialized caches retain + * all the configuration properties of the original cache. Note that the serialized form does + * not include cache contents, but only configuration. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the most general key type this builder will be able to create caches for. This is + * normally {@code Object} unless it is constrained by using a method like {@code + * #removalListener} + * @param the most general value type this builder will be able to create caches for. This is + * normally {@code Object} unless it is constrained by using a method like {@code + * #removalListener} + */ +public final class Caffeine { + static final Logger logger = Logger.getLogger(Caffeine.class.getName()); + static final Supplier ENABLED_STATS_COUNTER_SUPPLIER = ConcurrentStatsCounter::new; + + enum Strength { + WEAK, + SOFT + } + + static final int UNSET_INT = -1; + + static final int DEFAULT_INITIAL_CAPACITY = 16; + static final int DEFAULT_EXPIRATION_NANOS = 0; + static final int DEFAULT_REFRESH_NANOS = 0; + + boolean strictParsing = true; + + long maximumSize = UNSET_INT; + long maximumWeight = UNSET_INT; + int initialCapacity = UNSET_INT; + + long expireAfterWriteNanos = UNSET_INT; + long expireAfterAccessNanos = UNSET_INT; + long refreshAfterWriteNanos = UNSET_INT; + + RemovalListener evictionListener; + RemovalListener removalListener; + Supplier statsCounterSupplier; + + @SuppressWarnings("deprecation") + CacheWriter writer; + + Weigher weigher; + Expiry expiry; + Scheduler scheduler; + Executor executor; + Ticker ticker; + + Strength keyStrength; + Strength valueStrength; + + private Caffeine() {} + + /** Ensures that the argument expression is true. */ + static void requireArgument(boolean expression, String template, Object... args) { + if (!expression) { + throw new IllegalArgumentException(String.format(template, args)); + } + } + + /** Ensures that the argument expression is true. */ + static void requireArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** Ensures that the state expression is true. */ + static void requireState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** Ensures that the state expression is true. */ + static void requireState(boolean expression, String template, Object... args) { + if (!expression) { + throw new IllegalStateException(String.format(template, args)); + } + } + + /** Returns the smallest power of two greater than or equal to {@code x}. */ + static int ceilingPowerOfTwo(int x) { + // From Hacker's Delight, Chapter 3, Harry S. Warren Jr. + return 1 << -Integer.numberOfLeadingZeros(x - 1); + } + + /** Returns the smallest power of two greater than or equal to {@code x}. */ + static long ceilingPowerOfTwo(long x) { + // From Hacker's Delight, Chapter 3, Harry S. Warren Jr. + return 1L << -Long.numberOfLeadingZeros(x - 1); + } + + /** + * Constructs a new {@code Caffeine} instance with default settings, including strong keys, strong + * values, and no automatic eviction of any kind. + * + *

Note that while this return type is {@code Caffeine}, type parameters on the + * {@link #build} methods allow you to create a cache of any key and value type desired. + * + * @return a new instance with default settings + */ + public static Caffeine newBuilder() { + return new Caffeine<>(); + } + + /** + * Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}. + * + * @param spec the specification to build from + * @return a new instance with the specification's settings + */ + public static Caffeine from(CaffeineSpec spec) { + Caffeine builder = spec.toBuilder(); + builder.strictParsing = false; + return builder; + } + + /** + * Constructs a new {@code Caffeine} instance with the settings specified in {@code spec}. + * + * @param spec a String in the format specified by {@link CaffeineSpec} + * @return a new instance with the specification's settings + */ + public static Caffeine from(String spec) { + return from(CaffeineSpec.parse(spec)); + } + + /** + * Sets the minimum total size for the internal data structures. Providing a large enough estimate + * at construction time avoids the need for expensive resizing operations later, but setting this + * value unnecessarily high wastes memory. + * + * @param initialCapacity minimum total size for the internal data structures + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code initialCapacity} is negative + * @throws IllegalStateException if an initial capacity was already set + */ + public Caffeine initialCapacity(int initialCapacity) { + requireState( + this.initialCapacity == UNSET_INT, + "initial capacity was already set to %s", + this.initialCapacity); + requireArgument(initialCapacity >= 0); + this.initialCapacity = initialCapacity; + return this; + } + + boolean hasInitialCapacity() { + return (initialCapacity != UNSET_INT); + } + + int getInitialCapacity() { + return hasInitialCapacity() ? initialCapacity : DEFAULT_INITIAL_CAPACITY; + } + + /** + * Specifies the executor to use when running asynchronous tasks. The executor is delegated to + * when sending removal notifications, when asynchronous computations are performed by {@link + * AsyncCache} or {@link LoadingCache#refresh} or {@link #refreshAfterWrite}, or when performing + * periodic maintenance. By default, {@link ForkJoinPool#commonPool()} is used. + * + *

The primary intent of this method is to facilitate testing of caches which have been + * configured with {@link #removalListener} or utilize asynchronous computations. A test may + * instead prefer to configure the cache to execute tasks directly on the same thread. + * + *

Beware that configuring a cache with an executor that discards tasks or never runs them may + * experience non-deterministic behavior. + * + * @param executor the executor to use for asynchronous execution + * @return this {@code Caffeine} instance (for chaining) + * @throws NullPointerException if the specified executor is null + */ + public Caffeine executor(Executor executor) { + requireState(this.executor == null, "executor was already set to %s", this.executor); + this.executor = requireNonNull(executor); + return this; + } + + Executor getExecutor() { + return (executor == null) ? ForkJoinPool.commonPool() : executor; + } + + /** + * Specifies the scheduler to use when scheduling routine maintenance based on an expiration + * event. This augments the periodic maintenance that occurs during normal cache operations to + * allow for the prompt removal of expired entries regardless of whether any cache activity is + * occurring at that time. By default, {@link Scheduler#disabledScheduler()} is used. + * + *

The scheduling between expiration events is paced to exploit batching and to minimize + * executions in short succession. This minimum difference between the scheduled executions is + * implementation-specific, currently at ~1 second (2^30 ns). In addition, the provided scheduler + * may not offer real-time guarantees (including {@link ScheduledThreadPoolExecutor}). The + * scheduling is best-effort and does not make any hard guarantees of when an expired entry will + * be removed. + * + *

Note for Java 9 and later: consider using {@link Scheduler#systemScheduler()} to + * leverage the dedicated, system-wide scheduling thread. + * + * @param scheduler the scheduler that submits a task to the {@link #executor(Executor)} after a + * given delay + * @return this {@code Caffeine} instance (for chaining) + * @throws NullPointerException if the specified scheduler is null + */ + public Caffeine scheduler(Scheduler scheduler) { + requireState(this.scheduler == null, "scheduler was already set to %s", this.scheduler); + this.scheduler = requireNonNull(scheduler); + return this; + } + + Scheduler getScheduler() { + if ((scheduler == null) || (scheduler == Scheduler.disabledScheduler())) { + return Scheduler.disabledScheduler(); + } else if (scheduler == Scheduler.systemScheduler()) { + return scheduler; + } + return Scheduler.guardedScheduler(scheduler); + } + + /** + * Specifies the maximum number of entries the cache may contain. Note that the cache may evict + * an entry before this limit is exceeded or temporarily exceed the threshold while evicting. + * As the cache size grows close to the maximum, the cache evicts entries that are less likely to + * be used again. For example, the cache may evict an entry because it hasn't been used recently + * or very often. + * + *

When {@code size} is zero, elements will be evicted immediately after being loaded into the + * cache. This can be useful in testing, or to disable caching temporarily without a code change. + * As eviction is scheduled on the configured {@link #executor}, tests may instead prefer to + * configure the cache to execute tasks directly on the same thread. + * + *

This feature cannot be used in conjunction with {@link #maximumWeight}. + * + * @param maximumSize the maximum size of the cache + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code size} is negative + * @throws IllegalStateException if a maximum size or weight was already set + */ + public Caffeine maximumSize(long maximumSize) { + requireState( + this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); + requireState( + this.maximumWeight == UNSET_INT, + "maximum weight was already set to %s", + this.maximumWeight); + requireState(this.weigher == null, "maximum size can not be combined with weigher"); + requireArgument(maximumSize >= 0, "maximum size must not be negative"); + this.maximumSize = maximumSize; + return this; + } + + /** + * Specifies the maximum weight of entries the cache may contain. Weight is determined using the + * {@link Weigher} specified with {@link #weigher}, and use of this method requires a + * corresponding call to {@link #weigher} prior to calling {@link #build}. + * + *

Note that the cache may evict an entry before this limit is exceeded or temporarily + * exceed the threshold while evicting. As the cache size grows close to the maximum, the + * cache evicts entries that are less likely to be used again. For example, the cache may evict an + * entry because it hasn't been used recently or very often. + * + *

When {@code maximumWeight} is zero, elements will be evicted immediately after being loaded + * into the cache. This can be useful in testing, or to disable caching temporarily without a code + * change. As eviction is scheduled on the configured {@link #executor}, tests may instead prefer + * to configure the cache to execute tasks directly on the same thread. + * + *

Note that weight is only used to determine whether the cache is over capacity; it has no + * effect on selecting which entry should be evicted next. + * + *

This feature cannot be used in conjunction with {@link #maximumSize}. + * + * @param maximumWeight the maximum total weight of entries the cache may contain + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code maximumWeight} is negative + * @throws IllegalStateException if a maximum weight or size was already set + */ + public Caffeine maximumWeight(long maximumWeight) { + requireState( + this.maximumWeight == UNSET_INT, + "maximum weight was already set to %s", + this.maximumWeight); + requireState( + this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); + requireArgument(maximumWeight >= 0, "maximum weight must not be negative"); + this.maximumWeight = maximumWeight; + return this; + } + + /** + * Specifies the weigher to use in determining the weight of entries. Entry weight is taken into + * consideration by {@link #maximumWeight(long)} when determining which entries to evict, and use + * of this method requires a corresponding call to {@link #maximumWeight(long)} prior to calling + * {@link #build}. Weights are measured and recorded when entries are inserted into or updated in + * the cache, and are thus effectively static during the lifetime of a cache entry. + * + *

When the weight of an entry is zero it will not be considered for size-based eviction + * (though it still may be evicted by other means). + * + *

Important note: Instead of returning this as a {@code Caffeine} instance, + * this method returns {@code Caffeine}. From this point on, either the original reference + * or the returned reference may be used to complete configuration and build the cache, but only + * the "generic" one is type-safe. That is, it will properly prevent you from building caches + * whose key or value types are incompatible with the types accepted by the weigher already + * provided; the {@code Caffeine} type cannot do this. For best results, simply use the standard + * method-chaining idiom, as illustrated in the documentation at top, configuring a {@code + * Caffeine} and building your {@link Cache} all in a single statement. + * + *

Warning: if you ignore the above advice, and use this {@code Caffeine} to build a + * cache whose key or value type is incompatible with the weigher, you will likely experience a + * {@link ClassCastException} at some undefined point in the future. + * + * @param weigher the weigher to use in calculating the weight of cache entries + * @param key type of the weigher + * @param value type of the weigher + * @return the cache builder reference that should be used instead of {@code this} for any + * remaining configuration and cache building + * @throws IllegalStateException if a weigher was already set + */ + public Caffeine weigher( + Weigher weigher) { + requireNonNull(weigher); + requireState(this.weigher == null, "weigher was already set to %s", this.weigher); + requireState( + !strictParsing || this.maximumSize == UNSET_INT, + "weigher can not be combined with maximum size"); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + self.weigher = weigher; + return self; + } + + boolean evicts() { + return getMaximum() != UNSET_INT; + } + + boolean isWeighted() { + return (weigher != null); + } + + long getMaximum() { + return isWeighted() ? maximumWeight : maximumSize; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + Weigher getWeigher(boolean isAsync) { + Weigher delegate = + (weigher == null) || (weigher == Weigher.singletonWeigher()) + ? Weigher.singletonWeigher() + : Weigher.boundedWeigher((Weigher) weigher); + return isAsync ? (Weigher) new AsyncWeigher(delegate) : delegate; + } + + /** + * Specifies that each key (not value) stored in the cache should be wrapped in a {@link + * WeakReference} (by default, strong references are used). + * + *

Warning: when this method is used, the resulting cache will use identity ({@code ==}) + * comparison to determine equality of keys. Its {@link Cache#asMap} view will therefore + * technically violate the {@link Map} specification (in the same way that {@link IdentityHashMap} + * does). + * + *

Entries with keys that have been garbage collected may be counted in {@link + * Cache#estimatedSize()}, but will never be visible to read or write operations; such entries are + * cleaned up as part of the routine maintenance described in the class javadoc. + * + *

This feature cannot be used in conjunction with {@link #writer}. + * + *

This feature cannot be used in conjunction with {@link #writer} or when {@link #weakKeys()} + * is combined with {@link #buildAsync}. + * + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalStateException if the key strength was already set or the writer was set + */ + public Caffeine weakKeys() { + requireState(keyStrength == null, "Key strength was already set to %s", keyStrength); + requireState(writer == null, "Weak keys may not be used with CacheWriter"); + + keyStrength = Strength.WEAK; + return this; + } + + boolean isStrongKeys() { + return (keyStrength == null); + } + + /** + * Specifies that each value (not key) stored in the cache should be wrapped in a {@link + * WeakReference} (by default, strong references are used). + * + *

Weak values will be garbage collected once they are weakly reachable. This makes them a poor + * candidate for caching; consider {@link #softValues} instead. + * + *

Note: when this method is used, the resulting cache will use identity ({@code ==}) + * comparison to determine equality of values. + * + *

Entries with values that have been garbage collected may be counted in {@link + * Cache#estimatedSize()}, but will never be visible to read or write operations; such entries are + * cleaned up as part of the routine maintenance described in the class javadoc. + * + *

This feature cannot be used in conjunction with {@link #buildAsync}. + * + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalStateException if the value strength was already set + */ + public Caffeine weakValues() { + requireState(valueStrength == null, "Value strength was already set to %s", valueStrength); + valueStrength = Strength.WEAK; + return this; + } + + boolean isStrongValues() { + return (valueStrength == null); + } + + boolean isWeakValues() { + return (valueStrength == Strength.WEAK); + } + + /** + * Specifies that each value (not key) stored in the cache should be wrapped in a {@link + * SoftReference} (by default, strong references are used). Softly-referenced objects will be + * garbage-collected in a globally least-recently-used manner, in response to memory + * demand. + * + *

Warning: in most circumstances it is better to set a per-cache {@linkplain + * #maximumSize(long) maximum size} instead of using soft references. You should only use this + * method if you are very familiar with the practical consequences of soft references. + * + *

Note: when this method is used, the resulting cache will use identity ({@code ==}) + * comparison to determine equality of values. + * + *

Entries with values that have been garbage collected may be counted in {@link + * Cache#estimatedSize()}, but will never be visible to read or write operations; such entries are + * cleaned up as part of the routine maintenance described in the class javadoc. + * + *

This feature cannot be used in conjunction with {@link #buildAsync}. + * + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalStateException if the value strength was already set + */ + public Caffeine softValues() { + requireState(valueStrength == null, "Value strength was already set to %s", valueStrength); + valueStrength = Strength.SOFT; + return this; + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + *

Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible + * to read or write operations. Expired entries are cleaned up as part of the routine maintenance + * described in the class javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt + * removal of expired entries. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if the time to live or time to idle was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + */ + public Caffeine expireAfterWrite(Duration duration) { + return expireAfterWrite(saturatedToNanos(duration), TimeUnit.NANOSECONDS); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + *

Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible + * to read or write operations. Expired entries are cleaned up as part of the routine maintenance + * described in the class javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt + * removal of expired entries. + * + *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred + * when feasible), use {@link #expireAfterWrite(Duration)} instead. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed + * @param unit the unit that {@code duration} is expressed in + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if the time to live or variable expiration was already set + */ + public Caffeine expireAfterWrite(long duration, TimeUnit unit) { + requireState( + expireAfterWriteNanos == UNSET_INT, + "expireAfterWrite was already set to %s ns", + expireAfterWriteNanos); + requireState(expiry == null, "expireAfterWrite may not be used with variable expiration"); + requireArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterWriteNanos = unit.toNanos(duration); + return this; + } + + long getExpiresAfterWriteNanos() { + return expiresAfterWrite() ? expireAfterWriteNanos : DEFAULT_EXPIRATION_NANOS; + } + + boolean expiresAfterWrite() { + return (expireAfterWriteNanos != UNSET_INT); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations on the + * collection-views of {@link Cache#asMap}. + * + *

Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible + * to read or write operations. Expired entries are cleaned up as part of the routine maintenance + * described in the class javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt + * removal of expired entries. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if the time to idle or time to live was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + */ + public Caffeine expireAfterAccess(Duration duration) { + return expireAfterAccess(saturatedToNanos(duration), TimeUnit.NANOSECONDS); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * read. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations on the + * collection-views of {@link Cache#asMap}. + * + *

Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible + * to read or write operations. Expired entries are cleaned up as part of the routine maintenance + * described in the class javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt + * removal of expired entries. + * + *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred + * when feasible), use {@link #expireAfterAccess(Duration)} instead. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @param unit the unit that {@code duration} is expressed in + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if the time to idle or variable expiration was already set + */ + public Caffeine expireAfterAccess(long duration, TimeUnit unit) { + requireState( + expireAfterAccessNanos == UNSET_INT, + "expireAfterAccess was already set to %s ns", + expireAfterAccessNanos); + requireState(expiry == null, "expireAfterAccess may not be used with variable expiration"); + requireArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterAccessNanos = unit.toNanos(duration); + return this; + } + + long getExpiresAfterAccessNanos() { + return expiresAfterAccess() ? expireAfterAccessNanos : DEFAULT_EXPIRATION_NANOS; + } + + boolean expiresAfterAccess() { + return (expireAfterAccessNanos != UNSET_INT); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a duration has + * elapsed after the entry's creation, the most recent replacement of its value, or its last read. + * The expiration time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations on the + * collection-views of {@link Cache#asMap}. + * + *

Expired entries may be counted in {@link Cache#estimatedSize()}, but will never be visible + * to read or write operations. Expired entries are cleaned up as part of the routine maintenance + * described in the class javadoc. A {@link #scheduler(Scheduler)} may be configured for a prompt + * removal of expired entries. + * + *

Important note: after invoking this method, do not continue to use this cache + * builder reference; instead use the reference this method returns. At runtime, these + * point to the same instance, but only the returned reference has the correct generic type + * information so as to ensure type safety. For best results, use the standard method-chaining + * idiom illustrated in the class documentation above, configuring a builder and building your + * cache in a single statement. Failure to heed this advice can result in a {@link + * ClassCastException} being thrown by a cache operation at some undefined point in the + * future. + * + * @param expiry the expiry to use in calculating the expiration time of cache entries + * @param key type of the weigher + * @param value type of the weigher + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalStateException if expiration was already set + */ + public Caffeine expireAfter( + Expiry expiry) { + requireNonNull(expiry); + requireState(this.expiry == null, "Expiry was already set to %s", this.expiry); + requireState( + this.expireAfterAccessNanos == UNSET_INT, "Expiry may not be used with expiresAfterAccess"); + requireState( + this.expireAfterWriteNanos == UNSET_INT, "Expiry may not be used with expiresAfterWrite"); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + self.expiry = expiry; + return self; + } + + boolean expiresVariable() { + return expiry != null; + } + + @SuppressWarnings("unchecked") + Expiry getExpiry(boolean isAsync) { + return isAsync && (expiry != null) + ? (Expiry) new AsyncExpiry<>(expiry) + : (Expiry) expiry; + } + + /** + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. The semantics + * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link + * CacheLoader#reload}. + * + *

Automatic refreshes are performed when the first stale request for an entry occurs. The + * request triggering refresh will make an asynchronous call to {@link CacheLoader#reload} and + * immediately return the old value. + * + *

Note: all exceptions thrown during refresh will be logged and then swallowed. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is zero or negative + * @throws IllegalStateException if the refresh interval was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + */ + public Caffeine refreshAfterWrite(Duration duration) { + return refreshAfterWrite(saturatedToNanos(duration), TimeUnit.NANOSECONDS); + } + + /** + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. The semantics + * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link + * CacheLoader#reload}. + * + *

Automatic refreshes are performed when the first stale request for an entry occurs. The + * request triggering refresh will make an asynchronous call to {@link CacheLoader#reload} and + * immediately return the old value. + * + *

Note: all exceptions thrown during refresh will be logged and then swallowed. + * + *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred + * when feasible), use {@link #refreshAfterWrite(Duration)} instead. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @param unit the unit that {@code duration} is expressed in + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is zero or negative + * @throws IllegalStateException if the refresh interval was already set + */ + public Caffeine refreshAfterWrite(long duration, TimeUnit unit) { + requireNonNull(unit); + requireState( + refreshAfterWriteNanos == UNSET_INT, + "refreshAfterWriteNanos was already set to %s ns", + refreshAfterWriteNanos); + requireArgument(duration > 0, "duration must be positive: %s %s", duration, unit); + this.refreshAfterWriteNanos = unit.toNanos(duration); + return this; + } + + long getRefreshAfterWriteNanos() { + return refreshAfterWrite() ? refreshAfterWriteNanos : DEFAULT_REFRESH_NANOS; + } + + boolean refreshAfterWrite() { + return refreshAfterWriteNanos != UNSET_INT; + } + + /** + * Specifies a nanosecond-precision time source for use in determining when entries should be + * expired or refreshed. By default, {@link System#nanoTime} is used. + * + *

The primary intent of this method is to facilitate testing of caches which have been + * configured with {@link #expireAfterWrite}, {@link #expireAfterAccess}, or {@link + * #refreshAfterWrite}. + * + * @param ticker a nanosecond-precision time source + * @return this {@code Caffeine} instance (for chaining) + * @throws IllegalStateException if a ticker was already set + * @throws NullPointerException if the specified ticker is null + */ + public Caffeine ticker(Ticker ticker) { + requireState(this.ticker == null, "Ticker was already set to %s", this.ticker); + this.ticker = requireNonNull(ticker); + return this; + } + + Ticker getTicker() { + boolean useTicker = + expiresVariable() + || expiresAfterAccess() + || expiresAfterWrite() + || refreshAfterWrite() + || isRecordingStats(); + return useTicker ? (ticker == null) ? Ticker.systemTicker() : ticker : Ticker.disabledTicker(); + } + + /** + * Specifies a listener instance that caches should notify each time an entry is evicted. The + * cache will invoke this listener during the atomic operation to remove the entry. In the case of + * expiration or reference collection, the entry may be pending removal and will be discarded as + * as part of the routine maintenance described in the class documentation above. For a more + * prompt notification on expiration a {@link #scheduler(Scheduler)} may be configured. A {@link + * #removalListener(RemovalListener)} may be preferred when the listener should be invoked for any + * {@linkplain RemovalCause reason}, be performed outside of the atomic operation to remove the + * entry, and delegated to the configured {@link #executor(Executor)}. + * + *

Important note: after invoking this method, do not continue to use this cache + * builder reference; instead use the reference this method returns. At runtime, these + * point to the same instance, but only the returned reference has the correct generic type + * information so as to ensure type safety. For best results, use the standard method-chaining + * idiom illustrated in the class documentation above, configuring a builder and building your + * cache in a single statement. Failure to heed this advice can result in a {@link + * ClassCastException} being thrown by a cache operation at some undefined point in the + * future. + * + *

Warning: any exception thrown by {@code listener} will not be propagated to + * the {@code Cache} user, only logged via a {@link Logger}. + * + *

This feature cannot be used in conjunction when {@link #weakKeys()} is combined with {@link + * #buildAsync}. + * + * @param evictionListener a listener instance that caches should notify each time an entry is + * being automatically removed due to eviction + * @param the key type of the listener + * @param the value type of the listener + * @return the cache builder reference that should be used instead of {@code this} for any + * remaining configuration and cache building + * @throws IllegalStateException if a removal listener was already set + * @throws NullPointerException if the specified removal listener is null + */ + public Caffeine evictionListener( + RemovalListener evictionListener) { + requireState( + this.evictionListener == null, + "eviction listener was already set to %s", + this.evictionListener); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + self.evictionListener = requireNonNull(evictionListener); + return self; + } + + /** + * Specifies a listener instance that caches should notify each time an entry is removed for any + * {@linkplain RemovalCause reason}. The cache will invoke this listener on the configured {@link + * #executor(Executor)} after the entry's removal operation has completed. In the case of + * expiration or reference collection, the entry may be pending removal and will be discarded as + * as part of the routine maintenance described in the class documentation above. For a more + * prompt notification on expiration a {@link #scheduler(Scheduler)} may be configured. An {@link + * #evictionListener(RemovalListener)} may be preferred when the listener should be invoked as + * part of the atomic operation to remove the entry. + * + *

Important note: after invoking this method, do not continue to use this cache + * builder reference; instead use the reference this method returns. At runtime, these + * point to the same instance, but only the returned reference has the correct generic type + * information so as to ensure type safety. For best results, use the standard method-chaining + * idiom illustrated in the class documentation above, configuring a builder and building your + * cache in a single statement. Failure to heed this advice can result in a {@link + * ClassCastException} being thrown by a cache operation at some undefined point in the + * future. + * + *

Warning: any exception thrown by {@code listener} will not be propagated to + * the {@code Cache} user, only logged via a {@link Logger}. + * + * @param removalListener a listener instance that caches should notify each time an entry is + * removed + * @param the key type of the listener + * @param the value type of the listener + * @return the cache builder reference that should be used instead of {@code this} for any + * remaining configuration and cache building + * @throws IllegalStateException if a removal listener was already set + * @throws NullPointerException if the specified removal listener is null + */ + public Caffeine removalListener( + RemovalListener removalListener) { + requireState( + this.removalListener == null, + "removal listener was already set to %s", + this.removalListener); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + self.removalListener = requireNonNull(removalListener); + return self; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + RemovalListener getRemovalListener(boolean async) { + RemovalListener castedListener = (RemovalListener) removalListener; + return async && (castedListener != null) + ? new AsyncRemovalListener(castedListener, getExecutor()) + : castedListener; + } + + /** + * Specifies a writer instance that caches should notify each time an entry is explicitly created + * or modified, or removed for any {@linkplain RemovalCause reason}. The writer is not notified + * when an entry is loaded or computed. Each cache created by this builder will invoke this writer + * as part of the atomic operation that modifies the cache. + * + *

Important note: after invoking this method, do not continue to use this cache + * builder reference; instead use the reference this method returns. At runtime, these + * point to the same instance, but only the returned reference has the correct generic type + * information so as to ensure type safety. For best results, use the standard method-chaining + * idiom illustrated in the class documentation above, configuring a builder and building your + * cache in a single statement. Failure to heed this advice can result in a {@link + * ClassCastException} being thrown by a cache operation at some undefined point in the + * future. + * + *

Warning: any exception thrown by {@code writer} will be propagated to the {@code + * Cache} user. + * + *

This feature cannot be used in conjunction with {@link #weakKeys()}, {@link + * #evictionListener(RemovalListener)}, or {@link #buildAsync}. + * + * @param writer a writer instance that caches should notify each time an entry is explicitly + * created or modified, or removed for any reason + * @param the key type of the writer + * @param the value type of the writer + * @return the cache builder reference that should be used instead of {@code this} for any + * remaining configuration and cache building + * @throws IllegalStateException if a writer was already set or if the key strength is weak + * @throws NullPointerException if the specified writer is null + * @deprecated Scheduled for removal in version 3.0.0. Consider instead using {@link Map} compute + * methods for extending manual write and remove operations, and using {@link + * #evictionListener(RemovalListener)} for extending removals due to eviction. + */ + @Deprecated + public Caffeine writer( + CacheWriter writer) { + requireState(this.writer == null, "Writer was already set to %s", this.writer); + requireState(keyStrength == null, "Weak keys may not be used with CacheWriter"); + requireState(evictionListener == null, "Eviction listener may not be used with CacheWriter"); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + self.writer = requireNonNull(writer); + return self; + } + + @SuppressWarnings({"deprecation", "unchecked"}) + CacheWriter getCacheWriter(boolean async) { + CacheWriter castedWriter; + if (evictionListener == null) { + castedWriter = (CacheWriter) writer; + } else { + castedWriter = new CacheWriterAdapter<>(evictionListener, async); + } + return (castedWriter == null) ? CacheWriter.disabledWriter() : castedWriter; + } + + /** + * Enables the accumulation of {@link CacheStats} during the operation of the cache. Without this + * {@link Cache#stats} will return zero for all statistics. Note that recording statistics + * requires bookkeeping to be performed with each operation, and thus imposes a performance + * penalty on cache operation. + * + * @return this {@code Caffeine} instance (for chaining) + */ + public Caffeine recordStats() { + requireState(this.statsCounterSupplier == null, "Statistics recording was already set"); + statsCounterSupplier = ENABLED_STATS_COUNTER_SUPPLIER; + return this; + } + + /** + * Enables the accumulation of {@link CacheStats} during the operation of the cache. Without this + * {@link Cache#stats} will return zero for all statistics. Note that recording statistics + * requires bookkeeping to be performed with each operation, and thus imposes a performance + * penalty on cache operation. Any exception thrown by the supplied {@link StatsCounter} will be + * suppressed and logged. + * + * @param statsCounterSupplier a supplier instance that returns a new {@link StatsCounter} + * @return this {@code Caffeine} instance (for chaining) + */ + public Caffeine recordStats(Supplier statsCounterSupplier) { + requireState(this.statsCounterSupplier == null, "Statistics recording was already set"); + requireNonNull(statsCounterSupplier); + this.statsCounterSupplier = () -> StatsCounter.guardedStatsCounter(statsCounterSupplier.get()); + return this; + } + + boolean isRecordingStats() { + return (statsCounterSupplier != null); + } + + Supplier getStatsCounterSupplier() { + return (statsCounterSupplier == null) + ? StatsCounter::disabledStatsCounter + : statsCounterSupplier; + } + + boolean isBounded() { + return (maximumSize != UNSET_INT) + || (maximumWeight != UNSET_INT) + || (expireAfterAccessNanos != UNSET_INT) + || (expireAfterWriteNanos != UNSET_INT) + || (expiry != null) + || (keyStrength != null) + || (valueStrength != null); + } + + /** + * Builds a cache which does not automatically load values when keys are requested unless a + * mapping function is provided. Note that multiple threads can concurrently load values for + * distinct keys. + * + *

Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a {@code + * CacheLoader}. + * + *

This method does not alter the state of this {@code Caffeine} instance, so it can be invoked + * again to create multiple independent caches. + * + * @param the key type of the cache + * @param the value type of the cache + * @return a cache having the requested features + */ + public Cache build() { + requireWeightWithWeigher(); + requireNonLoadingCache(); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + return isBounded() + ? new BoundedLocalCache.BoundedLocalManualCache<>(self) + : new UnboundedLocalCache.UnboundedLocalManualCache<>(self); + } + + /** + * Builds a cache, which either returns an already-loaded value for a given key or atomically + * computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently + * loading the value for this key, simply waits for that thread to finish and returns its loaded + * value. Note that multiple threads can concurrently load values for distinct keys. + * + *

This method does not alter the state of this {@code Caffeine} instance, so it can be invoked + * again to create multiple independent caches. + * + * @param loader the cache loader used to obtain new values + * @param the key type of the loader + * @param the value type of the loader + * @return a cache having the requested features + */ + public LoadingCache build( + CacheLoader loader) { + requireWeightWithWeigher(); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + return isBounded() || refreshAfterWrite() + ? new BoundedLocalCache.BoundedLocalLoadingCache<>(self, loader) + : new UnboundedLocalCache.UnboundedLocalLoadingCache<>(self, loader); + } + + /** + * Builds a cache which does not automatically load values when keys are requested unless a + * mapping function is provided. The returned {@link CompletableFuture} may be already loaded or + * currently computing the value for a given key. If the asynchronous computation fails or + * computes a {@code null} value then the entry will be automatically removed. Note that multiple + * threads can concurrently load values for distinct keys. + * + *

Consider {@link #buildAsync(CacheLoader)} or {@link #buildAsync(AsyncCacheLoader)} instead, + * if it is feasible to implement an {@code CacheLoader} or {@code AsyncCacheLoader}. + * + *

This method does not alter the state of this {@code Caffeine} instance, so it can be invoked + * again to create multiple independent caches. + * + *

This construction cannot be used with {@link #weakValues()}, {@link #softValues()}, {@link + * #writer(CacheWriter)}, or when {@link #weakKeys()} are combined with {@link + * #evictionListener(RemovalListener)}. + * + * @param the key type of the cache + * @param the value type of the cache + * @return a cache having the requested features + */ + public AsyncCache buildAsync() { + requireState(valueStrength == null, "Weak or soft values can not be combined with AsyncCache"); + requireState(writer == null, "CacheWriter can not be combined with AsyncCache"); + requireState( + isStrongKeys() || (evictionListener == null), + "Weak keys cannot be combined eviction listener and with AsyncLoadingCache"); + requireWeightWithWeigher(); + requireNonLoadingCache(); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + return isBounded() + ? new BoundedLocalCache.BoundedLocalAsyncCache<>(self) + : new UnboundedLocalCache.UnboundedLocalAsyncCache<>(self); + } + + /** + * Builds a cache, which either returns a {@link CompletableFuture} already loaded or currently + * computing the value for a given key, or atomically computes the value asynchronously through a + * supplied mapping function or the supplied {@code CacheLoader}. If the asynchronous computation + * fails or computes a {@code null} value then the entry will be automatically removed. Note that + * multiple threads can concurrently load values for distinct keys. + * + *

This method does not alter the state of this {@code Caffeine} instance, so it can be invoked + * again to create multiple independent caches. + * + *

This construction cannot be used with {@link #weakValues()}, {@link #softValues()}, {@link + * #writer(CacheWriter)}, or when {@link #weakKeys()} are combined with {@link + * #evictionListener(RemovalListener)}. + * + * @param loader the cache loader used to obtain new values + * @param the key type of the loader + * @param the value type of the loader + * @return a cache having the requested features + */ + public AsyncLoadingCache buildAsync( + CacheLoader loader) { + return buildAsync((AsyncCacheLoader) loader); + } + + /** + * Builds a cache, which either returns a {@link CompletableFuture} already loaded or currently + * computing the value for a given key, or atomically computes the value asynchronously through a + * supplied mapping function or the supplied {@code AsyncCacheLoader}. If the asynchronous + * computation fails or computes a {@code null} value then the entry will be automatically + * removed. Note that multiple threads can concurrently load values for distinct keys. + * + *

This method does not alter the state of this {@code Caffeine} instance, so it can be invoked + * again to create multiple independent caches. + * + *

This construction cannot be used with {@link #weakValues()}, {@link #softValues()}, {@link + * #writer(CacheWriter)}, or when {@link #weakKeys()} are combined with {@link + * #evictionListener(RemovalListener)}. + * + * @param loader the cache loader used to obtain new values + * @param the key type of the loader + * @param the value type of the loader + * @return a cache having the requested features + */ + public AsyncLoadingCache buildAsync( + AsyncCacheLoader loader) { + requireState(isStrongValues(), "Weak or soft values cannot be combined with AsyncLoadingCache"); + requireState(writer == null, "CacheWriter cannot be combined with AsyncLoadingCache"); + requireState( + isStrongKeys() || (evictionListener == null), + "Weak keys cannot be combined eviction listener and with AsyncLoadingCache"); + + requireWeightWithWeigher(); + requireNonNull(loader); + + @SuppressWarnings("unchecked") + Caffeine self = (Caffeine) this; + return isBounded() || refreshAfterWrite() + ? new BoundedLocalCache.BoundedLocalAsyncLoadingCache<>(self, loader) + : new UnboundedLocalCache.UnboundedLocalAsyncLoadingCache<>(self, loader); + } + + void requireNonLoadingCache() { + requireState(refreshAfterWriteNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache"); + } + + void requireWeightWithWeigher() { + if (weigher == null) { + requireState(maximumWeight == UNSET_INT, "maximumWeight requires weigher"); + } else if (strictParsing) { + requireState(maximumWeight != UNSET_INT, "weigher requires maximumWeight"); + } else if (maximumWeight == UNSET_INT) { + logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight"); + } + } + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + private static long saturatedToNanos(Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } + + /** + * Returns a string representation for this Caffeine instance. The exact form of the returned + * string is not specified. + */ + @Override + public String toString() { + StringBuilder s = new StringBuilder(75); + s.append(getClass().getSimpleName()).append('{'); + int baseLength = s.length(); + if (initialCapacity != UNSET_INT) { + s.append("initialCapacity=").append(initialCapacity).append(", "); + } + if (maximumSize != UNSET_INT) { + s.append("maximumSize=").append(maximumSize).append(", "); + } + if (maximumWeight != UNSET_INT) { + s.append("maximumWeight=").append(maximumWeight).append(", "); + } + if (expireAfterWriteNanos != UNSET_INT) { + s.append("expireAfterWrite=").append(expireAfterWriteNanos).append("ns, "); + } + if (expireAfterAccessNanos != UNSET_INT) { + s.append("expireAfterAccess=").append(expireAfterAccessNanos).append("ns, "); + } + if (expiry != null) { + s.append("expiry, "); + } + if (refreshAfterWriteNanos != UNSET_INT) { + s.append("refreshAfterWriteNanos=").append(refreshAfterWriteNanos).append("ns, "); + } + if (keyStrength != null) { + s.append("keyStrength=").append(keyStrength.toString().toLowerCase(US)).append(", "); + } + if (valueStrength != null) { + s.append("valueStrength=").append(valueStrength.toString().toLowerCase(US)).append(", "); + } + if (evictionListener != null) { + s.append("evictionListener, "); + } + if (removalListener != null) { + s.append("removalListener, "); + } + if (writer != null) { + s.append("writer, "); + } + if (s.length() > baseLength) { + s.deleteCharAt(s.length() - 2); + } + return s.append('}').toString(); + } + + @SuppressWarnings("deprecation") + static final class CacheWriterAdapter implements CacheWriter, Serializable { + private static final long serialVersionUID = 1; + + final RemovalListener delegate; + final boolean isAsync; + + CacheWriterAdapter(RemovalListener delegate, boolean isAsync) { + this.delegate = delegate; + this.isAsync = isAsync; + } + + @Override + public void write(K key, V value) {} + + @Override + public void delete(K key, V value, RemovalCause cause) { + if (cause.wasEvicted()) { + try { + if (isAsync && (value != null)) { + @SuppressWarnings("unchecked") + CompletableFuture future = (CompletableFuture) value; + value = Async.getIfReady(future); + } + delegate.onRemoval(key, value, cause); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by eviction listener", t); + } + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CaffeineSpec.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CaffeineSpec.java new file mode 100644 index 0000000000000..51c0e375a843b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/CaffeineSpec.java @@ -0,0 +1,390 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.Strength; + +import java.time.Duration; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.UNSET_INT; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.requireArgument; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.requireState; + +/** + * A specification of a {@link Caffeine} builder configuration. + * + *

{@code CaffeineSpec} supports parsing configuration off of a string, which makes it especially + * useful for command-line configuration of a {@code Caffeine} builder. + * + *

The string syntax is a series of comma-separated keys or key-value pairs, each corresponding + * to a {@code Caffeine} builder method. + * + *

    + *
  • {@code initialCapacity=[integer]}: sets {@link Caffeine#initialCapacity}. + *
  • {@code maximumSize=[long]}: sets {@link Caffeine#maximumSize}. + *
  • {@code maximumWeight=[long]}: sets {@link Caffeine#maximumWeight}. + *
  • {@code expireAfterAccess=[duration]}: sets {@link Caffeine#expireAfterAccess}. + *
  • {@code expireAfterWrite=[duration]}: sets {@link Caffeine#expireAfterWrite}. + *
  • {@code refreshAfterWrite=[duration]}: sets {@link Caffeine#refreshAfterWrite}. + *
  • {@code weakKeys}: sets {@link Caffeine#weakKeys}. + *
  • {@code weakValues}: sets {@link Caffeine#weakValues}. + *
  • {@code softValues}: sets {@link Caffeine#softValues}. + *
  • {@code recordStats}: sets {@link Caffeine#recordStats}. + *
+ * + *

Durations are represented as either an ISO-8601 string using {@link + * Duration#parse(CharSequence)} or by an integer followed by one of "d", "h", "m", or "s", + * representing days, hours, minutes, or seconds respectively. There is currently no short syntax to + * request durations in milliseconds, microseconds, or nanoseconds. + * + *

Whitespace before and after commas and equal signs is ignored. Keys may not be repeated; it is + * also illegal to use the following pairs of keys in a single value: + * + *

    + *
  • {@code maximumSize} and {@code maximumWeight} + *
  • {@code weakValues} and {@code softValues} + *
+ * + *

{@code CaffeineSpec} does not support configuring {@code Caffeine} methods with non-value + * parameters. These must be configured in code. + * + *

A new {@code Caffeine} builder can be instantiated from a {@code CaffeineSpec} using {@link + * Caffeine#from(CaffeineSpec)} or {@link Caffeine#from(String)}. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public final class CaffeineSpec { + static final String SPLIT_OPTIONS = ","; + static final String SPLIT_KEY_VALUE = "="; + + final String specification; + + int initialCapacity = UNSET_INT; + long maximumWeight = UNSET_INT; + long maximumSize = UNSET_INT; + boolean recordStats; + + Strength keyStrength; + Strength valueStrength; + Duration expireAfterWrite; + Duration expireAfterAccess; + Duration refreshAfterWrite; + + private CaffeineSpec(String specification) { + this.specification = requireNonNull(specification); + } + + /** + * Returns a {@link Caffeine} builder configured according to this specification. + * + * @return a builder configured to the specification + */ + Caffeine toBuilder() { + Caffeine builder = Caffeine.newBuilder(); + if (initialCapacity != UNSET_INT) { + builder.initialCapacity(initialCapacity); + } + if (maximumSize != UNSET_INT) { + builder.maximumSize(maximumSize); + } + if (maximumWeight != UNSET_INT) { + builder.maximumWeight(maximumWeight); + } + if (keyStrength != null) { + requireState(keyStrength == Strength.WEAK); + builder.weakKeys(); + } + if (valueStrength != null) { + if (valueStrength == Strength.WEAK) { + builder.weakValues(); + } else if (valueStrength == Strength.SOFT) { + builder.softValues(); + } else { + throw new IllegalStateException(); + } + } + if (expireAfterWrite != null) { + builder.expireAfterWrite(expireAfterWrite); + } + if (expireAfterAccess != null) { + builder.expireAfterAccess(expireAfterAccess); + } + if (refreshAfterWrite != null) { + builder.refreshAfterWrite(refreshAfterWrite); + } + if (recordStats) { + builder.recordStats(); + } + return builder; + } + + /** + * Creates a CaffeineSpec from a string. + * + * @param specification the string form + * @return the parsed specification + */ + @SuppressWarnings("StringSplitter") + public static CaffeineSpec parse(String specification) { + CaffeineSpec spec = new CaffeineSpec(specification); + for (String option : specification.split(SPLIT_OPTIONS)) { + spec.parseOption(option.trim()); + } + return spec; + } + + /** Parses and applies the configuration option. */ + void parseOption(String option) { + if (option.isEmpty()) { + return; + } + + @SuppressWarnings("StringSplitter") + String[] keyAndValue = option.split(SPLIT_KEY_VALUE); + requireArgument( + keyAndValue.length <= 2, "key-value pair %s with more than one equals sign", option); + + String key = keyAndValue[0].trim(); + String value = (keyAndValue.length == 1) ? null : keyAndValue[1].trim(); + + configure(key, value); + } + + /** Configures the setting. */ + void configure(String key, String value) { + switch (key) { + case "initialCapacity": + initialCapacity(key, value); + return; + case "maximumSize": + maximumSize(key, value); + return; + case "maximumWeight": + maximumWeight(key, value); + return; + case "weakKeys": + weakKeys(value); + return; + case "weakValues": + valueStrength(key, value, Strength.WEAK); + return; + case "softValues": + valueStrength(key, value, Strength.SOFT); + return; + case "expireAfterAccess": + expireAfterAccess(key, value); + return; + case "expireAfterWrite": + expireAfterWrite(key, value); + return; + case "refreshAfterWrite": + refreshAfterWrite(key, value); + return; + case "recordStats": + recordStats(value); + return; + default: + throw new IllegalArgumentException("Unknown key " + key); + } + } + + /** Configures the initial capacity. */ + void initialCapacity(String key, String value) { + requireArgument( + initialCapacity == UNSET_INT, "initial capacity was already set to %,d", initialCapacity); + initialCapacity = parseInt(key, value); + } + + /** Configures the maximum size. */ + void maximumSize(String key, String value) { + requireArgument(maximumSize == UNSET_INT, "maximum size was already set to %,d", maximumSize); + requireArgument( + maximumWeight == UNSET_INT, "maximum weight was already set to %,d", maximumWeight); + maximumSize = parseLong(key, value); + } + + /** Configures the maximum size. */ + void maximumWeight(String key, String value) { + requireArgument( + maximumWeight == UNSET_INT, "maximum weight was already set to %,d", maximumWeight); + requireArgument(maximumSize == UNSET_INT, "maximum size was already set to %,d", maximumSize); + maximumWeight = parseLong(key, value); + } + + /** Configures the keys as weak references. */ + void weakKeys(String value) { + requireArgument(value == null, "weak keys does not take a value"); + requireArgument(keyStrength == null, "weak keys was already set"); + keyStrength = Strength.WEAK; + } + + /** Configures the value as weak or soft references. */ + void valueStrength(String key, String value, Strength strength) { + requireArgument(value == null, "%s does not take a value", key); + requireArgument(valueStrength == null, "%s was already set to %s", key, valueStrength); + valueStrength = strength; + } + + /** Configures expire after access. */ + void expireAfterAccess(String key, String value) { + requireArgument(expireAfterAccess == null, "expireAfterAccess was already set"); + expireAfterAccess = parseDuration(key, value); + } + + /** Configures expire after write. */ + void expireAfterWrite(String key, String value) { + requireArgument(expireAfterWrite == null, "expireAfterWrite was already set"); + expireAfterWrite = parseDuration(key, value); + } + + /** Configures refresh after write. */ + void refreshAfterWrite(String key, String value) { + requireArgument(refreshAfterWrite == null, "refreshAfterWrite was already set"); + refreshAfterWrite = parseDuration(key, value); + } + + /** Configures the value as weak or soft references. */ + void recordStats(String value) { + requireArgument(value == null, "record stats does not take a value"); + requireArgument(!recordStats, "record stats was already set"); + recordStats = true; + } + + /** Returns a parsed int value. */ + static int parseInt(String key, String value) { + requireArgument((value != null) && !value.isEmpty(), "value of key %s was omitted", key); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("key %s value was set to %s, must be an integer", key, value), e); + } + } + + /** Returns a parsed long value. */ + static long parseLong(String key, String value) { + requireArgument((value != null) && !value.isEmpty(), "value of key %s was omitted", key); + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("key %s value was set to %s, must be a long", key, value), e); + } + } + + /** Returns a parsed duration value. */ + static Duration parseDuration(String key, String value) { + requireArgument((value != null) && !value.isEmpty(), "value of key %s omitted", key); + + @SuppressWarnings("NullAway") + boolean isIsoFormat = value.contains("p") || value.contains("P"); + if (isIsoFormat) { + Duration duration = Duration.parse(value); + requireArgument( + !duration.isNegative(), + "key %s invalid format; was %s, but the duration cannot be negative", + key, + value); + return duration; + } + + @SuppressWarnings("NullAway") + long duration = parseLong(key, value.substring(0, value.length() - 1)); + TimeUnit unit = parseTimeUnit(key, value); + return Duration.ofNanos(unit.toNanos(duration)); + } + + /** Returns a parsed {@link TimeUnit} value. */ + static TimeUnit parseTimeUnit(String key, String value) { + requireArgument((value != null) && !value.isEmpty(), "value of key %s omitted", key); + @SuppressWarnings("NullAway") + char lastChar = Character.toLowerCase(value.charAt(value.length() - 1)); + switch (lastChar) { + case 'd': + return TimeUnit.DAYS; + case 'h': + return TimeUnit.HOURS; + case 'm': + return TimeUnit.MINUTES; + case 's': + return TimeUnit.SECONDS; + default: + throw new IllegalArgumentException( + String.format( + "key %s invalid format; was %s, must end with one of [dDhHmMsS]", key, value)); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof CaffeineSpec)) { + return false; + } + CaffeineSpec spec = (CaffeineSpec) o; + return Objects.equals(refreshAfterWrite, spec.refreshAfterWrite) + && Objects.equals(expireAfterAccess, spec.expireAfterAccess) + && Objects.equals(expireAfterWrite, spec.expireAfterWrite) + && (initialCapacity == spec.initialCapacity) + && (maximumWeight == spec.maximumWeight) + && (valueStrength == spec.valueStrength) + && (keyStrength == spec.keyStrength) + && (maximumSize == spec.maximumSize) + && (recordStats == spec.recordStats); + } + + @Override + public int hashCode() { + return Objects.hash( + initialCapacity, + maximumSize, + maximumWeight, + keyStrength, + valueStrength, + recordStats, + expireAfterWrite, + expireAfterAccess, + refreshAfterWrite); + } + + /** + * Returns a string that can be used to parse an equivalent {@code CaffeineSpec}. The order and + * form of this representation is not guaranteed, except that parsing its output will produce a + * {@code CaffeineSpec} equal to this instance. + * + * @return a string representation of this specification + */ + public String toParsableString() { + return specification; + } + + /** + * Returns a string representation for this {@code CaffeineSpec} instance. The form of this + * representation is not guaranteed. + */ + @Override + public String toString() { + return getClass().getSimpleName() + '{' + toParsableString() + '}'; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Expiry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Expiry.java new file mode 100644 index 0000000000000..61d1d0a8f5d85 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Expiry.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * Calculates when cache entries expire. A single expiration time is retained so that the lifetime + * of an entry may be extended or reduced by subsequent evaluations. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public interface Expiry { + + /** + * Specifies that the entry should be automatically removed from the cache once the duration has + * elapsed after the entry's creation. To indicate no expiration an entry may be given an + * excessively long period, such as {@code Long#MAX_VALUE}. + * + *

Note: The {@code currentTime} is supplied by the configured {@link Ticker} and by + * default does not relate to system or wall-clock time. When calculating the duration based on a + * time stamp, the current time should be obtained independently. + * + * @param key the key represented by this entry + * @param value the value represented by this entry + * @param currentTime the current time, in nanoseconds + * @return the length of time before the entry expires, in nanoseconds + */ + long expireAfterCreate(K key, V value, long currentTime); + + /** + * Specifies that the entry should be automatically removed from the cache once the duration has + * elapsed after the replacement of its value. To indicate no expiration an entry may be given an + * excessively long period, such as {@code Long#MAX_VALUE}. The {@code currentDuration} may be + * returned to not modify the expiration time. + * + *

Note: The {@code currentTime} is supplied by the configured {@link Ticker} and by + * default does not relate to system or wall-clock time. When calculating the duration based on a + * time stamp, the current time should be obtained independently. + * + * @param key the key represented by this entry + * @param value the value represented by this entry + * @param currentTime the current time, in nanoseconds + * @param currentDuration the current duration, in nanoseconds + * @return the length of time before the entry expires, in nanoseconds + */ + long expireAfterUpdate(K key, V value, long currentTime, long currentDuration); + + /** + * Specifies that the entry should be automatically removed from the cache once the duration has + * elapsed after its last read. To indicate no expiration an entry may be given an excessively + * long period, such as {@code Long#MAX_VALUE}. The {@code currentDuration} may be returned to not + * modify the expiration time. + * + *

Note: The {@code currentTime} is supplied by the configured {@link Ticker} and by + * default does not relate to system or wall-clock time. When calculating the duration based on a + * time stamp, the current time should be obtained independently. + * + * @param key the key represented by this entry + * @param value the value represented by this entry + * @param currentTime the current time, in nanoseconds + * @param currentDuration the current duration, in nanoseconds + * @return the length of time before the entry expires, in nanoseconds + */ + long expireAfterRead(K key, V value, long currentTime, long currentDuration); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FD.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FD.java new file mode 100644 index 0000000000000..4d458c58f80d8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FD.java @@ -0,0 +1,157 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.References.LookupKeyReference; +import org.apache.iotdb.github.benmanes.caffeine.cache.References.SoftValueReference; +import org.apache.iotdb.github.benmanes.caffeine.cache.References.WeakKeyReference; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • WeakKeys + *
  • SoftValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FD extends Node implements NodeFactory { + protected static final long VALUE_OFFSET = + UnsafeAccess.objectFieldOffset( + FD.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.VALUE); + + volatile SoftValueReference value; + + FD() {} + + FD( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + this(new WeakKeyReference(key, keyReferenceQueue), value, valueReferenceQueue, weight, now); + } + + FD(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + UnsafeAccess.UNSAFE.putObject( + this, VALUE_OFFSET, new SoftValueReference(keyReference, value, valueReferenceQueue)); + } + + public final Object getKeyReference() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + return valueRef.getKeyReference(); + } + + public final K getKey() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + WeakKeyReference keyRef = (WeakKeyReference) valueRef.getKeyReference(); + return keyRef.get(); + } + + public final V getValue() { + for (; ; ) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + V referent = ref.get(); + if ((referent != null) || (ref == value)) { + return referent; + } + } + } + + public final Object getValueReference() { + return UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + } + + public final void setValue(V value, ReferenceQueue referenceQueue) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + UnsafeAccess.UNSAFE.putOrderedObject( + this, VALUE_OFFSET, new SoftValueReference(getKeyReference(), value, referenceQueue)); + ref.clear(); + } + + public final boolean containsValue(Object value) { + return getValue() == value; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FD<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FD<>(keyReference, value, valueReferenceQueue, weight, now); + } + + public Object newLookupKey(Object key) { + return new LookupKeyReference<>(key); + } + + public Object newReferenceKey(K key, ReferenceQueue referenceQueue) { + return new WeakKeyReference(key, referenceQueue); + } + + public boolean softValues() { + return true; + } + + public final boolean isAlive() { + Object key = getKeyReference(); + return (key != RETIRED_WEAK_KEY) && (key != DEAD_WEAK_KEY); + } + + public final boolean isRetired() { + return (getKeyReference() == RETIRED_WEAK_KEY); + } + + public final void retire() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + WeakKeyReference keyRef = (WeakKeyReference) valueRef.getKeyReference(); + keyRef.clear(); + valueRef.setKeyReference(RETIRED_WEAK_KEY); + valueRef.clear(); + } + + public final boolean isDead() { + return (getKeyReference() == DEAD_WEAK_KEY); + } + + public final void die() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + WeakKeyReference keyRef = (WeakKeyReference) valueRef.getKeyReference(); + keyRef.clear(); + valueRef.setKeyReference(DEAD_WEAK_KEY); + valueRef.clear(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDA.java new file mode 100644 index 0000000000000..b00d98a231ca4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDA.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDA extends FD { + protected static final long ACCESS_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FDA.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.ACCESS_TIME); + + volatile long accessTime; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDA() {} + + FDA( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + FDA(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInAccessOrder; + } + + public void setPreviousInVariableOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInVariableOrder() { + return nextInAccessOrder; + } + + public void setNextInVariableOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final long getAccessTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public final void setAccessTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public final Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public final void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public final Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public final void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDA<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDA<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAMS.java new file mode 100644 index 0000000000000..2e045ae5824cc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAMS.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDAMS extends FDA { + int queueType; + + FDAMS() {} + + FDAMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDAMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAMW.java new file mode 100644 index 0000000000000..a0c9d7dbe2405 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAMW.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDAMW extends FDA { + int queueType; + + int weight; + + int policyWeight; + + FDAMW() {} + + FDAMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDAMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAR.java new file mode 100644 index 0000000000000..c938adf18b5ac --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAR.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDAR extends FDA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FDAR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + FDAR() {} + + FDAR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FDAR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDARMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDARMS.java new file mode 100644 index 0000000000000..12788d7519a54 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDARMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDARMS extends FDAR { + int queueType; + + FDARMS() {} + + FDARMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDARMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDARMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDARMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDARMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDARMW.java new file mode 100644 index 0000000000000..ffb91b0d9de91 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDARMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDARMW extends FDAR { + int queueType; + + int weight; + + int policyWeight; + + FDARMW() {} + + FDARMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDARMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDARMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDARMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAW.java new file mode 100644 index 0000000000000..4f296434b8a34 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAW.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDAW extends FDA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FDAW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + FDAW() {} + + FDAW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FDAW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWMS.java new file mode 100644 index 0000000000000..aabc1958416bd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDAWMS extends FDAW { + int queueType; + + FDAWMS() {} + + FDAWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDAWMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWMW.java new file mode 100644 index 0000000000000..116a089eaed0b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDAWMW extends FDAW { + int queueType; + + int weight; + + int policyWeight; + + FDAWMW() {} + + FDAWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDAWMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWR.java new file mode 100644 index 0000000000000..9da7dcf830b18 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWR.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDAWR extends FDAW { + FDAWR() {} + + FDAWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDAWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWRMS.java new file mode 100644 index 0000000000000..28dfb9a6e2645 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWRMS.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDAWRMS extends FDAWR { + int queueType; + + FDAWRMS() {} + + FDAWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDAWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWRMW.java new file mode 100644 index 0000000000000..d5afb89b0b02b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDAWRMW.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDAWRMW extends FDAWR { + int queueType; + + int weight; + + int policyWeight; + + FDAWRMW() {} + + FDAWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDAWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDAWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDAWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDMS.java new file mode 100644 index 0000000000000..98f252e29d95b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDMS.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDMS extends FD { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDMS() {} + + FDMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDMW.java new file mode 100644 index 0000000000000..a33d3b7a83e76 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDMW.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDMW extends FD { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDMW() {} + + FDMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDR.java new file mode 100644 index 0000000000000..9b95905a1adbd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDR.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDR extends FD { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FDR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + FDR() {} + + FDR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FDR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDRMS.java new file mode 100644 index 0000000000000..2783205dc9f3b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDRMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDRMS extends FDR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDRMS() {} + + FDRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDRMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDRMW.java new file mode 100644 index 0000000000000..fc590d7594cb9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDRMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDRMW extends FDR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDRMW() {} + + FDRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDRMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDW.java new file mode 100644 index 0000000000000..8dc8cf4b9bb7f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDW.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDW extends FD { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FDW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + FDW() {} + + FDW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FDW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWMS.java new file mode 100644 index 0000000000000..d2ee3377b57c5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDWMS extends FDW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDWMS() {} + + FDWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWMW.java new file mode 100644 index 0000000000000..a99702725d9b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDWMW extends FDW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDWMW() {} + + FDWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWR.java new file mode 100644 index 0000000000000..3cb85a7672c3e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWR.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FDWR extends FDW { + FDWR() {} + + FDWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWRMS.java new file mode 100644 index 0000000000000..a18ce67e8705f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWRMS.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDWRMS extends FDWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDWRMS() {} + + FDWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FDWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWRMW.java new file mode 100644 index 0000000000000..a59b517fc4a20 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FDWRMW.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FDWRMW extends FDWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FDWRMW() {} + + FDWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FDWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FDWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FDWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FS.java new file mode 100644 index 0000000000000..7b557273a2f39 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FS.java @@ -0,0 +1,140 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.References.LookupKeyReference; +import org.apache.iotdb.github.benmanes.caffeine.cache.References.WeakKeyReference; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.Objects; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • WeakKeys + *
  • StrongValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FS extends Node implements NodeFactory { + protected static final long KEY_OFFSET = + UnsafeAccess.objectFieldOffset( + FS.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.KEY); + + protected static final long VALUE_OFFSET = + UnsafeAccess.objectFieldOffset( + FS.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.VALUE); + + volatile WeakKeyReference key; + + volatile V value; + + FS() {} + + FS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + this(new WeakKeyReference(key, keyReferenceQueue), value, valueReferenceQueue, weight, now); + } + + FS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + UnsafeAccess.UNSAFE.putObject(this, KEY_OFFSET, keyReference); + UnsafeAccess.UNSAFE.putObject(this, VALUE_OFFSET, value); + } + + public final K getKey() { + return ((Reference) UnsafeAccess.UNSAFE.getObject(this, KEY_OFFSET)).get(); + } + + public final Object getKeyReference() { + return UnsafeAccess.UNSAFE.getObject(this, KEY_OFFSET); + } + + public final V getValue() { + return value; + } + + public final Object getValueReference() { + return UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + } + + public final void setValue(V value, ReferenceQueue referenceQueue) { + UnsafeAccess.UNSAFE.putObject(this, VALUE_OFFSET, value); + } + + public final boolean containsValue(Object value) { + return Objects.equals(value, getValue()); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FS<>(keyReference, value, valueReferenceQueue, weight, now); + } + + public Object newLookupKey(Object key) { + return new LookupKeyReference<>(key); + } + + public Object newReferenceKey(K key, ReferenceQueue referenceQueue) { + return new WeakKeyReference(key, referenceQueue); + } + + public final boolean isAlive() { + Object key = getKeyReference(); + return (key != RETIRED_WEAK_KEY) && (key != DEAD_WEAK_KEY); + } + + public final boolean isRetired() { + return (getKeyReference() == RETIRED_WEAK_KEY); + } + + public final void retire() { + UnsafeAccess.UNSAFE.putObject(this, KEY_OFFSET, RETIRED_WEAK_KEY); + } + + public final boolean isDead() { + return (getKeyReference() == DEAD_WEAK_KEY); + } + + public final void die() { + UnsafeAccess.UNSAFE.putObject(this, VALUE_OFFSET, null); + UnsafeAccess.UNSAFE.putObject(this, KEY_OFFSET, DEAD_WEAK_KEY); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSA.java new file mode 100644 index 0000000000000..a7c0bb44c5134 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSA.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSA extends FS { + protected static final long ACCESS_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FSA.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.ACCESS_TIME); + + volatile long accessTime; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSA() {} + + FSA( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + FSA(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInAccessOrder; + } + + public void setPreviousInVariableOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInVariableOrder() { + return nextInAccessOrder; + } + + public void setNextInVariableOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final long getAccessTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public final void setAccessTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public final Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public final void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public final Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public final void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSA<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSA<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAMS.java new file mode 100644 index 0000000000000..df3ed8e8d0b27 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAMS.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSAMS extends FSA { + int queueType; + + FSAMS() {} + + FSAMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSAMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAMW.java new file mode 100644 index 0000000000000..90f10b87de690 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAMW.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSAMW extends FSA { + int queueType; + + int weight; + + int policyWeight; + + FSAMW() {} + + FSAMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSAMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAR.java new file mode 100644 index 0000000000000..9da2fb45c08f2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAR.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSAR extends FSA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FSAR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + FSAR() {} + + FSAR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FSAR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSARMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSARMS.java new file mode 100644 index 0000000000000..030c6fc8bffcc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSARMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSARMS extends FSAR { + int queueType; + + FSARMS() {} + + FSARMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSARMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSARMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSARMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSARMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSARMW.java new file mode 100644 index 0000000000000..8e5e7cd37ed5c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSARMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSARMW extends FSAR { + int queueType; + + int weight; + + int policyWeight; + + FSARMW() {} + + FSARMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSARMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSARMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSARMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAW.java new file mode 100644 index 0000000000000..2a43f904a5198 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAW.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSAW extends FSA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FSAW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + FSAW() {} + + FSAW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FSAW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWMS.java new file mode 100644 index 0000000000000..35dcaaf555699 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSAWMS extends FSAW { + int queueType; + + FSAWMS() {} + + FSAWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSAWMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWMW.java new file mode 100644 index 0000000000000..5f5f9917a4621 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSAWMW extends FSAW { + int queueType; + + int weight; + + int policyWeight; + + FSAWMW() {} + + FSAWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSAWMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWR.java new file mode 100644 index 0000000000000..c2c4a2a6cec93 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWR.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSAWR extends FSAW { + FSAWR() {} + + FSAWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSAWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWRMS.java new file mode 100644 index 0000000000000..5c7ba13d67e27 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWRMS.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSAWRMS extends FSAWR { + int queueType; + + FSAWRMS() {} + + FSAWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSAWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWRMW.java new file mode 100644 index 0000000000000..83eb280dda03c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSAWRMW.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSAWRMW extends FSAWR { + int queueType; + + int weight; + + int policyWeight; + + FSAWRMW() {} + + FSAWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSAWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSAWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSAWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSMS.java new file mode 100644 index 0000000000000..f13ae78a1fbc8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSMS.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSMS extends FS { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSMS() {} + + FSMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSMW.java new file mode 100644 index 0000000000000..11190a2fcc1b8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSMW.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSMW extends FS { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSMW() {} + + FSMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSR.java new file mode 100644 index 0000000000000..076794cb1c421 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSR.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSR extends FS { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FSR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + FSR() {} + + FSR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FSR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSRMS.java new file mode 100644 index 0000000000000..6555dd5e2ff96 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSRMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSRMS extends FSR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSRMS() {} + + FSRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSRMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSRMW.java new file mode 100644 index 0000000000000..2137c29558ab0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSRMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSRMW extends FSR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSRMW() {} + + FSRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSRMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSW.java new file mode 100644 index 0000000000000..c98ab3b2a939a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSW.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSW extends FS { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FSW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + FSW() {} + + FSW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FSW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWMS.java new file mode 100644 index 0000000000000..ba839b7e8fcc4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSWMS extends FSW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSWMS() {} + + FSWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWMW.java new file mode 100644 index 0000000000000..ad3e5999ab518 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSWMW extends FSW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSWMW() {} + + FSWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWR.java new file mode 100644 index 0000000000000..b4d04ab85b0c9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWR.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FSWR extends FSW { + FSWR() {} + + FSWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWRMS.java new file mode 100644 index 0000000000000..542e9082106cb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWRMS.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSWRMS extends FSWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSWRMS() {} + + FSWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FSWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWRMW.java new file mode 100644 index 0000000000000..3f0c17145b7c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FSWRMW.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FSWRMW extends FSWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FSWRMW() {} + + FSWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FSWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FSWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FSWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FW.java new file mode 100644 index 0000000000000..a10376b84ada4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FW.java @@ -0,0 +1,157 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.References.LookupKeyReference; +import org.apache.iotdb.github.benmanes.caffeine.cache.References.WeakKeyReference; +import org.apache.iotdb.github.benmanes.caffeine.cache.References.WeakValueReference; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • WeakKeys + *
  • WeakValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FW extends Node implements NodeFactory { + protected static final long VALUE_OFFSET = + UnsafeAccess.objectFieldOffset( + FW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.VALUE); + + volatile WeakValueReference value; + + FW() {} + + FW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + this(new WeakKeyReference(key, keyReferenceQueue), value, valueReferenceQueue, weight, now); + } + + FW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + UnsafeAccess.UNSAFE.putObject( + this, VALUE_OFFSET, new WeakValueReference(keyReference, value, valueReferenceQueue)); + } + + public final Object getKeyReference() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + return valueRef.getKeyReference(); + } + + public final K getKey() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + WeakKeyReference keyRef = (WeakKeyReference) valueRef.getKeyReference(); + return keyRef.get(); + } + + public final V getValue() { + for (; ; ) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + V referent = ref.get(); + if ((referent != null) || (ref == value)) { + return referent; + } + } + } + + public final Object getValueReference() { + return UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + } + + public final void setValue(V value, ReferenceQueue referenceQueue) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + UnsafeAccess.UNSAFE.putOrderedObject( + this, VALUE_OFFSET, new WeakValueReference(getKeyReference(), value, referenceQueue)); + ref.clear(); + } + + public final boolean containsValue(Object value) { + return getValue() == value; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FW<>(keyReference, value, valueReferenceQueue, weight, now); + } + + public Object newLookupKey(Object key) { + return new LookupKeyReference<>(key); + } + + public Object newReferenceKey(K key, ReferenceQueue referenceQueue) { + return new WeakKeyReference(key, referenceQueue); + } + + public boolean weakValues() { + return true; + } + + public final boolean isAlive() { + Object key = getKeyReference(); + return (key != RETIRED_WEAK_KEY) && (key != DEAD_WEAK_KEY); + } + + public final boolean isRetired() { + return (getKeyReference() == RETIRED_WEAK_KEY); + } + + public final void retire() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + WeakKeyReference keyRef = (WeakKeyReference) valueRef.getKeyReference(); + keyRef.clear(); + valueRef.setKeyReference(RETIRED_WEAK_KEY); + valueRef.clear(); + } + + public final boolean isDead() { + return (getKeyReference() == DEAD_WEAK_KEY); + } + + public final void die() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + WeakKeyReference keyRef = (WeakKeyReference) valueRef.getKeyReference(); + keyRef.clear(); + valueRef.setKeyReference(DEAD_WEAK_KEY); + valueRef.clear(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWA.java new file mode 100644 index 0000000000000..92fa0fce3fb1e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWA.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWA extends FW { + protected static final long ACCESS_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FWA.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.ACCESS_TIME); + + volatile long accessTime; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWA() {} + + FWA( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + FWA(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInAccessOrder; + } + + public void setPreviousInVariableOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInVariableOrder() { + return nextInAccessOrder; + } + + public void setNextInVariableOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final long getAccessTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public final void setAccessTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public final Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public final void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public final Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public final void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWA<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWA<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAMS.java new file mode 100644 index 0000000000000..67aae0c53e8c2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAMS.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWAMS extends FWA { + int queueType; + + FWAMS() {} + + FWAMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWAMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAMW.java new file mode 100644 index 0000000000000..8e40e4e36ed23 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAMW.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWAMW extends FWA { + int queueType; + + int weight; + + int policyWeight; + + FWAMW() {} + + FWAMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWAMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAR.java new file mode 100644 index 0000000000000..0fcc5959d8526 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAR.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWAR extends FWA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FWAR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + FWAR() {} + + FWAR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FWAR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWARMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWARMS.java new file mode 100644 index 0000000000000..8624e3598cf85 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWARMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWARMS extends FWAR { + int queueType; + + FWARMS() {} + + FWARMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWARMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWARMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWARMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWARMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWARMW.java new file mode 100644 index 0000000000000..877aabbd23b03 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWARMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWARMW extends FWAR { + int queueType; + + int weight; + + int policyWeight; + + FWARMW() {} + + FWARMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWARMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWARMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWARMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAW.java new file mode 100644 index 0000000000000..58b3b32901a03 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAW.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWAW extends FWA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FWAW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + FWAW() {} + + FWAW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FWAW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWMS.java new file mode 100644 index 0000000000000..9c977db10eaf4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWAWMS extends FWAW { + int queueType; + + FWAWMS() {} + + FWAWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWAWMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWMW.java new file mode 100644 index 0000000000000..6eef32f3ff3a4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWAWMW extends FWAW { + int queueType; + + int weight; + + int policyWeight; + + FWAWMW() {} + + FWAWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWAWMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWR.java new file mode 100644 index 0000000000000..ec23bc6b7c369 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWR.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWAWR extends FWAW { + FWAWR() {} + + FWAWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWAWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWRMS.java new file mode 100644 index 0000000000000..6e2facea98012 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWRMS.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWAWRMS extends FWAWR { + int queueType; + + FWAWRMS() {} + + FWAWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWAWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWRMW.java new file mode 100644 index 0000000000000..331986c31438a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWAWRMW.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWAWRMW extends FWAWR { + int queueType; + + int weight; + + int policyWeight; + + FWAWRMW() {} + + FWAWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWAWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWAWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWAWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWMS.java new file mode 100644 index 0000000000000..29d4b8dc676e3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWMS.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWMS extends FW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWMS() {} + + FWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWMW.java new file mode 100644 index 0000000000000..ea24b36211174 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWMW.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWMW extends FW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWMW() {} + + FWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWR.java new file mode 100644 index 0000000000000..4b9af05912c88 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWR.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWR extends FW { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FWR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + FWR() {} + + FWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWRMS.java new file mode 100644 index 0000000000000..fb319dd81493c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWRMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWRMS extends FWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWRMS() {} + + FWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWRMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWRMW.java new file mode 100644 index 0000000000000..b6fbf41d3e9cf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWRMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWRMW extends FWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWRMW() {} + + FWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWRMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWW.java new file mode 100644 index 0000000000000..d628e44a3037b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWW.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWW extends FW { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + FWW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + FWW() {} + + FWW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + FWW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWMS.java new file mode 100644 index 0000000000000..9575c6f125bee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWWMS extends FWW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWWMS() {} + + FWWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWMW.java new file mode 100644 index 0000000000000..7c1d94507b5de --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWWMW extends FWW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWWMW() {} + + FWWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWR.java new file mode 100644 index 0000000000000..fcc6beda81aa7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWR.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class FWWR extends FWW { + FWWR() {} + + FWWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWRMS.java new file mode 100644 index 0000000000000..d56c969dc678b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWRMS.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWWRMS extends FWWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWWRMS() {} + + FWWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + FWWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWRMW.java new file mode 100644 index 0000000000000..5e7af0b2ec483 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FWWRMW.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class FWWRMW extends FWWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + FWWRMW() {} + + FWWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + FWWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new FWWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new FWWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FrequencySketch.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FrequencySketch.java new file mode 100644 index 0000000000000..7d061b30b7a79 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/FrequencySketch.java @@ -0,0 +1,207 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.requireArgument; + +/** + * A probabilistic multiset for estimating the popularity of an element within a time window. The + * maximum frequency of an element is limited to 15 (4-bits) and an aging process periodically + * halves the popularity of all elements. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +final class FrequencySketch { + + /* + * This class maintains a 4-bit CountMinSketch [1] with periodic aging to provide the popularity + * history for the TinyLfu admission policy [2]. The time and space efficiency of the sketch + * allows it to cheaply estimate the frequency of an entry in a stream of cache access events. + * + * The counter matrix is represented as a single dimensional array holding 16 counters per slot. A + * fixed depth of four balances the accuracy and cost, resulting in a width of four times the + * length of the array. To retain an accurate estimation the array's length equals the maximum + * number of entries in the cache, increased to the closest power-of-two to exploit more efficient + * bit masking. This configuration results in a confidence of 93.75% and error bound of e / width. + * + * The frequency of all entries is aged periodically using a sampling window based on the maximum + * number of entries in the cache. This is referred to as the reset operation by TinyLfu and keeps + * the sketch fresh by dividing all counters by two and subtracting based on the number of odd + * counters found. The O(n) cost of aging is amortized, ideal for hardware prefetching, and uses + * inexpensive bit manipulations per array location. + * + * [1] An Improved Data Stream Summary: The Count-Min Sketch and its Applications + * http://dimacs.rutgers.edu/~graham/pubs/papers/cm-full.pdf + * [2] TinyLFU: A Highly Efficient Cache Admission Policy + * https://dl.acm.org/citation.cfm?id=3149371 + */ + + static final long[] SEED = { // A mixture of seeds from FNV-1a, CityHash, and Murmur3 + 0xc3a5c85c97cb3127L, 0xb492b66fbe98f273L, 0x9ae16a3b2f90404fL, 0xcbf29ce484222325L + }; + static final long RESET_MASK = 0x7777777777777777L; + static final long ONE_MASK = 0x1111111111111111L; + + int sampleSize; + int tableMask; + long[] table; + int size; + + /** + * Creates a lazily initialized frequency sketch, requiring {@link #ensureCapacity} be called when + * the maximum size of the cache has been determined. + */ + @SuppressWarnings("NullAway.Init") + public FrequencySketch() {} + + /** + * Initializes and increases the capacity of this FrequencySketch instance, if necessary, + * to ensure that it can accurately estimate the popularity of elements given the maximum size of + * the cache. This operation forgets all previous counts when resizing. + * + * @param maximumSize the maximum size of the cache + */ + public void ensureCapacity(long maximumSize) { + requireArgument(maximumSize >= 0); + int maximum = (int) Math.min(maximumSize, Integer.MAX_VALUE >>> 1); + if ((table != null) && (table.length >= maximum)) { + return; + } + + table = new long[(maximum == 0) ? 1 : Caffeine.ceilingPowerOfTwo(maximum)]; + tableMask = Math.max(0, table.length - 1); + sampleSize = (maximumSize == 0) ? 10 : (10 * maximum); + if (sampleSize <= 0) { + sampleSize = Integer.MAX_VALUE; + } + size = 0; + } + + /** + * Returns if the sketch has not yet been initialized, requiring that {@link #ensureCapacity} is + * called before it begins to track frequencies. + */ + public boolean isNotInitialized() { + return (table == null); + } + + /** + * Returns the estimated number of occurrences of an element, up to the maximum (15). + * + * @param e the element to count occurrences of + * @return the estimated number of occurrences of the element; possibly zero but never negative + */ + public int frequency(E e) { + if (isNotInitialized()) { + return 0; + } + + int hash = spread(e.hashCode()); + int start = (hash & 3) << 2; + int frequency = Integer.MAX_VALUE; + for (int i = 0; i < 4; i++) { + int index = indexOf(hash, i); + int count = (int) ((table[index] >>> ((start + i) << 2)) & 0xfL); + frequency = Math.min(frequency, count); + } + return frequency; + } + + /** + * Increments the popularity of the element if it does not exceed the maximum (15). The popularity + * of all elements will be periodically down sampled when the observed events exceeds a threshold. + * This process provides a frequency aging to allow expired long term entries to fade away. + * + * @param e the element to add + */ + public void increment(E e) { + if (isNotInitialized()) { + return; + } + + int hash = spread(e.hashCode()); + int start = (hash & 3) << 2; + + // Loop unrolling improves throughput by 5m ops/s + int index0 = indexOf(hash, 0); + int index1 = indexOf(hash, 1); + int index2 = indexOf(hash, 2); + int index3 = indexOf(hash, 3); + + boolean added = incrementAt(index0, start); + added |= incrementAt(index1, start + 1); + added |= incrementAt(index2, start + 2); + added |= incrementAt(index3, start + 3); + + if (added && (++size == sampleSize)) { + reset(); + } + } + + /** + * Increments the specified counter by 1 if it is not already at the maximum value (15). + * + * @param i the table index (16 counters) + * @param j the counter to increment + * @return if incremented + */ + boolean incrementAt(int i, int j) { + int offset = j << 2; + long mask = (0xfL << offset); + if ((table[i] & mask) != mask) { + table[i] += (1L << offset); + return true; + } + return false; + } + + /** Reduces every counter by half of its original value. */ + void reset() { + int count = 0; + for (int i = 0; i < table.length; i++) { + count += Long.bitCount(table[i] & ONE_MASK); + table[i] = (table[i] >>> 1) & RESET_MASK; + } + size = (size >>> 1) - (count >>> 2); + } + + /** + * Returns the table index for the counter at the specified depth. + * + * @param item the element's hash + * @param i the counter depth + * @return the table index + */ + int indexOf(int item, int i) { + long hash = (item + SEED[i]) * SEED[i]; + hash += (hash >>> 32); + return ((int) hash) & tableMask; + } + + /** + * Applies a supplemental hash function to a given hashCode, which defends against poor quality + * hash functions. + */ + int spread(int x) { + x = ((x >>> 16) ^ x) * 0x45d9f3b; + x = ((x >>> 16) ^ x) * 0x45d9f3b; + return (x >>> 16) ^ x; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LinkedDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LinkedDeque.java new file mode 100644 index 0000000000000..bf6f5c3841e2b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LinkedDeque.java @@ -0,0 +1,170 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Comparator; +import java.util.Deque; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A linked list extension of the {@link Deque} interface where the link pointers are tightly + * integrated with the element. Linked deques have no capacity restrictions; they grow as necessary + * to support usage. They are not thread-safe; in the absence of external synchronization, they do + * not support concurrent access by multiple threads. Null elements are prohibited. + * + *

Most LinkedDeque operations run in constant time by assuming that the element + * parameter is associated with the deque instance. Any usage that violates this assumption will + * result in non-deterministic behavior. + * + *

An element can exist in only one instance of a deque implementation, but may exist in multiple + * implementations. Each implementation must define unique names for accessing and modifying its + * link pointers. + * + *

The iterators returned by this class are not fail-fast: If the deque is + * modified at any time after the iterator is created, the iterator will be in an unknown state. + * Thus, in the face of concurrent modification, the iterator risks arbitrary, non-deterministic + * behavior at an undetermined time in the future. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of elements held in this collection + */ +interface LinkedDeque extends Deque { + + /** + * Returns if the element is at the front of the deque. + * + * @param e the linked element + */ + boolean isFirst(E e); + + /** + * Returns if the element is at the back of the deque. + * + * @param e the linked element + */ + boolean isLast(E e); + + /** + * Moves the element to the front of the deque so that it becomes the first element. + * + * @param e the linked element + */ + void moveToFront(E e); + + /** + * Moves the element to the back of the deque so that it becomes the last element. + * + * @param e the linked element + */ + void moveToBack(E e); + + /** + * Retrieves the previous element or null if either the element is unlinked or the first + * element on the deque. + */ + E getPrevious(E e); + + /** Sets the previous element or null if there is no link. */ + void setPrevious(E e, E prev); + + /** + * Retrieves the next element or null if either the element is unlinked or the last + * element on the deque. + */ + E getNext(E e); + + /** Sets the next element or null if there is no link. */ + void setNext(E e, E next); + + @Override + PeekingIterator iterator(); + + @Override + PeekingIterator descendingIterator(); + + interface PeekingIterator extends Iterator { + + /** Returns the next element in the iteration, without advancing the iteration. */ + E peek(); + + /** Returns an iterator that returns the first iteration followed by the second iteration. */ + static PeekingIterator concat(PeekingIterator first, PeekingIterator second) { + return new PeekingIterator() { + @Override + public boolean hasNext() { + return first.hasNext() || second.hasNext(); + } + + @Override + public E next() { + if (first.hasNext()) { + return first.next(); + } else if (second.hasNext()) { + return second.next(); + } + throw new NoSuchElementException(); + } + + @Override + public E peek() { + return first.hasNext() ? first.peek() : second.peek(); + } + }; + } + + /** Returns an iterator that selects the greater element from the backing iterators. */ + static PeekingIterator comparing( + PeekingIterator first, PeekingIterator second, Comparator comparator) { + return new PeekingIterator() { + @Override + public boolean hasNext() { + return first.hasNext() || second.hasNext(); + } + + @Override + public E next() { + if (!first.hasNext()) { + return second.next(); + } else if (!second.hasNext()) { + return first.next(); + } + E o1 = first.peek(); + E o2 = second.peek(); + boolean greaterOrEqual = (comparator.compare(o1, o2) >= 0); + return greaterOrEqual ? first.next() : second.next(); + } + + @Override + public E peek() { + if (!first.hasNext()) { + return second.peek(); + } else if (!second.hasNext()) { + return first.peek(); + } + E o1 = first.peek(); + E o2 = second.peek(); + boolean greaterOrEqual = (comparator.compare(o1, o2) >= 0); + return greaterOrEqual ? first.peek() : second.peek(); + } + }; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LoadingCache.java new file mode 100644 index 0000000000000..e63ec26123535 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LoadingCache.java @@ -0,0 +1,107 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Map; +import java.util.concurrent.CompletionException; + +/** + * A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and + * are stored in the cache until either evicted or manually invalidated. + * + *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of keys maintained by this cache + * @param the type of mapped values + */ +public interface LoadingCache extends Cache { + + /** + * Returns the value associated with the {@code key} in this cache, obtaining that value from + * {@link CacheLoader#load(Object)} if necessary. + * + *

If another call to {@link #get} is currently loading the value for the {@code key}, this + * thread simply waits for that thread to finish and returns its loaded value. Note that multiple + * threads can concurrently load values for distinct keys. + * + *

If the specified key is not already associated with a value, attempts to compute its value + * and enters it into this cache unless {@code null}. The entire method invocation is performed + * atomically, so the function is applied at most once per key. Some attempted update operations + * on this cache by other threads may be blocked while the computation is in progress, so the + * computation should be short and simple, and must not attempt to update any other mappings of + * this cache. + * + * @param key key with which the specified value is to be associated + * @return the current (existing or computed) value associated with the specified key, or null if + * the computed value is null + * @throws NullPointerException if the specified key is null + * @throws IllegalStateException if the computation detectably attempts a recursive update to this + * cache that would otherwise never complete + * @throws CompletionException if a checked exception was thrown while loading the value + * @throws RuntimeException or Error if the {@link CacheLoader} does so, in which case the mapping + * is left unestablished + */ + V get(K key); + + /** + * Returns a map of the values associated with the {@code keys}, creating or retrieving those + * values if necessary. The returned map contains entries that were already cached, combined with + * the newly loaded entries; it will never contain null keys or values. + * + *

Caches loaded by a {@link CacheLoader} will issue a single request to {@link + * CacheLoader#loadAll} for all keys which are not already present in the cache. All entries + * returned by {@link CacheLoader#loadAll} will be stored in the cache, over-writing any + * previously cached values. If another call to {@link #get} tries to load the value for a key in + * {@code keys}, implementations may either have that thread load the entry or simply wait for + * this thread to finish and returns the loaded value. In the case of overlapping non-blocking + * loads, the last load to complete will replace the existing entry. Note that multiple threads + * can concurrently load values for distinct keys. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @param keys the keys whose associated values are to be returned + * @return the unmodifiable mapping of keys to values for the specified keys in this cache + * @throws NullPointerException if the specified collection is null or contains a null element + * @throws CompletionException if a checked exception was thrown while loading the value + * @throws RuntimeException or Error if the {@link CacheLoader} does so, if {@link + * CacheLoader#loadAll} returns {@code null}, or returns a map containing null keys or values. + * In all cases, the mapping is left unestablished. + */ + Map getAll(Iterable keys); + + /** + * Loads a new value for the {@code key}, asynchronously. While the new value is loading the + * previous value (if any) will continue to be returned by {@code get(key)} unless it is evicted. + * If the new value is loaded successfully it will replace the previous value in the cache; if an + * exception is thrown while refreshing the previous value will remain, and the exception will + * be logged (using {@link java.util.logging.Logger}) and swallowed. + * + *

Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#reload} if the cache + * currently contains a value for the {@code key}, and {@link CacheLoader#load} otherwise. Loading + * is asynchronous by delegating to the default executor. + * + * @param key key with which a value may be associated + * @throws NullPointerException if the specified key is null + */ + void refresh(K key); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalAsyncCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalAsyncCache.java new file mode 100644 index 0000000000000..4f3ff8af4e8ab --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalAsyncCache.java @@ -0,0 +1,1228 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.LocalAsyncCache.AsyncBulkCompleter.NullMapCompletionException; +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.CacheStats; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides a skeletal implementation of the {@link AsyncCache} interface to minimize the + * effort required to implement a {@link LocalCache}. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +interface LocalAsyncCache extends AsyncCache { + Logger logger = Logger.getLogger(LocalAsyncCache.class.getName()); + + /** Returns the backing {@link LocalCache} data store. */ + LocalCache> cache(); + + /** Returns the policy supported by this implementation and its configuration. */ + Policy policy(); + + @Override + default CompletableFuture getIfPresent(Object key) { + return cache().getIfPresent(key, /* recordStats */ true); + } + + @Override + default CompletableFuture get(K key, Function mappingFunction) { + requireNonNull(mappingFunction); + return get( + key, + (k1, executor) -> + CompletableFuture.supplyAsync(() -> mappingFunction.apply(key), executor)); + } + + @Override + default CompletableFuture get( + K key, BiFunction> mappingFunction) { + return get(key, mappingFunction, /* recordStats */ true); + } + + @SuppressWarnings({"FutureReturnValueIgnored", "NullAway"}) + default CompletableFuture get( + K key, + BiFunction> mappingFunction, + boolean recordStats) { + long startTime = cache().statsTicker().read(); + @SuppressWarnings({"unchecked", "rawtypes"}) + CompletableFuture[] result = new CompletableFuture[1]; + CompletableFuture future = + cache() + .computeIfAbsent( + key, + k -> { + result[0] = mappingFunction.apply(key, cache().executor()); + return requireNonNull(result[0]); + }, + recordStats, /* recordLoad */ + false); + if (result[0] != null) { + handleCompletion(key, result[0], startTime, /* recordMiss */ false); + } + return future; + } + + @Override + default CompletableFuture> getAll( + Iterable keys, Function, Map> mappingFunction) { + requireNonNull(mappingFunction); + return getAll( + keys, + (keysToLoad, executor) -> + CompletableFuture.supplyAsync(() -> mappingFunction.apply(keysToLoad), executor)); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + default CompletableFuture> getAll( + Iterable keys, + BiFunction, Executor, CompletableFuture>> mappingFunction) { + requireNonNull(mappingFunction); + requireNonNull(keys); + + Map> futures = new LinkedHashMap<>(); + Map> proxies = new HashMap<>(); + for (K key : keys) { + if (futures.containsKey(key)) { + continue; + } + CompletableFuture future = cache().getIfPresent(key, /* recordStats */ false); + if (future == null) { + CompletableFuture proxy = new CompletableFuture<>(); + future = cache().putIfAbsent(key, proxy); + if (future == null) { + future = proxy; + proxies.put(key, proxy); + } + } + futures.put(key, future); + } + cache().statsCounter().recordMisses(proxies.size()); + cache().statsCounter().recordHits(futures.size() - proxies.size()); + if (proxies.isEmpty()) { + return composeResult(futures); + } + + AsyncBulkCompleter completer = new AsyncBulkCompleter<>(cache(), proxies); + try { + mappingFunction.apply(proxies.keySet(), cache().executor()).whenComplete(completer); + return composeResult(futures); + } catch (Throwable t) { + completer.accept(/* result */ null, t); + throw t; + } + } + + /** + * Returns a future that waits for all of the dependent futures to complete and returns the + * combined mapping if successful. If any future fails then it is automatically removed from the + * cache if still present. + */ + default CompletableFuture> composeResult(Map> futures) { + if (futures.isEmpty()) { + return CompletableFuture.completedFuture(Collections.emptyMap()); + } + @SuppressWarnings("rawtypes") + CompletableFuture[] array = futures.values().toArray(new CompletableFuture[0]); + return CompletableFuture.allOf(array) + .thenApply( + ignored -> { + Map result = new LinkedHashMap<>(futures.size()); + futures.forEach( + (key, future) -> { + V value = future.getNow(null); + if (value != null) { + result.put(key, value); + } + }); + return Collections.unmodifiableMap(result); + }); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + default void put(K key, CompletableFuture valueFuture) { + if (valueFuture.isCompletedExceptionally() + || (valueFuture.isDone() && (valueFuture.join() == null))) { + cache().statsCounter().recordLoadFailure(0L); + cache().remove(key); + return; + } + long startTime = cache().statsTicker().read(); + cache().put(key, valueFuture); + handleCompletion(key, valueFuture, startTime, /* recordMiss */ false); + } + + @SuppressWarnings("FutureReturnValueIgnored") + default void handleCompletion( + K key, CompletableFuture valueFuture, long startTime, boolean recordMiss) { + AtomicBoolean completed = new AtomicBoolean(); + valueFuture.whenComplete( + (value, error) -> { + if (!completed.compareAndSet(false, true)) { + // Ignore multiple invocations due to ForkJoinPool retrying on delays + return; + } + long loadTime = cache().statsTicker().read() - startTime; + if (value == null) { + if ((error != null) + && !(error instanceof CancellationException) + && !(error instanceof TimeoutException)) { + logger.log(Level.WARNING, "Exception thrown during asynchronous load", error); + } + cache().remove(key, valueFuture); + cache().statsCounter().recordLoadFailure(loadTime); + if (recordMiss) { + cache().statsCounter().recordMisses(1); + } + } else { + // update the weight and expiration timestamps + cache().replace(key, valueFuture, valueFuture); + cache().statsCounter().recordLoadSuccess(loadTime); + if (recordMiss) { + cache().statsCounter().recordMisses(1); + } + } + }); + } + + /** A function executed asynchronously after a bulk load completes. */ + final class AsyncBulkCompleter implements BiConsumer, Throwable> { + private final LocalCache> cache; + private final Map> proxies; + private final long startTime; + + AsyncBulkCompleter( + LocalCache> cache, Map> proxies) { + this.startTime = cache.statsTicker().read(); + this.proxies = proxies; + this.cache = cache; + } + + @Override + public void accept(Map result, Throwable error) { + long loadTime = cache.statsTicker().read() - startTime; + + if (result == null) { + if (error == null) { + error = new NullMapCompletionException(); + } + for (Map.Entry> entry : proxies.entrySet()) { + cache.remove(entry.getKey(), entry.getValue()); + entry.getValue().obtrudeException(error); + } + cache.statsCounter().recordLoadFailure(loadTime); + if (!(error instanceof CancellationException) && !(error instanceof TimeoutException)) { + logger.log(Level.WARNING, "Exception thrown during asynchronous load", error); + } + } else { + fillProxies(result); + addNewEntries(result); + cache.statsCounter().recordLoadSuccess(loadTime); + } + } + + /** Populates the proxies with the computed result. */ + private void fillProxies(Map result) { + proxies.forEach( + (key, future) -> { + V value = result.get(key); + future.obtrudeValue(value); + if (value == null) { + cache.remove(key, future); + } else { + // update the weight and expiration timestamps + cache.replace(key, future, future); + } + }); + } + + /** Adds to the cache any extra entries computed that were not requested. */ + private void addNewEntries(Map result) { + if (proxies.size() == result.size()) { + return; + } + result.forEach( + (key, value) -> { + if (!proxies.containsKey(key)) { + cache.put(key, CompletableFuture.completedFuture(value)); + } + }); + } + + static final class NullMapCompletionException extends CompletionException { + private static final long serialVersionUID = 1L; + + public NullMapCompletionException() { + super("null map", null); + } + } + } + + /* --------------- Asynchronous view --------------- */ + final class AsyncAsMapView implements ConcurrentMap> { + final LocalAsyncCache asyncCache; + + AsyncAsMapView(LocalAsyncCache asyncCache) { + this.asyncCache = requireNonNull(asyncCache); + } + + @Override + public boolean isEmpty() { + return asyncCache.cache().isEmpty(); + } + + @Override + public int size() { + return asyncCache.cache().size(); + } + + @Override + public void clear() { + asyncCache.cache().clear(); + } + + @Override + public boolean containsKey(Object key) { + return asyncCache.cache().containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return asyncCache.cache().containsValue(value); + } + + @Override + public CompletableFuture get(Object key) { + return asyncCache.cache().get(key); + } + + @Override + public CompletableFuture putIfAbsent(K key, CompletableFuture value) { + CompletableFuture prior = asyncCache.cache().putIfAbsent(key, value); + long startTime = asyncCache.cache().statsTicker().read(); + if (prior == null) { + asyncCache.handleCompletion(key, value, startTime, /* recordMiss */ false); + } + return prior; + } + + @Override + public CompletableFuture put(K key, CompletableFuture value) { + CompletableFuture prior = asyncCache.cache().put(key, value); + long startTime = asyncCache.cache().statsTicker().read(); + asyncCache.handleCompletion(key, value, startTime, /* recordMiss */ false); + return prior; + } + + @SuppressWarnings("FutureReturnValueIgnored") + @Override + public void putAll(Map> map) { + map.forEach(this::put); + } + + @Override + public CompletableFuture replace(K key, CompletableFuture value) { + CompletableFuture prior = asyncCache.cache().replace(key, value); + long startTime = asyncCache.cache().statsTicker().read(); + if (prior != null) { + asyncCache.handleCompletion(key, value, startTime, /* recordMiss */ false); + } + return prior; + } + + @Override + public boolean replace(K key, CompletableFuture oldValue, CompletableFuture newValue) { + boolean replaced = asyncCache.cache().replace(key, oldValue, newValue); + long startTime = asyncCache.cache().statsTicker().read(); + if (replaced) { + asyncCache.handleCompletion(key, newValue, startTime, /* recordMiss */ false); + } + return replaced; + } + + @Override + public CompletableFuture remove(Object key) { + return asyncCache.cache().remove(key); + } + + @Override + public boolean remove(Object key, Object value) { + return asyncCache.cache().remove(key, value); + } + + @SuppressWarnings("FutureReturnValueIgnored") + @Override + public CompletableFuture computeIfAbsent( + K key, Function> mappingFunction) { + requireNonNull(mappingFunction); + @SuppressWarnings({"rawtypes", "unchecked"}) + CompletableFuture[] result = new CompletableFuture[1]; + long startTime = asyncCache.cache().statsTicker().read(); + CompletableFuture future = + asyncCache + .cache() + .computeIfAbsent( + key, + k -> { + result[0] = mappingFunction.apply(k); + return result[0]; + }, /* recordStats */ + false, /* recordLoad */ + false); + + if (result[0] == null) { + if ((future != null) && asyncCache.cache().isRecordingStats()) { + future.whenComplete( + (r, e) -> { + if ((r != null) || (e == null)) { + asyncCache.cache().statsCounter().recordHits(1); + } + }); + } + } else { + asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss */ true); + } + return future; + } + + @Override + public CompletableFuture computeIfPresent( + K key, + BiFunction, ? extends CompletableFuture> + remappingFunction) { + requireNonNull(remappingFunction); + + @SuppressWarnings({"rawtypes", "unchecked"}) + CompletableFuture[] result = new CompletableFuture[1]; + long startTime = asyncCache.cache().statsTicker().read(); + asyncCache + .cache() + .compute( + key, + (k, oldValue) -> { + result[0] = (oldValue == null) ? null : remappingFunction.apply(k, oldValue); + return result[0]; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (result[0] != null) { + asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss */ false); + } + return result[0]; + } + + @Override + public CompletableFuture compute( + K key, + BiFunction, ? extends CompletableFuture> + remappingFunction) { + requireNonNull(remappingFunction); + + @SuppressWarnings({"rawtypes", "unchecked"}) + CompletableFuture[] result = new CompletableFuture[1]; + long startTime = asyncCache.cache().statsTicker().read(); + asyncCache + .cache() + .compute( + key, + (k, oldValue) -> { + result[0] = remappingFunction.apply(k, oldValue); + return result[0]; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (result[0] != null) { + asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss */ false); + } + return result[0]; + } + + @Override + public CompletableFuture merge( + K key, + CompletableFuture value, + BiFunction< + ? super CompletableFuture, + ? super CompletableFuture, + ? extends CompletableFuture> + remappingFunction) { + requireNonNull(value); + requireNonNull(remappingFunction); + + @SuppressWarnings({"rawtypes", "unchecked"}) + CompletableFuture[] result = new CompletableFuture[1]; + long startTime = asyncCache.cache().statsTicker().read(); + asyncCache + .cache() + .compute( + key, + (k, oldValue) -> { + result[0] = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); + return result[0]; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (result[0] != null) { + asyncCache.handleCompletion(key, result[0], startTime, /* recordMiss */ false); + } + return result[0]; + } + + @Override + public Set keySet() { + return asyncCache.cache().keySet(); + } + + @Override + public Collection> values() { + return asyncCache.cache().values(); + } + + @Override + public Set>> entrySet() { + return asyncCache.cache().entrySet(); + } + + @Override + public boolean equals(Object o) { + return asyncCache.cache().equals(o); + } + + @Override + public int hashCode() { + return asyncCache.cache().hashCode(); + } + + @Override + public String toString() { + return asyncCache.cache().toString(); + } + } + + /* --------------- Synchronous view --------------- */ + final class CacheView extends AbstractCacheView { + private static final long serialVersionUID = 1L; + + final LocalAsyncCache asyncCache; + + CacheView(LocalAsyncCache asyncCache) { + this.asyncCache = requireNonNull(asyncCache); + } + + @Override + LocalAsyncCache asyncCache() { + return asyncCache; + } + } + + @SuppressWarnings("serial") + abstract class AbstractCacheView implements Cache, Serializable { + transient AsMapView asMapView; + + abstract LocalAsyncCache asyncCache(); + + @Override + public V getIfPresent(Object key) { + CompletableFuture future = asyncCache().cache().getIfPresent(key, /* recordStats */ true); + return Async.getIfReady(future); + } + + @Override + public Map getAllPresent(Iterable keys) { + Set uniqueKeys = new LinkedHashSet<>(); + for (Object key : keys) { + uniqueKeys.add(key); + } + + int misses = 0; + Map result = new LinkedHashMap<>(); + for (Object key : uniqueKeys) { + CompletableFuture future = asyncCache().cache().get(key); + Object value = Async.getIfReady(future); + if (value == null) { + misses++; + } else { + result.put(key, value); + } + } + asyncCache().cache().statsCounter().recordMisses(misses); + asyncCache().cache().statsCounter().recordHits(result.size()); + + @SuppressWarnings("unchecked") + Map castedResult = (Map) result; + return Collections.unmodifiableMap(castedResult); + } + + @Override + public V get(K key, Function mappingFunction) { + return resolve(asyncCache().get(key, mappingFunction)); + } + + @Override + public Map getAll( + Iterable keys, Function, Map> mappingFunction) { + return resolve(asyncCache().getAll(keys, mappingFunction)); + } + + @SuppressWarnings({"PMD.AvoidThrowingNullPointerException", "PMD.PreserveStackTrace"}) + protected static T resolve(CompletableFuture future) throws Error { + try { + return future.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof NullMapCompletionException) { + throw new NullPointerException(e.getCause().getMessage()); + } else if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } else if (e.getCause() instanceof Error) { + throw (Error) e.getCause(); + } + throw new CompletionException(e.getCause()); + } catch (InterruptedException e) { + throw new CompletionException(e); + } + } + + @Override + public void put(K key, V value) { + requireNonNull(value); + asyncCache().cache().put(key, CompletableFuture.completedFuture(value)); + } + + @Override + public void putAll(Map map) { + map.forEach(this::put); + } + + @Override + public void invalidate(Object key) { + asyncCache().cache().remove(key); + } + + @Override + public void invalidateAll(Iterable keys) { + asyncCache().cache().invalidateAll(keys); + } + + @Override + public void invalidateAll() { + asyncCache().cache().clear(); + } + + @Override + public long estimatedSize() { + return asyncCache().cache().estimatedSize(); + } + + @Override + public CacheStats stats() { + return asyncCache().cache().statsCounter().snapshot(); + } + + @Override + public void cleanUp() { + asyncCache().cache().cleanUp(); + } + + @Override + public Policy policy() { + return asyncCache().policy(); + } + + @Override + public ConcurrentMap asMap() { + return (asMapView == null) ? (asMapView = new AsMapView<>(asyncCache().cache())) : asMapView; + } + } + + final class AsMapView extends AbstractMap implements ConcurrentMap { + final LocalCache> delegate; + + Collection values; + Set> entries; + + AsMapView(LocalCache> delegate) { + this.delegate = delegate; + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + requireNonNull(value); + + for (CompletableFuture valueFuture : delegate.values()) { + if (value.equals(Async.getIfReady(valueFuture))) { + return true; + } + } + return false; + } + + @Override + public V get(Object key) { + return Async.getIfReady(delegate.get(key)); + } + + @Override + public V putIfAbsent(K key, V value) { + requireNonNull(value); + + for (; ; ) { + CompletableFuture priorFuture = delegate.get(key); + if (priorFuture != null) { + if (!priorFuture.isDone()) { + Async.getWhenSuccessful(priorFuture); + continue; + } + + V prior = Async.getWhenSuccessful(priorFuture); + if (prior != null) { + return prior; + } + } + + boolean[] added = {false}; + CompletableFuture computed = + delegate.compute( + key, + (k, valueFuture) -> { + added[0] = + (valueFuture == null) + || (valueFuture.isDone() && (Async.getIfReady(valueFuture) == null)); + return added[0] ? CompletableFuture.completedFuture(value) : valueFuture; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (added[0]) { + return null; + } else { + V prior = Async.getWhenSuccessful(computed); + if (prior != null) { + return prior; + } + } + } + } + + @Override + public V put(K key, V value) { + requireNonNull(value); + CompletableFuture oldValueFuture = + delegate.put(key, CompletableFuture.completedFuture(value)); + return Async.getWhenSuccessful(oldValueFuture); + } + + @Override + public V remove(Object key) { + CompletableFuture oldValueFuture = delegate.remove(key); + return Async.getWhenSuccessful(oldValueFuture); + } + + @Override + public boolean remove(Object key, Object value) { + requireNonNull(key); + if (value == null) { + return false; + } + + @SuppressWarnings("unchecked") + K castedKey = (K) key; + boolean[] done = {false}; + boolean[] removed = {false}; + for (; ; ) { + CompletableFuture future = delegate.get(key); + if ((future == null) || future.isCompletedExceptionally()) { + return false; + } + + Async.getWhenSuccessful(future); + delegate.compute( + castedKey, + (k, oldValueFuture) -> { + if (oldValueFuture == null) { + done[0] = true; + return null; + } else if (!oldValueFuture.isDone()) { + return oldValueFuture; + } + + done[0] = true; + V oldValue = Async.getIfReady(oldValueFuture); + removed[0] = value.equals(oldValue); + return (oldValue == null) || removed[0] ? null : oldValueFuture; + }, /* recordStats */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + true); + + if (done[0]) { + return removed[0]; + } + } + } + + @Override + public V replace(K key, V value) { + requireNonNull(value); + + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + boolean[] done = {false}; + for (; ; ) { + CompletableFuture future = delegate.get(key); + if ((future == null) || future.isCompletedExceptionally()) { + return null; + } + + Async.getWhenSuccessful(future); + delegate.compute( + key, + (k, oldValueFuture) -> { + if (oldValueFuture == null) { + done[0] = true; + return null; + } else if (!oldValueFuture.isDone()) { + return oldValueFuture; + } + + done[0] = true; + oldValue[0] = Async.getIfReady(oldValueFuture); + return (oldValue[0] == null) ? null : CompletableFuture.completedFuture(value); + }, /* recordStats */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (done[0]) { + return oldValue[0]; + } + } + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + requireNonNull(oldValue); + requireNonNull(newValue); + + boolean[] done = {false}; + boolean[] replaced = {false}; + for (; ; ) { + CompletableFuture future = delegate.get(key); + if ((future == null) || future.isCompletedExceptionally()) { + return false; + } + + Async.getWhenSuccessful(future); + delegate.compute( + key, + (k, oldValueFuture) -> { + if (oldValueFuture == null) { + done[0] = true; + return null; + } else if (!oldValueFuture.isDone()) { + return oldValueFuture; + } + + done[0] = true; + replaced[0] = oldValue.equals(Async.getIfReady(oldValueFuture)); + return replaced[0] ? CompletableFuture.completedFuture(newValue) : oldValueFuture; + }, /* recordStats */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (done[0]) { + return replaced[0]; + } + } + } + + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + requireNonNull(mappingFunction); + + for (; ; ) { + CompletableFuture priorFuture = delegate.get(key); + if (priorFuture != null) { + if (!priorFuture.isDone()) { + Async.getWhenSuccessful(priorFuture); + continue; + } + + V prior = Async.getWhenSuccessful(priorFuture); + if (prior != null) { + delegate.statsCounter().recordHits(1); + return prior; + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + CompletableFuture[] future = new CompletableFuture[1]; + CompletableFuture computed = + delegate.compute( + key, + (k, valueFuture) -> { + if ((valueFuture != null) + && valueFuture.isDone() + && (Async.getIfReady(valueFuture) != null)) { + return valueFuture; + } + + V newValue = + delegate.statsAware(mappingFunction, /* recordLoad */ true).apply(key); + if (newValue == null) { + return null; + } + future[0] = CompletableFuture.completedFuture(newValue); + return future[0]; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + V result = Async.getWhenSuccessful(computed); + if ((computed == future[0]) || (result != null)) { + return result; + } + } + } + + @Override + public V computeIfPresent( + K key, BiFunction remappingFunction) { + requireNonNull(remappingFunction); + + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] newValue = (V[]) new Object[1]; + for (; ; ) { + Async.getWhenSuccessful(delegate.get(key)); + + CompletableFuture valueFuture = + delegate.computeIfPresent( + key, + (k, oldValueFuture) -> { + if (!oldValueFuture.isDone()) { + return oldValueFuture; + } + + V oldValue = Async.getIfReady(oldValueFuture); + if (oldValue == null) { + return null; + } + + newValue[0] = remappingFunction.apply(key, oldValue); + return (newValue[0] == null) + ? null + : CompletableFuture.completedFuture(newValue[0]); + }); + + if (newValue[0] != null) { + return newValue[0]; + } else if (valueFuture == null) { + return null; + } + } + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + requireNonNull(remappingFunction); + + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] newValue = (V[]) new Object[1]; + for (; ; ) { + Async.getWhenSuccessful(delegate.get(key)); + + CompletableFuture valueFuture = + delegate.compute( + key, + (k, oldValueFuture) -> { + if ((oldValueFuture != null) && !oldValueFuture.isDone()) { + return oldValueFuture; + } + + V oldValue = Async.getIfReady(oldValueFuture); + BiFunction function = + delegate.statsAware( + remappingFunction, /* recordMiss */ + false, /* recordLoad */ + true, + /* recordLoadFailure */ true); + newValue[0] = function.apply(key, oldValue); + return (newValue[0] == null) + ? null + : CompletableFuture.completedFuture(newValue[0]); + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + false); + + if (newValue[0] != null) { + return newValue[0]; + } else if (valueFuture == null) { + return null; + } + } + } + + @Override + public V merge( + K key, V value, BiFunction remappingFunction) { + requireNonNull(value); + requireNonNull(remappingFunction); + + CompletableFuture newValueFuture = CompletableFuture.completedFuture(value); + boolean[] merged = {false}; + for (; ; ) { + Async.getWhenSuccessful(delegate.get(key)); + + CompletableFuture mergedValueFuture = + delegate.merge( + key, + newValueFuture, + (oldValueFuture, valueFuture) -> { + if ((oldValueFuture != null) && !oldValueFuture.isDone()) { + return oldValueFuture; + } + + merged[0] = true; + V oldValue = Async.getIfReady(oldValueFuture); + if (oldValue == null) { + return valueFuture; + } + V mergedValue = remappingFunction.apply(oldValue, value); + if (mergedValue == null) { + return null; + } else if (mergedValue == oldValue) { + return oldValueFuture; + } else if (mergedValue == value) { + return valueFuture; + } + return CompletableFuture.completedFuture(mergedValue); + }); + + if (merged[0] || (mergedValueFuture == newValueFuture)) { + return Async.getWhenSuccessful(mergedValueFuture); + } + } + } + + @Override + public Set keySet() { + return delegate.keySet(); + } + + @Override + public Collection values() { + return (values == null) ? (values = new Values()) : values; + } + + @Override + public Set> entrySet() { + return (entries == null) ? (entries = new EntrySet()) : entries; + } + + private final class Values extends AbstractCollection { + + @Override + public boolean isEmpty() { + return AsMapView.this.isEmpty(); + } + + @Override + public int size() { + return AsMapView.this.size(); + } + + @Override + public boolean contains(Object o) { + return AsMapView.this.containsValue(o); + } + + @Override + public void clear() { + AsMapView.this.clear(); + } + + @Override + public Iterator iterator() { + return new Iterator() { + Iterator> iterator = entrySet().iterator(); + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public V next() { + return iterator.next().getValue(); + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + } + + private final class EntrySet extends AbstractSet> { + + @Override + public boolean isEmpty() { + return AsMapView.this.isEmpty(); + } + + @Override + public int size() { + return AsMapView.this.size(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry entry = (Entry) o; + Object key = entry.getKey(); + Object value = entry.getValue(); + if ((key == null) || (value == null)) { + return false; + } + V cachedValue = AsMapView.this.get(key); + return (cachedValue != null) && cachedValue.equals(value); + } + + @Override + public boolean remove(Object obj) { + if (!(obj instanceof Entry)) { + return false; + } + Entry entry = (Entry) obj; + return AsMapView.this.remove(entry.getKey(), entry.getValue()); + } + + @Override + public void clear() { + AsMapView.this.clear(); + } + + @Override + public Iterator> iterator() { + return new Iterator>() { + Iterator>> iterator = delegate.entrySet().iterator(); + Entry cursor; + K removalKey; + + @Override + public boolean hasNext() { + while ((cursor == null) && iterator.hasNext()) { + Entry> entry = iterator.next(); + V value = Async.getIfReady(entry.getValue()); + if (value != null) { + cursor = new WriteThroughEntry<>(AsMapView.this, entry.getKey(), value); + } + } + return (cursor != null); + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + @SuppressWarnings("NullAway") + K key = cursor.getKey(); + Entry entry = cursor; + removalKey = key; + cursor = null; + return entry; + } + + @Override + public void remove() { + Caffeine.requireState(removalKey != null); + delegate.remove(removalKey); + removalKey = null; + } + }; + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalAsyncLoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalAsyncLoadingCache.java new file mode 100644 index 0000000000000..6a4574b037941 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalAsyncLoadingCache.java @@ -0,0 +1,206 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides a skeletal implementation of the {@link AsyncLoadingCache} interface to + * minimize the effort required to implement a {@link LocalCache}. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +abstract class LocalAsyncLoadingCache + implements LocalAsyncCache, AsyncLoadingCache { + static final Logger logger = Logger.getLogger(LocalAsyncLoadingCache.class.getName()); + + final boolean canBulkLoad; + final AsyncCacheLoader loader; + + LoadingCacheView cacheView; + + @SuppressWarnings("unchecked") + LocalAsyncLoadingCache(AsyncCacheLoader loader) { + this.loader = (AsyncCacheLoader) loader; + this.canBulkLoad = canBulkLoad(loader); + } + + /** Returns whether the supplied cache loader has bulk load functionality. */ + private static boolean canBulkLoad(AsyncCacheLoader loader) { + try { + Class defaultLoaderClass = AsyncCacheLoader.class; + if (loader instanceof CacheLoader) { + defaultLoaderClass = CacheLoader.class; + + Method classLoadAll = loader.getClass().getMethod("loadAll", Iterable.class); + Method defaultLoadAll = CacheLoader.class.getMethod("loadAll", Iterable.class); + if (!classLoadAll.equals(defaultLoadAll)) { + return true; + } + } + + Method classAsyncLoadAll = + loader.getClass().getMethod("asyncLoadAll", Iterable.class, Executor.class); + Method defaultAsyncLoadAll = + defaultLoaderClass.getMethod("asyncLoadAll", Iterable.class, Executor.class); + return !classAsyncLoadAll.equals(defaultAsyncLoadAll); + } catch (NoSuchMethodException | SecurityException e) { + logger.log(Level.WARNING, "Cannot determine if CacheLoader can bulk load", e); + return false; + } + } + + @Override + public CompletableFuture get(K key) { + return get(key, loader::asyncLoad); + } + + @Override + public CompletableFuture> getAll(Iterable keys) { + if (canBulkLoad) { + return getAll(keys, loader::asyncLoadAll); + } + + Map> result = new LinkedHashMap<>(); + Function> mappingFunction = this::get; + for (K key : keys) { + CompletableFuture future = result.computeIfAbsent(key, mappingFunction); + requireNonNull(future); + } + return composeResult(result); + } + + @Override + public LoadingCache synchronous() { + return (cacheView == null) ? (cacheView = new LoadingCacheView<>(this)) : cacheView; + } + + /* --------------- Synchronous views --------------- */ + + static final class LoadingCacheView extends AbstractCacheView + implements LoadingCache { + private static final long serialVersionUID = 1L; + + final LocalAsyncLoadingCache asyncCache; + + LoadingCacheView(LocalAsyncLoadingCache asyncCache) { + this.asyncCache = requireNonNull(asyncCache); + } + + @Override + LocalAsyncLoadingCache asyncCache() { + return asyncCache; + } + + @Override + @SuppressWarnings("PMD.PreserveStackTrace") + public V get(K key) { + return resolve(asyncCache.get(key)); + } + + @Override + @SuppressWarnings("PMD.PreserveStackTrace") + public Map getAll(Iterable keys) { + return resolve(asyncCache.getAll(keys)); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + public void refresh(K key) { + requireNonNull(key); + + long[] writeTime = new long[1]; + CompletableFuture oldValueFuture = asyncCache.cache().getIfPresentQuietly(key, writeTime); + if ((oldValueFuture == null) + || (oldValueFuture.isDone() && oldValueFuture.isCompletedExceptionally())) { + asyncCache.get(key, asyncCache.loader::asyncLoad, /* recordStats */ false); + return; + } else if (!oldValueFuture.isDone()) { + // no-op if load is pending + return; + } + + oldValueFuture.thenAccept( + oldValue -> { + long now = asyncCache.cache().statsTicker().read(); + CompletableFuture refreshFuture = + (oldValue == null) + ? asyncCache.loader.asyncLoad(key, asyncCache.cache().executor()) + : asyncCache.loader.asyncReload(key, oldValue, asyncCache.cache().executor()); + refreshFuture.whenComplete( + (newValue, error) -> { + long loadTime = asyncCache.cache().statsTicker().read() - now; + if (error != null) { + asyncCache.cache().statsCounter().recordLoadFailure(loadTime); + if (!(error instanceof CancellationException) + && !(error instanceof TimeoutException)) { + logger.log(Level.WARNING, "Exception thrown during refresh", error); + } + return; + } + + boolean[] discard = new boolean[1]; + asyncCache + .cache() + .compute( + key, + (k, currentValue) -> { + if (currentValue == null) { + return (newValue == null) ? null : refreshFuture; + } else if (currentValue == oldValueFuture) { + long expectedWriteTime = writeTime[0]; + if (asyncCache.cache().hasWriteTime()) { + asyncCache.cache().getIfPresentQuietly(key, writeTime); + } + if (writeTime[0] == expectedWriteTime) { + return (newValue == null) ? null : refreshFuture; + } + } + discard[0] = true; + return currentValue; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + true); + + if (discard[0] && asyncCache.cache().hasRemovalListener()) { + asyncCache.cache().notifyRemoval(key, refreshFuture, RemovalCause.REPLACED); + } + if (newValue == null) { + asyncCache.cache().statsCounter().recordLoadFailure(loadTime); + } else { + asyncCache.cache().statsCounter().recordLoadSuccess(loadTime); + } + }); + }); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalCache.java new file mode 100644 index 0000000000000..245ee2a53a9ed --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalCache.java @@ -0,0 +1,207 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * An in-memory cache providing thread safety and atomicity guarantees. This interface provides an + * extension to {@link ConcurrentMap} for use with skeletal implementations. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +interface LocalCache extends ConcurrentMap { + + /** Returns whether this cache has statistics enabled. */ + boolean isRecordingStats(); + + /** Returns the {@link StatsCounter} used by this cache. */ + StatsCounter statsCounter(); + + /** Returns whether this cache notifies when an entry is removed. */ + boolean hasRemovalListener(); + + /** Returns the {@link RemovalListener} used by this cache. */ + RemovalListener removalListener(); + + /** Asynchronously sends a removal notification to the listener. */ + void notifyRemoval(K key, V value, RemovalCause cause); + + /** Returns the {@link Executor} used by this cache. */ + Executor executor(); + + /** Returns whether the cache captures the write time of the entry. */ + boolean hasWriteTime(); + + /** Returns the {@link Ticker} used by this cache for expiration. */ + Ticker expirationTicker(); + + /** Returns the {@link Ticker} used by this cache for statistics. */ + Ticker statsTicker(); + + /** See {@link Cache#estimatedSize()}. */ + long estimatedSize(); + + /** + * See {@link Cache#getIfPresent(Object)}. This method differs by accepting a parameter of whether + * to record the hit and miss statistics based on the success of this operation. + */ + V getIfPresent(Object key, boolean recordStats); + + /** + * See {@link Cache#getIfPresent(Object)}. This method differs by not recording the access with + * the statistics nor the eviction policy, and populates the write time if known. + */ + V getIfPresentQuietly(Object key, long[ /* 1 */] writeTime); + + /** See {@link Cache#getAllPresent}. */ + Map getAllPresent(Iterable keys); + + /** + * See {@link Cache#put(Object, Object)}. This method differs by allowing the operation to not + * notify the writer when an entry was inserted or updated. + */ + V put(K key, V value, boolean notifyWriter); + + @Override + default V compute(K key, BiFunction remappingFunction) { + return compute( + key, + remappingFunction, /* recordMiss */ + false, + /* recordLoad */ true, /* recordLoadFailure */ + true); + } + + /** + * See {@link ConcurrentMap#compute}. This method differs by accepting parameters indicating + * whether to record miss and load statistics based on the success of this operation. + */ + V compute( + K key, + BiFunction remappingFunction, + boolean recordMiss, + boolean recordLoad, + boolean recordLoadFailure); + + @Override + default V computeIfAbsent(K key, Function mappingFunction) { + return computeIfAbsent(key, mappingFunction, /* recordStats */ true, /* recordLoad */ true); + } + + /** + * See {@link ConcurrentMap#computeIfAbsent}. This method differs by accepting parameters + * indicating how to record statistics. + */ + V computeIfAbsent( + K key, + Function mappingFunction, + boolean recordStats, + boolean recordLoad); + + /** See {@link Cache#invalidateAll(Iterable)}. */ + default void invalidateAll(Iterable keys) { + for (Object key : keys) { + remove(key); + } + } + + /** See {@link Cache#cleanUp}. */ + void cleanUp(); + + /** Decorates the remapping function to record statistics if enabled. */ + default Function statsAware( + Function mappingFunction, boolean recordLoad) { + if (!isRecordingStats()) { + return mappingFunction; + } + return key -> { + R value; + statsCounter().recordMisses(1); + long startTime = statsTicker().read(); + try { + value = mappingFunction.apply(key); + } catch (RuntimeException | Error e) { + statsCounter().recordLoadFailure(statsTicker().read() - startTime); + throw e; + } + long loadTime = statsTicker().read() - startTime; + if (recordLoad) { + if (value == null) { + statsCounter().recordLoadFailure(loadTime); + } else { + statsCounter().recordLoadSuccess(loadTime); + } + } + return value; + }; + } + + /** Decorates the remapping function to record statistics if enabled. */ + default BiFunction statsAware( + BiFunction remappingFunction) { + return statsAware( + remappingFunction, /* recordMiss */ + true, + /* recordLoad */ true, /* recordLoadFailure */ + true); + } + + /** Decorates the remapping function to record statistics if enabled. */ + default BiFunction statsAware( + BiFunction remappingFunction, + boolean recordMiss, + boolean recordLoad, + boolean recordLoadFailure) { + if (!isRecordingStats()) { + return remappingFunction; + } + return (t, u) -> { + R result; + if ((u == null) && recordMiss) { + statsCounter().recordMisses(1); + } + long startTime = statsTicker().read(); + try { + result = remappingFunction.apply(t, u); + } catch (RuntimeException | Error e) { + if (recordLoadFailure) { + statsCounter().recordLoadFailure(statsTicker().read() - startTime); + } + throw e; + } + long loadTime = statsTicker().read() - startTime; + if (recordLoad) { + if (result == null) { + statsCounter().recordLoadFailure(loadTime); + } else { + statsCounter().recordLoadSuccess(loadTime); + } + } + return result; + }; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalCacheFactory.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalCacheFactory.java new file mode 100644 index 0000000000000..80b8b8b7a4731 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalCacheFactory.java @@ -0,0 +1,103 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.reflect.Constructor; + +/** + * WARNING: GENERATED CODE + * + *

A factory for caches optimized for a particular configuration. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +final class LocalCacheFactory { + public static final String MAXIMUM = "maximum"; + + public static final String WINDOW_MAXIMUM = "windowMaximum"; + + public static final String MAIN_PROTECTED_MAXIMUM = "mainProtectedMaximum"; + + public static final String WEIGHTED_SIZE = "weightedSize"; + + public static final String WINDOW_WEIGHTED_SIZE = "windowWeightedSize"; + + public static final String MAIN_PROTECTED_WEIGHTED_SIZE = "mainProtectedWeightedSize"; + + public static final String KEY = "key"; + + public static final String VALUE = "value"; + + public static final String ACCESS_TIME = "accessTime"; + + public static final String WRITE_TIME = "writeTime"; + + private LocalCacheFactory() {} + + /** Returns a cache optimized for this configuration. */ + static BoundedLocalCache newBoundedLocalCache( + Caffeine builder, CacheLoader cacheLoader, boolean async) { + StringBuilder sb = new StringBuilder("org.apache.iotdb.github.benmanes.caffeine.cache."); + if (builder.isStrongKeys()) { + sb.append('S'); + } else { + sb.append('W'); + } + if (builder.isStrongValues()) { + sb.append('S'); + } else { + sb.append('I'); + } + if (builder.removalListener != null) { + sb.append('L'); + } + if (builder.isRecordingStats()) { + sb.append('S'); + } + if (builder.evicts()) { + sb.append('M'); + if (builder.isWeighted()) { + sb.append('W'); + } else { + sb.append('S'); + } + } + if (builder.expiresAfterAccess() || builder.expiresVariable()) { + sb.append('A'); + } + if (builder.expiresAfterWrite()) { + sb.append('W'); + } + if (builder.refreshAfterWrite()) { + sb.append('R'); + } + try { + Class clazz = Class.forName(sb.toString()); + Constructor ctor = + clazz.getDeclaredConstructor(Caffeine.class, CacheLoader.class, boolean.class); + @SuppressWarnings("unchecked") + BoundedLocalCache factory = + (BoundedLocalCache) ctor.newInstance(builder, cacheLoader, async); + return factory; + } catch (ReflectiveOperationException e) { + throw new IllegalStateException(sb.toString(), e); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalLoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalLoadingCache.java new file mode 100644 index 0000000000000..462a2704a8c2e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalLoadingCache.java @@ -0,0 +1,198 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides a skeletal implementation of the {@link LoadingCache} interface to minimize + * the effort required to implement a {@link LocalCache}. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +interface LocalLoadingCache extends LocalManualCache, LoadingCache { + Logger logger = Logger.getLogger(LocalLoadingCache.class.getName()); + + /** Returns the {@link CacheLoader} used by this cache. */ + CacheLoader cacheLoader(); + + /** Returns the {@link CacheLoader#load} as a mapping function. */ + Function mappingFunction(); + + /** Returns the {@link CacheLoader#loadAll} as a mapping function, if implemented. */ + Function, Map> bulkMappingFunction(); + + @Override + default V get(K key) { + return cache().computeIfAbsent(key, mappingFunction()); + } + + @Override + default Map getAll(Iterable keys) { + Function, Map> mappingFunction = bulkMappingFunction(); + return (mappingFunction == null) ? loadSequentially(keys) : getAll(keys, mappingFunction); + } + + /** Sequentially loads each missing entry. */ + default Map loadSequentially(Iterable keys) { + Set uniqueKeys = new LinkedHashSet<>(); + for (K key : keys) { + uniqueKeys.add(key); + } + + int count = 0; + Map result = new LinkedHashMap<>(uniqueKeys.size()); + try { + for (K key : uniqueKeys) { + count++; + + V value = get(key); + if (value != null) { + result.put(key, value); + } + } + } catch (Throwable t) { + cache().statsCounter().recordMisses(uniqueKeys.size() - count); + throw t; + } + return Collections.unmodifiableMap(result); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + default void refresh(K key) { + requireNonNull(key); + + long[] writeTime = new long[1]; + long startTime = cache().statsTicker().read(); + V oldValue = cache().getIfPresentQuietly(key, writeTime); + CompletableFuture refreshFuture = + (oldValue == null) + ? cacheLoader().asyncLoad(key, cache().executor()) + : cacheLoader().asyncReload(key, oldValue, cache().executor()); + refreshFuture.whenComplete( + (newValue, error) -> { + long loadTime = cache().statsTicker().read() - startTime; + if (error != null) { + if (!(error instanceof CancellationException) && !(error instanceof TimeoutException)) { + logger.log(Level.WARNING, "Exception thrown during refresh", error); + } + cache().statsCounter().recordLoadFailure(loadTime); + return; + } + + boolean[] discard = new boolean[1]; + cache() + .compute( + key, + (k, currentValue) -> { + if (currentValue == null) { + return newValue; + } else if (currentValue == oldValue) { + long expectedWriteTime = writeTime[0]; + if (cache().hasWriteTime()) { + cache().getIfPresentQuietly(key, writeTime); + } + if (writeTime[0] == expectedWriteTime) { + return newValue; + } + } + discard[0] = true; + return currentValue; + }, /* recordMiss */ + false, /* recordLoad */ + false, /* recordLoadFailure */ + true); + + if (discard[0] && cache().hasRemovalListener()) { + cache().notifyRemoval(key, newValue, RemovalCause.REPLACED); + } + if (newValue == null) { + cache().statsCounter().recordLoadFailure(loadTime); + } else { + cache().statsCounter().recordLoadSuccess(loadTime); + } + }); + } + + /** Returns a mapping function that adapts to {@link CacheLoader#load}. */ + static Function newMappingFunction(CacheLoader cacheLoader) { + return key -> { + try { + return cacheLoader.load(key); + } catch (RuntimeException e) { + throw e; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new CompletionException(e); + } catch (Exception e) { + throw new CompletionException(e); + } + }; + } + + /** Returns a mapping function that adapts to {@link CacheLoader#loadAll}, if implemented. */ + static Function, Map> newBulkMappingFunction( + CacheLoader cacheLoader) { + if (!hasLoadAll(cacheLoader)) { + return null; + } + return keysToLoad -> { + try { + @SuppressWarnings("unchecked") + Map loaded = (Map) cacheLoader.loadAll(keysToLoad); + return loaded; + } catch (RuntimeException e) { + throw e; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new CompletionException(e); + } catch (Exception e) { + throw new CompletionException(e); + } + }; + } + + /** Returns whether the supplied cache loader has bulk load functionality. */ + static boolean hasLoadAll(CacheLoader loader) { + try { + Method classLoadAll = loader.getClass().getMethod("loadAll", Iterable.class); + Method defaultLoadAll = CacheLoader.class.getMethod("loadAll", Iterable.class); + return !classLoadAll.equals(defaultLoadAll); + } catch (NoSuchMethodException | SecurityException e) { + logger.log(Level.WARNING, "Cannot determine if CacheLoader can bulk load", e); + return false; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalManualCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalManualCache.java new file mode 100644 index 0000000000000..c8a9b76553d50 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/LocalManualCache.java @@ -0,0 +1,164 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.CacheStats; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides a skeletal implementation of the {@link Cache} interface to minimize the + * effort required to implement a {@link LocalCache}. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +interface LocalManualCache extends Cache { + + /** Returns the backing {@link LocalCache} data store. */ + LocalCache cache(); + + @Override + default long estimatedSize() { + return cache().estimatedSize(); + } + + @Override + default void cleanUp() { + cache().cleanUp(); + } + + @Override + default V getIfPresent(Object key) { + return cache().getIfPresent(key, /* recordStats */ true); + } + + @Override + default V get(K key, Function mappingFunction) { + return cache().computeIfAbsent(key, mappingFunction); + } + + @Override + default Map getAllPresent(Iterable keys) { + return cache().getAllPresent(keys); + } + + @Override + default Map getAll( + Iterable keys, Function, Map> mappingFunction) { + requireNonNull(mappingFunction); + + Set keysToLoad = new LinkedHashSet<>(); + Map found = cache().getAllPresent(keys); + Map result = new LinkedHashMap<>(found.size()); + for (K key : keys) { + V value = found.get(key); + if (value == null) { + keysToLoad.add(key); + } + result.put(key, value); + } + if (keysToLoad.isEmpty()) { + return found; + } + + bulkLoad(keysToLoad, result, mappingFunction); + return Collections.unmodifiableMap(result); + } + + /** + * Performs a non-blocking bulk load of the missing keys. Any missing entry that materializes + * during the load are replaced when the loaded entries are inserted into the cache. + */ + default void bulkLoad( + Set keysToLoad, + Map result, + Function, Map> mappingFunction) { + boolean success = false; + long startTime = cache().statsTicker().read(); + try { + Map loaded = mappingFunction.apply(keysToLoad); + loaded.forEach((key, value) -> cache().put(key, value, /* notifyWriter */ false)); + for (K key : keysToLoad) { + V value = loaded.get(key); + if (value == null) { + result.remove(key); + } else { + result.put(key, value); + } + } + success = !loaded.isEmpty(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CompletionException(e); + } finally { + long loadTime = cache().statsTicker().read() - startTime; + if (success) { + cache().statsCounter().recordLoadSuccess(loadTime); + } else { + cache().statsCounter().recordLoadFailure(loadTime); + } + } + } + + @Override + default void put(K key, V value) { + cache().put(key, value); + } + + @Override + default void putAll(Map map) { + cache().putAll(map); + } + + @Override + default void invalidate(Object key) { + cache().remove(key); + } + + @Override + default void invalidateAll(Iterable keys) { + cache().invalidateAll(keys); + } + + @Override + default void invalidateAll() { + cache().clear(); + } + + @Override + default CacheStats stats() { + return cache().statsCounter().snapshot(); + } + + @Override + default ConcurrentMap asMap() { + return cache(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/MpscGrowableArrayQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/MpscGrowableArrayQueue.java new file mode 100644 index 0000000000000..22613d2ae8dba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/MpscGrowableArrayQueue.java @@ -0,0 +1,718 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.reflect.Field; +import java.util.AbstractQueue; +import java.util.Iterator; + +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.ceilingPowerOfTwo; +import static org.apache.iotdb.github.benmanes.caffeine.cache.UnsafeAccess.UNSAFE; +import static org.apache.iotdb.github.benmanes.caffeine.cache.UnsafeRefArrayAccess.REF_ARRAY_BASE; +import static org.apache.iotdb.github.benmanes.caffeine.cache.UnsafeRefArrayAccess.REF_ELEMENT_SHIFT; +import static org.apache.iotdb.github.benmanes.caffeine.cache.UnsafeRefArrayAccess.lvElement; +import static org.apache.iotdb.github.benmanes.caffeine.cache.UnsafeRefArrayAccess.soElement; + +/** + * An MPSC array queue which starts at initialCapacity and grows to maxCapacity in + * linked chunks of the initial size. The queue grows only when the current buffer is full and + * elements are not copied on resize, instead a link to the new buffer is stored in the old buffer + * for the consumer to follow.
+ * + *

This is a shaded copy of MpscGrowableArrayQueue provided by JCTools from version 2.0. + * + * @author nitsanw@yahoo.com (Nitsan Wakart) + */ +final class MpscGrowableArrayQueue extends MpscChunkedArrayQueue { + + /** + * @param initialCapacity the queue initial capacity. If chunk size is fixed this will be the + * chunk size. Must be 2 or more. + * @param maxCapacity the maximum capacity will be rounded up to the closest power of 2 and will + * be the upper limit of number of elements in this queue. Must be 4 or more and round up to a + * larger power of 2 than initialCapacity. + */ + MpscGrowableArrayQueue(int initialCapacity, int maxCapacity) { + super(initialCapacity, maxCapacity); + } + + @Override + protected int getNextBufferSize(E[] buffer) { + long maxSize = maxQueueCapacity / 2; + if (buffer.length > maxSize) { + throw new IllegalStateException(); + } + final int newSize = 2 * (buffer.length - 1); + return newSize + 1; + } + + @Override + protected long getCurrentBufferCapacity(long mask) { + return (mask + 2 == maxQueueCapacity) ? maxQueueCapacity : mask; + } +} + +@SuppressWarnings("OvershadowingSubclassFields") +abstract class MpscChunkedArrayQueue extends MpscChunkedArrayQueueColdProducerFields { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; + + MpscChunkedArrayQueue(int initialCapacity, int maxCapacity) { + super(initialCapacity, maxCapacity); + } + + @Override + protected long availableInQueue(long pIndex, long cIndex) { + return maxQueueCapacity - (pIndex - cIndex); + } + + @Override + public int capacity() { + return (int) (maxQueueCapacity / 2); + } + + @Override + protected int getNextBufferSize(E[] buffer) { + return buffer.length; + } + + @Override + protected long getCurrentBufferCapacity(long mask) { + return mask; + } +} + +abstract class MpscChunkedArrayQueueColdProducerFields extends BaseMpscLinkedArrayQueue { + protected final long maxQueueCapacity; + + MpscChunkedArrayQueueColdProducerFields(int initialCapacity, int maxCapacity) { + super(initialCapacity); + if (maxCapacity < 4) { + throw new IllegalArgumentException("Max capacity must be 4 or more"); + } + if (ceilingPowerOfTwo(initialCapacity) >= ceilingPowerOfTwo(maxCapacity)) { + throw new IllegalArgumentException( + "Initial capacity cannot exceed maximum capacity(both rounded up to a power of 2)"); + } + maxQueueCapacity = ((long) ceilingPowerOfTwo(maxCapacity)) << 1; + } +} + +abstract class BaseMpscLinkedArrayQueuePad1 extends AbstractQueue { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; +} + +abstract class BaseMpscLinkedArrayQueueProducerFields extends BaseMpscLinkedArrayQueuePad1 { + protected long producerIndex; +} + +@SuppressWarnings("OvershadowingSubclassFields") +abstract class BaseMpscLinkedArrayQueuePad2 extends BaseMpscLinkedArrayQueueProducerFields { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; +} + +@SuppressWarnings("NullAway") +abstract class BaseMpscLinkedArrayQueueConsumerFields extends BaseMpscLinkedArrayQueuePad2 { + protected long consumerMask; + protected E[] consumerBuffer; + protected long consumerIndex; +} + +@SuppressWarnings("OvershadowingSubclassFields") +abstract class BaseMpscLinkedArrayQueuePad3 extends BaseMpscLinkedArrayQueueConsumerFields { + byte p000, p001, p002, p003, p004, p005, p006, p007; + byte p008, p009, p010, p011, p012, p013, p014, p015; + byte p016, p017, p018, p019, p020, p021, p022, p023; + byte p024, p025, p026, p027, p028, p029, p030, p031; + byte p032, p033, p034, p035, p036, p037, p038, p039; + byte p040, p041, p042, p043, p044, p045, p046, p047; + byte p048, p049, p050, p051, p052, p053, p054, p055; + byte p056, p057, p058, p059, p060, p061, p062, p063; + byte p064, p065, p066, p067, p068, p069, p070, p071; + byte p072, p073, p074, p075, p076, p077, p078, p079; + byte p080, p081, p082, p083, p084, p085, p086, p087; + byte p088, p089, p090, p091, p092, p093, p094, p095; + byte p096, p097, p098, p099, p100, p101, p102, p103; + byte p104, p105, p106, p107, p108, p109, p110, p111; + byte p112, p113, p114, p115, p116, p117, p118, p119; +} + +@SuppressWarnings("NullAway") +abstract class BaseMpscLinkedArrayQueueColdProducerFields + extends BaseMpscLinkedArrayQueuePad3 { + protected volatile long producerLimit; + protected long producerMask; + protected E[] producerBuffer; +} + +@SuppressWarnings({"PMD", "NullAway", "restriction"}) +abstract class BaseMpscLinkedArrayQueue extends BaseMpscLinkedArrayQueueColdProducerFields { + // No post padding here, subclasses must add + + private static final long P_INDEX_OFFSET; + private static final long C_INDEX_OFFSET; + private static final long P_LIMIT_OFFSET; + + static { + try { + Field iField = BaseMpscLinkedArrayQueueProducerFields.class.getDeclaredField("producerIndex"); + P_INDEX_OFFSET = UNSAFE.objectFieldOffset(iField); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + try { + Field iField = BaseMpscLinkedArrayQueueConsumerFields.class.getDeclaredField("consumerIndex"); + C_INDEX_OFFSET = UNSAFE.objectFieldOffset(iField); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + try { + Field iField = + BaseMpscLinkedArrayQueueColdProducerFields.class.getDeclaredField("producerLimit"); + P_LIMIT_OFFSET = UNSAFE.objectFieldOffset(iField); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private static final Object JUMP = new Object(); + + /** + * @param initialCapacity the queue initial capacity. If chunk size is fixed this will be the + * chunk size. Must be 2 or more. + */ + BaseMpscLinkedArrayQueue(final int initialCapacity) { + if (initialCapacity < 2) { + throw new IllegalArgumentException("Initial capacity must be 2 or more"); + } + + int p2capacity = ceilingPowerOfTwo(initialCapacity); + // leave lower bit of mask clear + long mask = (p2capacity - 1L) << 1; + // need extra element to point at next array + E[] buffer = allocate(p2capacity + 1); + producerBuffer = buffer; + producerMask = mask; + consumerBuffer = buffer; + consumerMask = mask; + soProducerLimit(mask); // we know it's all empty to start with + } + + @Override + public final Iterator iterator() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return getClass().getName() + "@" + Integer.toHexString(hashCode()); + } + + @Override + @SuppressWarnings("MissingDefault") + public boolean offer(final E e) { + if (null == e) { + throw new NullPointerException(); + } + + long mask; + E[] buffer; + long pIndex; + + while (true) { + long producerLimit = lvProducerLimit(); + pIndex = lvProducerIndex(); + // lower bit is indicative of resize, if we see it we spin until it's cleared + if ((pIndex & 1) == 1) { + continue; + } + // pIndex is even (lower bit is 0) -> actual index is (pIndex >> 1) + + // mask/buffer may get changed by resizing -> only use for array access after successful CAS. + mask = this.producerMask; + buffer = this.producerBuffer; + // a successful CAS ties the ordering, lv(pIndex)-[mask/buffer]->cas(pIndex) + + // assumption behind this optimization is that queue is almost always empty or near empty + if (producerLimit <= pIndex) { + int result = offerSlowPath(mask, pIndex, producerLimit); + switch (result) { + case 0: + break; + case 1: + continue; + case 2: + return false; + case 3: + resize(mask, buffer, pIndex, e); + return true; + } + } + + if (casProducerIndex(pIndex, pIndex + 2)) { + break; + } + } + // INDEX visible before ELEMENT, consistent with consumer expectation + final long offset = modifiedCalcElementOffset(pIndex, mask); + soElement(buffer, offset, e); + return true; + } + + /** We do not inline resize into this method because we do not resize on fill. */ + private int offerSlowPath(long mask, long pIndex, long producerLimit) { + int result; + final long cIndex = lvConsumerIndex(); + long bufferCapacity = getCurrentBufferCapacity(mask); + result = 0; // 0 - goto pIndex CAS + if (cIndex + bufferCapacity > pIndex) { + if (!casProducerLimit(producerLimit, cIndex + bufferCapacity)) { + result = 1; // retry from top + } + } + // full and cannot grow + else if (availableInQueue(pIndex, cIndex) <= 0) { + result = 2; // -> return false; + } + // grab index for resize -> set lower bit + else if (casProducerIndex(pIndex, pIndex + 1)) { + result = 3; // -> resize + } else { + result = 1; // failed resize attempt, retry from top + } + return result; + } + + /** + * @return available elements in queue * 2 + */ + protected abstract long availableInQueue(long pIndex, final long cIndex); + + /** + * This method assumes index is actually (index << 1) because lower bit is used for resize. This + * is compensated for by reducing the element shift. The computation is constant folded, so + * there's no cost. + */ + private static long modifiedCalcElementOffset(long index, long mask) { + return REF_ARRAY_BASE + ((index & mask) << (REF_ELEMENT_SHIFT - 1)); + } + + /** + * {@inheritDoc} + * + *

This implementation is correct for single consumer thread use only. + */ + @Override + @SuppressWarnings("unchecked") + public E poll() { + final E[] buffer = consumerBuffer; + final long index = consumerIndex; + final long mask = consumerMask; + + final long offset = modifiedCalcElementOffset(index, mask); + Object e = lvElement(buffer, offset); // LoadLoad + if (e == null) { + if (index != lvProducerIndex()) { + // poll() == null iff queue is empty, null element is not strong enough indicator, so we + // must + // check the producer index. If the queue is indeed not empty we spin until element is + // visible. + do { + e = lvElement(buffer, offset); + } while (e == null); + } else { + return null; + } + } + if (e == JUMP) { + final E[] nextBuffer = getNextBuffer(buffer, mask); + return newBufferPoll(nextBuffer, index); + } + soElement(buffer, offset, null); + soConsumerIndex(index + 2); + return (E) e; + } + + /** + * {@inheritDoc} + * + *

This implementation is correct for single consumer thread use only. + */ + @SuppressWarnings("unchecked") + @Override + public E peek() { + final E[] buffer = consumerBuffer; + final long index = consumerIndex; + final long mask = consumerMask; + + final long offset = modifiedCalcElementOffset(index, mask); + Object e = lvElement(buffer, offset); // LoadLoad + if (e == null && index != lvProducerIndex()) { + // peek() == null iff queue is empty, null element is not strong enough indicator, so we must + // check the producer index. If the queue is indeed not empty we spin until element is + // visible. + while ((e = lvElement(buffer, offset)) == null) { + ; + } + } + if (e == JUMP) { + return newBufferPeek(getNextBuffer(buffer, mask), index); + } + return (E) e; + } + + @SuppressWarnings("unchecked") + private E[] getNextBuffer(final E[] buffer, final long mask) { + final long nextArrayOffset = nextArrayOffset(mask); + final E[] nextBuffer = (E[]) lvElement(buffer, nextArrayOffset); + soElement(buffer, nextArrayOffset, null); + return nextBuffer; + } + + private static long nextArrayOffset(final long mask) { + return modifiedCalcElementOffset(mask + 2, Long.MAX_VALUE); + } + + private E newBufferPoll(E[] nextBuffer, final long index) { + final long offsetInNew = newBufferAndOffset(nextBuffer, index); + final E n = lvElement(nextBuffer, offsetInNew); // LoadLoad + if (n == null) { + throw new IllegalStateException("new buffer must have at least one element"); + } + soElement(nextBuffer, offsetInNew, null); // StoreStore + soConsumerIndex(index + 2); + return n; + } + + private E newBufferPeek(E[] nextBuffer, final long index) { + final long offsetInNew = newBufferAndOffset(nextBuffer, index); + final E n = lvElement(nextBuffer, offsetInNew); // LoadLoad + if (null == n) { + throw new IllegalStateException("new buffer must have at least one element"); + } + return n; + } + + private long newBufferAndOffset(E[] nextBuffer, final long index) { + consumerBuffer = nextBuffer; + consumerMask = (nextBuffer.length - 2L) << 1; + final long offsetInNew = modifiedCalcElementOffset(index, consumerMask); + return offsetInNew; + } + + @Override + public final int size() { + // NOTE: because indices are on even numbers we cannot use the size util. + + /* + * It is possible for a thread to be interrupted or reschedule between the read of the producer + * and consumer indices, therefore protection is required to ensure size is within valid range. + * In the event of concurrent polls/offers to this method the size is OVER estimated as we read + * consumer index BEFORE the producer index. + */ + long after = lvConsumerIndex(); + long size; + while (true) { + final long before = after; + final long currentProducerIndex = lvProducerIndex(); + after = lvConsumerIndex(); + if (before == after) { + size = ((currentProducerIndex - after) >> 1); + break; + } + } + // Long overflow is impossible, so size is always positive. Integer overflow is possible for the + // unbounded + // indexed queues. + if (size > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else { + return (int) size; + } + } + + @Override + public final boolean isEmpty() { + // Order matters! + // Loading consumer before producer allows for producer increments after consumer index is read. + // This ensures this method is conservative in it's estimate. Note that as this is an MPMC there + // is + // nothing we can do to make this an exact method. + return (this.lvConsumerIndex() == this.lvProducerIndex()); + } + + private long lvProducerIndex() { + return UNSAFE.getLongVolatile(this, P_INDEX_OFFSET); + } + + private long lvConsumerIndex() { + return UNSAFE.getLongVolatile(this, C_INDEX_OFFSET); + } + + private void soProducerIndex(long v) { + UNSAFE.putOrderedLong(this, P_INDEX_OFFSET, v); + } + + private boolean casProducerIndex(long expect, long newValue) { + return UNSAFE.compareAndSwapLong(this, P_INDEX_OFFSET, expect, newValue); + } + + private void soConsumerIndex(long v) { + UNSAFE.putOrderedLong(this, C_INDEX_OFFSET, v); + } + + private long lvProducerLimit() { + return producerLimit; + } + + private boolean casProducerLimit(long expect, long newValue) { + return UNSAFE.compareAndSwapLong(this, P_LIMIT_OFFSET, expect, newValue); + } + + private void soProducerLimit(long v) { + UNSAFE.putOrderedLong(this, P_LIMIT_OFFSET, v); + } + + public long currentProducerIndex() { + return lvProducerIndex() / 2; + } + + public long currentConsumerIndex() { + return lvConsumerIndex() / 2; + } + + public abstract int capacity(); + + public boolean relaxedOffer(E e) { + return offer(e); + } + + @SuppressWarnings("unchecked") + public E relaxedPoll() { + final E[] buffer = consumerBuffer; + final long index = consumerIndex; + final long mask = consumerMask; + + final long offset = modifiedCalcElementOffset(index, mask); + Object e = lvElement(buffer, offset); // LoadLoad + if (e == null) { + return null; + } + if (e == JUMP) { + final E[] nextBuffer = getNextBuffer(buffer, mask); + return newBufferPoll(nextBuffer, index); + } + soElement(buffer, offset, null); + soConsumerIndex(index + 2); + return (E) e; + } + + @SuppressWarnings("unchecked") + public E relaxedPeek() { + final E[] buffer = consumerBuffer; + final long index = consumerIndex; + final long mask = consumerMask; + + final long offset = modifiedCalcElementOffset(index, mask); + Object e = lvElement(buffer, offset); // LoadLoad + if (e == JUMP) { + return newBufferPeek(getNextBuffer(buffer, mask), index); + } + return (E) e; + } + + private void resize(long oldMask, E[] oldBuffer, long pIndex, final E e) { + int newBufferLength = getNextBufferSize(oldBuffer); + final E[] newBuffer = allocate(newBufferLength); + + producerBuffer = newBuffer; + final int newMask = (newBufferLength - 2) << 1; + producerMask = newMask; + + final long offsetInOld = modifiedCalcElementOffset(pIndex, oldMask); + final long offsetInNew = modifiedCalcElementOffset(pIndex, newMask); + + soElement(newBuffer, offsetInNew, e); // element in new array + soElement(oldBuffer, nextArrayOffset(oldMask), newBuffer); // buffer linked + + // ASSERT code + final long cIndex = lvConsumerIndex(); + final long availableInQueue = availableInQueue(pIndex, cIndex); + if (availableInQueue <= 0) { + throw new IllegalStateException(); + } + + // Invalidate racing CASs + // We never set the limit beyond the bounds of a buffer + soProducerLimit(pIndex + Math.min(newMask, availableInQueue)); + + // make resize visible to the other producers + soProducerIndex(pIndex + 2); + + // INDEX visible before ELEMENT, consistent with consumer expectation + + // make resize visible to consumer + soElement(oldBuffer, offsetInOld, JUMP); + } + + @SuppressWarnings("unchecked") + public static E[] allocate(int capacity) { + return (E[]) new Object[capacity]; + } + + /** + * @return next buffer size(inclusive of next array pointer) + */ + protected abstract int getNextBufferSize(E[] buffer); + + /** + * @return current buffer capacity for elements (excluding next pointer and jump entry) * 2 + */ + protected abstract long getCurrentBufferCapacity(long mask); +} + +/** + * A concurrent access enabling class used by circular array based queues this class exposes an + * offset computation method along with differently memory fenced load/store methods into the + * underlying array. The class is pre-padded and the array is padded on either side to help with + * False sharing prvention. It is expected theat subclasses handle post padding. + * + *

Offset calculation is separate from access to enable the reuse of a give compute offset. + * + *

Load/Store methods using a buffer parameter are provided to allow the prevention of + * final field reload after a LoadLoad barrier. + * + *

+ */ +@SuppressWarnings("restriction") +final class UnsafeRefArrayAccess { + public static final long REF_ARRAY_BASE; + public static final int REF_ELEMENT_SHIFT; + + static { + final int scale = UNSAFE.arrayIndexScale(Object[].class); + if (4 == scale) { + REF_ELEMENT_SHIFT = 2; + } else if (8 == scale) { + REF_ELEMENT_SHIFT = 3; + } else { + throw new IllegalStateException("Unknown pointer size"); + } + REF_ARRAY_BASE = UNSAFE.arrayBaseOffset(Object[].class); + } + + private UnsafeRefArrayAccess() {} + + /** + * A plain store (no ordering/fences) of an element to a given offset + * + * @param buffer this.buffer + * @param offset computed via {@link UnsafeRefArrayAccess#calcElementOffset(long)} + * @param e an orderly kitty + */ + public static void spElement(E[] buffer, long offset, E e) { + UNSAFE.putObject(buffer, offset, e); + } + + /** + * An ordered store(store + StoreStore barrier) of an element to a given offset + * + * @param buffer this.buffer + * @param offset computed via {@link UnsafeRefArrayAccess#calcElementOffset} + * @param e an orderly kitty + */ + public static void soElement(E[] buffer, long offset, E e) { + UNSAFE.putOrderedObject(buffer, offset, e); + } + + /** + * A plain load (no ordering/fences) of an element from a given offset. + * + * @param buffer this.buffer + * @param offset computed via {@link UnsafeRefArrayAccess#calcElementOffset(long)} + * @return the element at the offset + */ + @SuppressWarnings("unchecked") + public static E lpElement(E[] buffer, long offset) { + return (E) UNSAFE.getObject(buffer, offset); + } + + /** + * A volatile load (load + LoadLoad barrier) of an element from a given offset. + * + * @param buffer this.buffer + * @param offset computed via {@link UnsafeRefArrayAccess#calcElementOffset(long)} + * @return the element at the offset + */ + @SuppressWarnings("unchecked") + public static E lvElement(E[] buffer, long offset) { + return (E) UNSAFE.getObjectVolatile(buffer, offset); + } + + /** + * @param index desirable element index + * @return the offset in bytes within the array for a given index. + */ + public static long calcElementOffset(long index) { + return REF_ARRAY_BASE + (index << REF_ELEMENT_SHIFT); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Node.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Node.java new file mode 100644 index 0000000000000..1d4c3a386578a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Node.java @@ -0,0 +1,276 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.AccessOrderDeque.AccessOrder; +import org.apache.iotdb.github.benmanes.caffeine.cache.WriteOrderDeque.WriteOrder; + +import java.lang.ref.ReferenceQueue; + +/** + * An entry in the cache containing the key, value, weight, access, and write metadata. The key or + * value may be held weakly or softly requiring identity comparison. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"GuardedByChecker", "GuardedByValidator"}) +abstract class Node implements AccessOrder>, WriteOrder> { + + /** Return the key or {@code null} if it has been reclaimed by the garbage collector. */ + public abstract K getKey(); + + /** + * Returns the reference that the cache is holding the entry by. This is either the key if + * strongly held or a {@link java.lang.ref.WeakReference} to that key. + */ + public abstract Object getKeyReference(); + + /** Return the value or {@code null} if it has been reclaimed by the garbage collector. */ + public abstract V getValue(); + + /** + * Returns the reference to the value. This is either the value if strongly held or a {@link + * java.lang.ref.Reference} to that value. + */ + public abstract Object getValueReference(); + + /** + * Sets the value, which may be held strongly, weakly, or softly. This update may be set lazily + * and rely on the memory fence when the lock is released. + */ + public abstract void setValue(V value, ReferenceQueue referenceQueue); + + /** + * Returns {@code true} if the given objects are considered equivalent. A strongly held value is + * compared by equality and a weakly or softly held value is compared by identity. + */ + public abstract boolean containsValue(Object value); + + /** Returns the weight of this entry from the entry's perspective. */ + public int getWeight() { + return 1; + } + + /** Sets the weight from the entry's perspective. */ + public void setWeight(int weight) {} + + /** Returns the weight of this entry from the policy's perspective. */ + public int getPolicyWeight() { + return 1; + } + + /** Sets the weight from the policy's perspective. */ + public void setPolicyWeight(int weight) {} + + /* --------------- Health --------------- */ + + /** If the entry is available in the hash-table and page replacement policy. */ + public abstract boolean isAlive(); + + /** + * If the entry was removed from the hash-table and is awaiting removal from the page replacement + * policy. + */ + public abstract boolean isRetired(); + + /** If the entry was removed from the hash-table and the page replacement policy. */ + public abstract boolean isDead(); + + /** Sets the node to the retired state. */ + public abstract void retire(); + + /** Sets the node to the dead state. */ + public abstract void die(); + + /* --------------- Variable order --------------- */ + + /** Returns the variable expiration time, in nanoseconds. */ + public long getVariableTime() { + return 0L; + } + + /** + * Sets the variable expiration time in nanoseconds. This update may be set lazily and rely on the + * memory fence when the lock is released. + */ + public void setVariableTime(long time) {} + + /** + * Atomically sets the variable time to the given updated value if the current value equals the + * expected value and returns if the update was successful. + */ + public boolean casVariableTime(long expect, long update) { + throw new UnsupportedOperationException(); + } + + public Node getPreviousInVariableOrder() { + throw new UnsupportedOperationException(); + } + + public void setPreviousInVariableOrder(Node prev) { + throw new UnsupportedOperationException(); + } + + public Node getNextInVariableOrder() { + throw new UnsupportedOperationException(); + } + + public void setNextInVariableOrder(Node prev) { + throw new UnsupportedOperationException(); + } + + /* --------------- Access order --------------- */ + + public static final int WINDOW = 0; + public static final int PROBATION = 1; + public static final int PROTECTED = 2; + + /** Returns if the entry is in the Window or Main space. */ + public boolean inWindow() { + return getQueueType() == WINDOW; + } + + /** Returns if the entry is in the Main space's probation queue. */ + public boolean inMainProbation() { + return getQueueType() == PROBATION; + } + + /** Returns if the entry is in the Main space's protected queue. */ + public boolean inMainProtected() { + return getQueueType() == PROTECTED; + } + + /** Sets the status to the Window queue. */ + public void makeWindow() { + setQueueType(WINDOW); + } + + /** Sets the status to the Main space's probation queue. */ + public void makeMainProbation() { + setQueueType(PROBATION); + } + + /** Sets the status to the Main space's protected queue. */ + public void makeMainProtected() { + setQueueType(PROTECTED); + } + + /** Returns the queue that the entry's resides in (window, probation, or protected). */ + public int getQueueType() { + return WINDOW; + } + + /** Set queue that the entry resides in (window, probation, or protected). */ + public void setQueueType(int queueType) { + throw new UnsupportedOperationException(); + } + + /** Returns the time that this entry was last accessed, in ns. */ + public long getAccessTime() { + return 0L; + } + + /** + * Sets the access time in nanoseconds. This update may be set lazily and rely on the memory fence + * when the lock is released. + */ + public void setAccessTime(long time) {} + + @Override + public Node getPreviousInAccessOrder() { + return null; + } + + @Override + public void setPreviousInAccessOrder(Node prev) { + throw new UnsupportedOperationException(); + } + + @Override + public Node getNextInAccessOrder() { + return null; + } + + @Override + public void setNextInAccessOrder(Node next) { + throw new UnsupportedOperationException(); + } + + /* --------------- Write order --------------- */ + + /** Returns the time that this entry was last written, in ns. */ + public long getWriteTime() { + return 0L; + } + + /** + * Sets the write time in nanoseconds. This update may be set lazily and rely on the memory fence + * when the lock is released. + */ + public void setWriteTime(long time) {} + + /** + * Atomically sets the write time to the given updated value if the current value equals the + * expected value and returns if the update was successful. + */ + public boolean casWriteTime(long expect, long update) { + throw new UnsupportedOperationException(); + } + + @Override + public Node getPreviousInWriteOrder() { + return null; + } + + @Override + public void setPreviousInWriteOrder(Node prev) { + throw new UnsupportedOperationException(); + } + + @Override + public Node getNextInWriteOrder() { + return null; + } + + @Override + public void setNextInWriteOrder(Node next) { + throw new UnsupportedOperationException(); + } + + @Override + public final String toString() { + return String.format( + "%s=[key=%s, value=%s, weight=%d, queueType=%,d, accessTimeNS=%,d, " + + "writeTimeNS=%,d, varTimeNs=%,d, prevInAccess=%s, nextInAccess=%s, prevInWrite=%s, " + + "nextInWrite=%s]", + getClass().getSimpleName(), + getKey(), + getValue(), + getWeight(), + getQueueType(), + getAccessTime(), + getWriteTime(), + getVariableTime(), + getPreviousInAccessOrder() != null, + getNextInAccessOrder() != null, + getPreviousInWriteOrder() != null, + getNextInWriteOrder() != null); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/NodeFactory.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/NodeFactory.java new file mode 100644 index 0000000000000..6990bbb66505e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/NodeFactory.java @@ -0,0 +1,136 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.References.WeakKeyReference; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A factory for cache nodes optimized for a particular configuration. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +interface NodeFactory { + Object RETIRED_STRONG_KEY = new Object(); + + Object DEAD_STRONG_KEY = new Object(); + + WeakKeyReference RETIRED_WEAK_KEY = new WeakKeyReference(null, null); + + WeakKeyReference DEAD_WEAK_KEY = new WeakKeyReference(null, null); + + /** Returns a node optimized for the specified features. */ + Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now); + + /** Returns a node optimized for the specified features. */ + Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now); + + /** + * Returns a key suitable for inserting into the cache. If the cache holds keys strongly then the + * key is returned. If the cache holds keys weakly then a {@link WeakKeyReference} holding the + * key argument is returned. + */ + default Object newReferenceKey(K key, ReferenceQueue referenceQueue) { + return key; + } + + /** + * Returns a key suitable for looking up an entry in the cache. If the cache holds keys strongly + * then the key is returned. If the cache holds keys weakly then a {@link + * org.apache.iotdb.github.benmanes.caffeine.cache.References.LookupKeyReference} holding the key + * argument is returned. + */ + default Object newLookupKey(Object key) { + return key; + } + + /** Returns a factory optimized for the specified features. */ + static NodeFactory newFactory(Caffeine builder, boolean isAsync) { + StringBuilder sb = new StringBuilder("org.apache.iotdb.github.benmanes.caffeine.cache."); + if (builder.isStrongKeys()) { + sb.append('P'); + } else { + sb.append('F'); + } + if (builder.isStrongValues()) { + sb.append('S'); + } else if (builder.isWeakValues()) { + sb.append('W'); + } else { + sb.append('D'); + } + if (builder.expiresVariable()) { + if (builder.refreshAfterWrite()) { + sb.append('A'); + if (builder.evicts()) { + sb.append('W'); + } + } else { + sb.append('W'); + } + } else { + if (builder.expiresAfterAccess()) { + sb.append('A'); + } + if (builder.expiresAfterWrite()) { + sb.append('W'); + } + } + if (builder.refreshAfterWrite()) { + sb.append('R'); + } + if (builder.evicts()) { + sb.append('M'); + if (isAsync || (builder.isWeighted() && (builder.weigher != Weigher.singletonWeigher()))) { + sb.append('W'); + } else { + sb.append('S'); + } + } + try { + Class clazz = Class.forName(sb.toString()); + @SuppressWarnings("unchecked") + NodeFactory factory = (NodeFactory) clazz.getDeclaredConstructor().newInstance(); + return factory; + } catch (ReflectiveOperationException e) { + throw new IllegalStateException(sb.toString(), e); + } + } + + /** Returns whether this factory supports weak values. */ + default boolean weakValues() { + return false; + } + + /** Returns whether this factory supports soft values. */ + default boolean softValues() { + return false; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PD.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PD.java new file mode 100644 index 0000000000000..4693282fafeae --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PD.java @@ -0,0 +1,142 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.References.SoftValueReference; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • StrongKeys + *
  • SoftValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PD extends Node implements NodeFactory { + protected static final long VALUE_OFFSET = + UnsafeAccess.objectFieldOffset( + PD.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.VALUE); + + volatile SoftValueReference value; + + PD() {} + + PD( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + this(key, value, valueReferenceQueue, weight, now); + } + + PD(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + UnsafeAccess.UNSAFE.putObject( + this, VALUE_OFFSET, new SoftValueReference(keyReference, value, valueReferenceQueue)); + } + + public final Object getKeyReference() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + return valueRef.getKeyReference(); + } + + public final K getKey() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + return (K) valueRef.getKeyReference(); + } + + public final V getValue() { + for (; ; ) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + V referent = ref.get(); + if ((referent != null) || (ref == value)) { + return referent; + } + } + } + + public final Object getValueReference() { + return UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + } + + public final void setValue(V value, ReferenceQueue referenceQueue) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + UnsafeAccess.UNSAFE.putOrderedObject( + this, VALUE_OFFSET, new SoftValueReference(getKeyReference(), value, referenceQueue)); + ref.clear(); + } + + public final boolean containsValue(Object value) { + return getValue() == value; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PD<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PD<>(keyReference, value, valueReferenceQueue, weight, now); + } + + public boolean softValues() { + return true; + } + + public final boolean isAlive() { + Object key = getKeyReference(); + return (key != RETIRED_STRONG_KEY) && (key != DEAD_STRONG_KEY); + } + + public final boolean isRetired() { + return (getKeyReference() == RETIRED_STRONG_KEY); + } + + public final void retire() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + valueRef.setKeyReference(RETIRED_STRONG_KEY); + valueRef.clear(); + } + + public final boolean isDead() { + return (getKeyReference() == DEAD_STRONG_KEY); + } + + public final void die() { + SoftValueReference valueRef = (SoftValueReference) getValueReference(); + valueRef.setKeyReference(DEAD_STRONG_KEY); + valueRef.clear(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDA.java new file mode 100644 index 0000000000000..e4b492c29666e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDA.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDA extends PD { + protected static final long ACCESS_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PDA.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.ACCESS_TIME); + + volatile long accessTime; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDA() {} + + PDA( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + PDA(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInAccessOrder; + } + + public void setPreviousInVariableOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInVariableOrder() { + return nextInAccessOrder; + } + + public void setNextInVariableOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final long getAccessTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public final void setAccessTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public final Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public final void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public final Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public final void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDA<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDA<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAMS.java new file mode 100644 index 0000000000000..dbc2ff80f71e0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAMS.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDAMS extends PDA { + int queueType; + + PDAMS() {} + + PDAMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDAMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAMW.java new file mode 100644 index 0000000000000..16a01defd89d9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAMW.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDAMW extends PDA { + int queueType; + + int weight; + + int policyWeight; + + PDAMW() {} + + PDAMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDAMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAR.java new file mode 100644 index 0000000000000..41b8561199847 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAR.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDAR extends PDA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PDAR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + PDAR() {} + + PDAR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PDAR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDARMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDARMS.java new file mode 100644 index 0000000000000..a1e3fc1d26b21 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDARMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDARMS extends PDAR { + int queueType; + + PDARMS() {} + + PDARMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDARMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDARMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDARMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDARMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDARMW.java new file mode 100644 index 0000000000000..02f8ba052e94e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDARMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDARMW extends PDAR { + int queueType; + + int weight; + + int policyWeight; + + PDARMW() {} + + PDARMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDARMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDARMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDARMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAW.java new file mode 100644 index 0000000000000..3fdd4a242799c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAW.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDAW extends PDA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PDAW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + PDAW() {} + + PDAW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PDAW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWMS.java new file mode 100644 index 0000000000000..1f50080869b87 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDAWMS extends PDAW { + int queueType; + + PDAWMS() {} + + PDAWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDAWMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWMW.java new file mode 100644 index 0000000000000..de155f12d0eb9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDAWMW extends PDAW { + int queueType; + + int weight; + + int policyWeight; + + PDAWMW() {} + + PDAWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDAWMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWR.java new file mode 100644 index 0000000000000..cd66233440feb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWR.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDAWR extends PDAW { + PDAWR() {} + + PDAWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDAWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWRMS.java new file mode 100644 index 0000000000000..3c6497f1308c1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWRMS.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDAWRMS extends PDAWR { + int queueType; + + PDAWRMS() {} + + PDAWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDAWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWRMW.java new file mode 100644 index 0000000000000..67a67772f3d82 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDAWRMW.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDAWRMW extends PDAWR { + int queueType; + + int weight; + + int policyWeight; + + PDAWRMW() {} + + PDAWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDAWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDAWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDAWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDMS.java new file mode 100644 index 0000000000000..c3adad2771f04 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDMS.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDMS extends PD { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDMS() {} + + PDMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDMW.java new file mode 100644 index 0000000000000..1f6a1cb8f4d1b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDMW.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDMW extends PD { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDMW() {} + + PDMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDR.java new file mode 100644 index 0000000000000..8c179d9f9ff36 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDR.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDR extends PD { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PDR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + PDR() {} + + PDR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PDR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDRMS.java new file mode 100644 index 0000000000000..37d528c0d89ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDRMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDRMS extends PDR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDRMS() {} + + PDRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDRMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDRMW.java new file mode 100644 index 0000000000000..53f928152bd67 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDRMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDRMW extends PDR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDRMW() {} + + PDRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDRMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDW.java new file mode 100644 index 0000000000000..04b9c79646414 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDW.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDW extends PD { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PDW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + PDW() {} + + PDW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PDW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWMS.java new file mode 100644 index 0000000000000..e25bb16e956df --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDWMS extends PDW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDWMS() {} + + PDWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWMW.java new file mode 100644 index 0000000000000..ba6c3f628a547 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDWMW extends PDW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDWMW() {} + + PDWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWR.java new file mode 100644 index 0000000000000..1c79914ec1238 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWR.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PDWR extends PDW { + PDWR() {} + + PDWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWRMS.java new file mode 100644 index 0000000000000..8538e648b8c7d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWRMS.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDWRMS extends PDWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDWRMS() {} + + PDWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PDWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWRMW.java new file mode 100644 index 0000000000000..6625b92705971 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PDWRMW.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • SoftValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PDWRMW extends PDWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PDWRMW() {} + + PDWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PDWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PDWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PDWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PS.java new file mode 100644 index 0000000000000..e63bf641f4773 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PS.java @@ -0,0 +1,128 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; +import java.util.Objects; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • StrongKeys + *
  • StrongValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PS extends Node implements NodeFactory { + protected static final long KEY_OFFSET = + UnsafeAccess.objectFieldOffset( + PS.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.KEY); + + protected static final long VALUE_OFFSET = + UnsafeAccess.objectFieldOffset( + PS.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.VALUE); + + volatile K key; + + volatile V value; + + PS() {} + + PS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + this(key, value, valueReferenceQueue, weight, now); + } + + PS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + UnsafeAccess.UNSAFE.putObject(this, KEY_OFFSET, keyReference); + UnsafeAccess.UNSAFE.putObject(this, VALUE_OFFSET, value); + } + + public final K getKey() { + return (K) UnsafeAccess.UNSAFE.getObject(this, KEY_OFFSET); + } + + public final Object getKeyReference() { + return UnsafeAccess.UNSAFE.getObject(this, KEY_OFFSET); + } + + public final V getValue() { + return value; + } + + public final Object getValueReference() { + return UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + } + + public final void setValue(V value, ReferenceQueue referenceQueue) { + UnsafeAccess.UNSAFE.putObject(this, VALUE_OFFSET, value); + } + + public final boolean containsValue(Object value) { + return Objects.equals(value, getValue()); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PS<>(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean isAlive() { + Object key = getKeyReference(); + return (key != RETIRED_STRONG_KEY) && (key != DEAD_STRONG_KEY); + } + + public final boolean isRetired() { + return (getKeyReference() == RETIRED_STRONG_KEY); + } + + public final void retire() { + UnsafeAccess.UNSAFE.putObject(this, KEY_OFFSET, RETIRED_STRONG_KEY); + } + + public final boolean isDead() { + return (getKeyReference() == DEAD_STRONG_KEY); + } + + public final void die() { + UnsafeAccess.UNSAFE.putObject(this, VALUE_OFFSET, null); + UnsafeAccess.UNSAFE.putObject(this, KEY_OFFSET, DEAD_STRONG_KEY); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSA.java new file mode 100644 index 0000000000000..73ddda58e913c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSA.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSA extends PS { + protected static final long ACCESS_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PSA.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.ACCESS_TIME); + + volatile long accessTime; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSA() {} + + PSA( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + PSA(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInAccessOrder; + } + + public void setPreviousInVariableOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInVariableOrder() { + return nextInAccessOrder; + } + + public void setNextInVariableOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final long getAccessTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public final void setAccessTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public final Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public final void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public final Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public final void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSA<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSA<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAMS.java new file mode 100644 index 0000000000000..d1a731114b023 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAMS.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSAMS extends PSA { + int queueType; + + PSAMS() {} + + PSAMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSAMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAMW.java new file mode 100644 index 0000000000000..4b4bdf66bfa3c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAMW.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSAMW extends PSA { + int queueType; + + int weight; + + int policyWeight; + + PSAMW() {} + + PSAMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSAMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAR.java new file mode 100644 index 0000000000000..b6e93da3215e6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAR.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSAR extends PSA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PSAR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + PSAR() {} + + PSAR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PSAR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSARMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSARMS.java new file mode 100644 index 0000000000000..831fa813788d5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSARMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSARMS extends PSAR { + int queueType; + + PSARMS() {} + + PSARMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSARMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSARMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSARMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSARMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSARMW.java new file mode 100644 index 0000000000000..46434453210ce --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSARMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSARMW extends PSAR { + int queueType; + + int weight; + + int policyWeight; + + PSARMW() {} + + PSARMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSARMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSARMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSARMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAW.java new file mode 100644 index 0000000000000..504c404d25025 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAW.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSAW extends PSA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PSAW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + PSAW() {} + + PSAW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PSAW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWMS.java new file mode 100644 index 0000000000000..1c5882e5db0b4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSAWMS extends PSAW { + int queueType; + + PSAWMS() {} + + PSAWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSAWMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWMW.java new file mode 100644 index 0000000000000..d04f7ec1de86b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSAWMW extends PSAW { + int queueType; + + int weight; + + int policyWeight; + + PSAWMW() {} + + PSAWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSAWMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWR.java new file mode 100644 index 0000000000000..0a5b766007fa3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWR.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSAWR extends PSAW { + PSAWR() {} + + PSAWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSAWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWRMS.java new file mode 100644 index 0000000000000..8bf0fc0d53565 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWRMS.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSAWRMS extends PSAWR { + int queueType; + + PSAWRMS() {} + + PSAWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSAWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWRMW.java new file mode 100644 index 0000000000000..2ab5ec3aa2b96 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSAWRMW.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSAWRMW extends PSAWR { + int queueType; + + int weight; + + int policyWeight; + + PSAWRMW() {} + + PSAWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSAWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSAWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSAWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSMS.java new file mode 100644 index 0000000000000..2edb516269b95 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSMS.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSMS extends PS { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSMS() {} + + PSMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSMW.java new file mode 100644 index 0000000000000..671bb886f7a89 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSMW.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSMW extends PS { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSMW() {} + + PSMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSR.java new file mode 100644 index 0000000000000..3d53ee9d1a21a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSR.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSR extends PS { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PSR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + PSR() {} + + PSR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PSR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSRMS.java new file mode 100644 index 0000000000000..12927fb8d1237 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSRMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSRMS extends PSR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSRMS() {} + + PSRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSRMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSRMW.java new file mode 100644 index 0000000000000..47a6f089d61f8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSRMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSRMW extends PSR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSRMW() {} + + PSRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSRMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSW.java new file mode 100644 index 0000000000000..47a2ccb3db177 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSW.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSW extends PS { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PSW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + PSW() {} + + PSW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PSW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWMS.java new file mode 100644 index 0000000000000..1e0d0d9d622d8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSWMS extends PSW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSWMS() {} + + PSWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWMW.java new file mode 100644 index 0000000000000..b7f0baaac44da --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSWMW extends PSW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSWMW() {} + + PSWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWR.java new file mode 100644 index 0000000000000..577ac49983c6b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWR.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PSWR extends PSW { + PSWR() {} + + PSWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWRMS.java new file mode 100644 index 0000000000000..bbe101a401900 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWRMS.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSWRMS extends PSWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSWRMS() {} + + PSWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PSWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWRMW.java new file mode 100644 index 0000000000000..0a01a87be2a66 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PSWRMW.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PSWRMW extends PSWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PSWRMW() {} + + PSWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PSWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PSWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PSWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PW.java new file mode 100644 index 0000000000000..c96f54a785fd4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PW.java @@ -0,0 +1,142 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.References.WeakValueReference; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • StrongKeys + *
  • WeakValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PW extends Node implements NodeFactory { + protected static final long VALUE_OFFSET = + UnsafeAccess.objectFieldOffset( + PW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.VALUE); + + volatile WeakValueReference value; + + PW() {} + + PW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + this(key, value, valueReferenceQueue, weight, now); + } + + PW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + UnsafeAccess.UNSAFE.putObject( + this, VALUE_OFFSET, new WeakValueReference(keyReference, value, valueReferenceQueue)); + } + + public final Object getKeyReference() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + return valueRef.getKeyReference(); + } + + public final K getKey() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + return (K) valueRef.getKeyReference(); + } + + public final V getValue() { + for (; ; ) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + V referent = ref.get(); + if ((referent != null) || (ref == value)) { + return referent; + } + } + } + + public final Object getValueReference() { + return UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + } + + public final void setValue(V value, ReferenceQueue referenceQueue) { + Reference ref = (Reference) UnsafeAccess.UNSAFE.getObject(this, VALUE_OFFSET); + UnsafeAccess.UNSAFE.putOrderedObject( + this, VALUE_OFFSET, new WeakValueReference(getKeyReference(), value, referenceQueue)); + ref.clear(); + } + + public final boolean containsValue(Object value) { + return getValue() == value; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PW<>(keyReference, value, valueReferenceQueue, weight, now); + } + + public boolean weakValues() { + return true; + } + + public final boolean isAlive() { + Object key = getKeyReference(); + return (key != RETIRED_STRONG_KEY) && (key != DEAD_STRONG_KEY); + } + + public final boolean isRetired() { + return (getKeyReference() == RETIRED_STRONG_KEY); + } + + public final void retire() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + valueRef.setKeyReference(RETIRED_STRONG_KEY); + valueRef.clear(); + } + + public final boolean isDead() { + return (getKeyReference() == DEAD_STRONG_KEY); + } + + public final void die() { + WeakValueReference valueRef = (WeakValueReference) getValueReference(); + valueRef.setKeyReference(DEAD_STRONG_KEY); + valueRef.clear(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWA.java new file mode 100644 index 0000000000000..1ea4b5b79ae13 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWA.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWA extends PW { + protected static final long ACCESS_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PWA.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.ACCESS_TIME); + + volatile long accessTime; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWA() {} + + PWA( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + PWA(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInAccessOrder; + } + + public void setPreviousInVariableOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInVariableOrder() { + return nextInAccessOrder; + } + + public void setNextInVariableOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final long getAccessTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public final void setAccessTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public final Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public final void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public final Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public final void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWA<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWA<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAMS.java new file mode 100644 index 0000000000000..c88576a05579a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAMS.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWAMS extends PWA { + int queueType; + + PWAMS() {} + + PWAMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWAMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAMW.java new file mode 100644 index 0000000000000..c32e901a09469 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAMW.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWAMW extends PWA { + int queueType; + + int weight; + + int policyWeight; + + PWAMW() {} + + PWAMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWAMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAR.java new file mode 100644 index 0000000000000..8e115ee311bcb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAR.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWAR extends PWA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PWAR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + PWAR() {} + + PWAR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PWAR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWARMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWARMS.java new file mode 100644 index 0000000000000..4983c4f0c7ec0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWARMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWARMS extends PWAR { + int queueType; + + PWARMS() {} + + PWARMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWARMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWARMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWARMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWARMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWARMW.java new file mode 100644 index 0000000000000..a1774cafe5ee0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWARMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWARMW extends PWAR { + int queueType; + + int weight; + + int policyWeight; + + PWARMW() {} + + PWARMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWARMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWARMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWARMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAW.java new file mode 100644 index 0000000000000..1a892692800f7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAW.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWAW extends PWA { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PWAW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + PWAW() {} + + PWAW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PWAW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWMS.java new file mode 100644 index 0000000000000..0570aa73c6e3a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWMS.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWAWMS extends PWAW { + int queueType; + + PWAWMS() {} + + PWAWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWAWMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWMW.java new file mode 100644 index 0000000000000..85bfb0d14ce96 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWMW.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWAWMW extends PWAW { + int queueType; + + int weight; + + int policyWeight; + + PWAWMW() {} + + PWAWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWAWMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWR.java new file mode 100644 index 0000000000000..8d89fea503acc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWR.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWAWR extends PWAW { + PWAWR() {} + + PWAWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWAWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, ACCESS_TIME_OFFSET); + } + + public void setVariableTime(long accessTime) { + UnsafeAccess.UNSAFE.putLong(this, ACCESS_TIME_OFFSET, accessTime); + } + + public boolean casVariableTime(long expect, long update) { + return (accessTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, ACCESS_TIME_OFFSET, expect, update); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWRMS.java new file mode 100644 index 0000000000000..69364ecf68bdb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWRMS.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWAWRMS extends PWAWR { + int queueType; + + PWAWRMS() {} + + PWAWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWAWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWRMW.java new file mode 100644 index 0000000000000..5f13121c4d93a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWAWRMW.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWAWRMW extends PWAWR { + int queueType; + + int weight; + + int policyWeight; + + PWAWRMW() {} + + PWAWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWAWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWAWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWAWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWMS.java new file mode 100644 index 0000000000000..159850705227d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWMS.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWMS extends PW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWMS() {} + + PWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWMW.java new file mode 100644 index 0000000000000..bd9619a58c8cd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWMW.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWMW extends PW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWMW() {} + + PWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWR.java new file mode 100644 index 0000000000000..04b047294c4c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWR.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWR extends PW { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PWR.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + PWR() {} + + PWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWRMS.java new file mode 100644 index 0000000000000..14cb3b127bfe7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWRMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWRMS extends PWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWRMS() {} + + PWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWRMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWRMW.java new file mode 100644 index 0000000000000..02abf627fc017 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWRMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWRMW extends PWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWRMW() {} + + PWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWRMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWW.java new file mode 100644 index 0000000000000..d89443e305af6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWW.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWW extends PW { + protected static final long WRITE_TIME_OFFSET = + UnsafeAccess.objectFieldOffset( + PWW.class, org.apache.iotdb.github.benmanes.caffeine.cache.LocalCacheFactory.WRITE_TIME); + + volatile long writeTime; + + Node previousInWriteOrder; + + Node nextInWriteOrder; + + PWW() {} + + PWW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + PWW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, now); + } + + public Node getPreviousInVariableOrder() { + return previousInWriteOrder; + } + + public void setPreviousInVariableOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public Node getNextInVariableOrder() { + return nextInWriteOrder; + } + + public void setNextInVariableOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public long getVariableTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public void setVariableTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public boolean casVariableTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public final long getWriteTime() { + return UnsafeAccess.UNSAFE.getLong(this, WRITE_TIME_OFFSET); + } + + public final void setWriteTime(long writeTime) { + UnsafeAccess.UNSAFE.putLong(this, WRITE_TIME_OFFSET, writeTime); + } + + public final Node getPreviousInWriteOrder() { + return previousInWriteOrder; + } + + public final void setPreviousInWriteOrder(Node previousInWriteOrder) { + this.previousInWriteOrder = previousInWriteOrder; + } + + public final Node getNextInWriteOrder() { + return nextInWriteOrder; + } + + public final void setNextInWriteOrder(Node nextInWriteOrder) { + this.nextInWriteOrder = nextInWriteOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWMS.java new file mode 100644 index 0000000000000..a3539ed702c80 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWMS.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWWMS extends PWW { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWWMS() {} + + PWWMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWWMS(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWWMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWWMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWMW.java new file mode 100644 index 0000000000000..27e8d9e68c05a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWMW.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWWMW extends PWW { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWWMW() {} + + PWWMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWWMW(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWWMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWWMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWR.java new file mode 100644 index 0000000000000..2daeba78717e2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWR.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +class PWWR extends PWW { + PWWR() {} + + PWWR( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWWR(Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public final boolean casWriteTime(long expect, long update) { + return (writeTime == expect) + && UnsafeAccess.UNSAFE.compareAndSwapLong(this, WRITE_TIME_OFFSET, expect, update); + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWWR<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWWR<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWRMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWRMS.java new file mode 100644 index 0000000000000..284d558efc240 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWRMS.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWWRMS extends PWWR { + int queueType; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWWRMS() {} + + PWWRMS( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + PWWRMS( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWWRMS<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWWRMS<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWRMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWRMW.java new file mode 100644 index 0000000000000..c72a0387ae763 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/PWWRMW.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache entry that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • WeakValues (inherited) + *
  • ExpireWrite (inherited) + *
  • RefreshWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "PMD.UnusedFormalParameter", "MissingOverride", "NullAway"}) +final class PWWRMW extends PWWR { + int queueType; + + int weight; + + int policyWeight; + + Node previousInAccessOrder; + + Node nextInAccessOrder; + + PWWRMW() {} + + PWWRMW( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + super(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + PWWRMW( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + super(keyReference, value, valueReferenceQueue, weight, now); + this.weight = weight; + } + + public int getQueueType() { + return queueType; + } + + public void setQueueType(int queueType) { + this.queueType = queueType; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public int getPolicyWeight() { + return policyWeight; + } + + public void setPolicyWeight(int policyWeight) { + this.policyWeight = policyWeight; + } + + public Node getPreviousInAccessOrder() { + return previousInAccessOrder; + } + + public void setPreviousInAccessOrder(Node previousInAccessOrder) { + this.previousInAccessOrder = previousInAccessOrder; + } + + public Node getNextInAccessOrder() { + return nextInAccessOrder; + } + + public void setNextInAccessOrder(Node nextInAccessOrder) { + this.nextInAccessOrder = nextInAccessOrder; + } + + public Node newNode( + K key, + ReferenceQueue keyReferenceQueue, + V value, + ReferenceQueue valueReferenceQueue, + int weight, + long now) { + return new PWWRMW<>(key, keyReferenceQueue, value, valueReferenceQueue, weight, now); + } + + public Node newNode( + Object keyReference, V value, ReferenceQueue valueReferenceQueue, int weight, long now) { + return new PWWRMW<>(keyReference, value, valueReferenceQueue, weight, now); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Pacer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Pacer.java new file mode 100644 index 0000000000000..e89a5e9aface9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Pacer.java @@ -0,0 +1,95 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.ceilingPowerOfTwo; + +/** + * A pacing scheduler that prevents executions from happening too frequently. Only one task may be + * scheduled at any given time, the earliest pending task takes precedence, and the delay may be + * increased if it is less than a tolerance threshold. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +final class Pacer { + static final long TOLERANCE = ceilingPowerOfTwo(TimeUnit.SECONDS.toNanos(1)); // 1.07s + + final Scheduler scheduler; + + long nextFireTime; + Future future; + + Pacer(Scheduler scheduler) { + this.scheduler = requireNonNull(scheduler); + } + + /** Schedules the task, pacing the execution if occurring too often. */ + public void schedule(Executor executor, Runnable command, long now, long delay) { + long scheduleAt = (now + delay); + + if (future == null) { + // short-circuit an immediate scheduler causing an infinite loop during initialization + if (nextFireTime != 0L) { + return; + } + } else if ((nextFireTime - now) > 0L) { + // Determine whether to reschedule + if (maySkip(scheduleAt)) { + return; + } + future.cancel(/* mayInterruptIfRunning */ false); + } + long actualDelay = calculateSchedule(now, delay, scheduleAt); + future = scheduler.schedule(executor, command, actualDelay, TimeUnit.NANOSECONDS); + } + + /** Attempts to cancel execution of the scheduled task, if present. */ + public void cancel() { + if (future != null) { + future.cancel(/* mayInterruptIfRunning */ false); + nextFireTime = 0L; + future = null; + } + } + + /** + * Returns if the current fire time is sooner, or if it is later and within the tolerance limit. + */ + boolean maySkip(long scheduleAt) { + long delta = (scheduleAt - nextFireTime); + return (delta >= 0L) || (-delta <= TOLERANCE); + } + + /** Returns the delay and sets the next fire time. */ + long calculateSchedule(long now, long delay, long scheduleAt) { + if (delay <= TOLERANCE) { + // Use a minimum delay if close to now + nextFireTime = (now + TOLERANCE); + return TOLERANCE; + } + nextFireTime = scheduleAt; + return delay; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Policy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Policy.java new file mode 100644 index 0000000000000..80ecc6967f41c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Policy.java @@ -0,0 +1,494 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.time.Duration; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.concurrent.TimeUnit; + +/** + * An access point for inspecting and performing low-level operations based on the cache's runtime + * characteristics. These operations are optional and dependent on how the cache was constructed and + * what abilities the implementation exposes. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public interface Policy { + + /** + * Returns whether the cache statistics are being accumulated. + * + * @return if cache statistics are being recorded + */ + boolean isRecordingStats(); + + /** + * Returns the value associated with the {@code key} in this cache, or {@code null} if there is no + * cached value for the {@code key}. Unlike {@link Cache#getIfPresent(Object)}, this method does + * not produce any side effects such as updating statistics, the eviction policy, reseting the + * expiration time, or triggering a refresh. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or {@code null} if this cache contains + * no mapping for the key + * @throws NullPointerException if the specified key is null + */ + default V getIfPresentQuietly(Object key) { + // This method was added & implemented in version 2.8.3 + throw new UnsupportedOperationException(); + } + + /** + * Returns access to perform operations based on the maximum size or maximum weight eviction + * policy. If the cache was not constructed with a size-based bound or the implementation does not + * support these operations, an empty {@link Optional} is returned. + * + * @return access to low-level operations for this cache if an eviction policy is used + */ + Optional> eviction(); + + /** + * Returns access to perform operations based on the time-to-idle expiration policy. This policy + * determines that an entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations on the + * collection-views of {@link Cache#asMap}. + * + *

If the cache was not constructed with access-based expiration or the implementation does not + * support these operations, an empty {@link Optional} is returned. + * + * @return access to low-level operations for this cache if a time-to-idle expiration policy is + * used + */ + Optional> expireAfterAccess(); + + /** + * Returns access to perform operations based on the time-to-live expiration policy. This policy + * determines that an entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + *

If the cache was not constructed with write-based expiration or the implementation does not + * support these operations, an empty {@link Optional} is returned. + * + * @return access to low-level operations for this cache if a time-to-live expiration policy is + * used + */ + Optional> expireAfterWrite(); + + /** + * Returns access to perform operations based on the variable expiration policy. This policy + * determines that an entry should be automatically removed from the cache once a per-entry + * duration has elapsed. + * + *

If the cache was not constructed with variable expiration or the implementation does not + * support these operations, an empty {@link Optional} is returned. + * + * @return access to low-level operations for this cache if a variable expiration policy is used + */ + default Optional> expireVariably() { + // This method will be abstract in version 3.0.0 + return Optional.empty(); + } + + /** + * Returns access to perform operations based on the time-to-live refresh policy. This policy + * determines that an entry should be automatically reloaded once a fixed duration has elapsed + * after the entry's creation, or the most recent replacement of its value. + * + *

If the cache was not constructed with write-based refresh or the implementation does not + * support these operations, an empty {@link Optional} is returned. + * + * @return access to low-level operations for this cache if a time-to-live refresh policy is used + */ + Optional> refreshAfterWrite(); + + /** The low-level operations for a cache with a size-based eviction policy. */ + interface Eviction { + + /** + * Returns whether the cache is bounded by a maximum size or maximum weight. + * + * @return if the size bounding takes into account the entry's weight + */ + boolean isWeighted(); + + /** + * Returns the weight of the entry. If this cache does not use a weighted size bound or does not + * support querying for the entry's weight, then the {@link OptionalInt} will be empty. + * + * @param key the key for the entry being queried + * @return the weight if the entry is present in the cache + */ + default OptionalInt weightOf(K key) { + // This method will be abstract in version 3.0.0 + return OptionalInt.empty(); + } + + /** + * Returns the approximate accumulated weight of entries in this cache. If this cache does not + * use a weighted size bound, then the {@link OptionalLong} will be empty. + * + * @return the combined weight of the values in this cache + */ + OptionalLong weightedSize(); + + /** + * Returns the maximum total weighted or unweighted size of this cache, depending on how the + * cache was constructed. This value can be best understood by inspecting {@link #isWeighted()}. + * + * @return the maximum size bounding, which may be either weighted or unweighted + */ + long getMaximum(); + + /** + * Specifies the maximum total size of this cache. This value may be interpreted as the weighted + * or unweighted threshold size based on how this cache was constructed. If the cache currently + * exceeds the new maximum size this operation eagerly evict entries until the cache shrinks to + * the appropriate size. + * + *

Note that some implementations may have an internal inherent bound on the maximum total + * size. If the value specified exceeds that bound, then the value is set to the internal + * maximum. + * + * @param maximum the maximum, interpreted as weighted or unweighted size depending on how this + * cache was constructed + * @throws IllegalArgumentException if the maximum size specified is negative + */ + void setMaximum(long maximum); + + /** + * Returns an unmodifiable snapshot {@link Map} view of the cache with ordered traversal. The + * order of iteration is from the entries least likely to be retained (coldest) to the entries + * most likely to be retained (hottest). This order is determined by the eviction policy's best + * guess at the time of creating this snapshot view. + * + *

Beware that obtaining the mappings is NOT a constant-time operation. Because of + * the asynchronous nature of the page replacement policy, determining the retention ordering + * requires a traversal of the entries. + * + * @param limit the maximum size of the returned map (use {@link Integer#MAX_VALUE} to disregard + * the limit) + * @return a snapshot view of the cache from coldest entry to the hottest + */ + Map coldest(int limit); + + /** + * Returns an unmodifiable snapshot {@link Map} view of the cache with ordered traversal. The + * order of iteration is from the entries most likely to be retained (hottest) to the entries + * least likely to be retained (coldest). This order is determined by the eviction policy's best + * guess at the time of creating this snapshot view. + * + *

Beware that obtaining the mappings is NOT a constant-time operation. Because of + * the asynchronous nature of the page replacement policy, determining the retention ordering + * requires a traversal of the entries. + * + * @param limit the maximum size of the returned map (use {@link Integer#MAX_VALUE} to disregard + * the limit) + * @return a snapshot view of the cache from hottest entry to the coldest + */ + Map hottest(int limit); + } + + /** The low-level operations for a cache with a fixed expiration policy. */ + interface Expiration { // To be renamed FixedExpiration in version 3.0.0 + + /** + * Returns the age of the entry based on the expiration policy. The entry's age is the cache's + * estimate of the amount of time since the entry's expiration was last reset. + * + *

An expiration policy uses the age to determine if an entry is fresh or stale by comparing + * it to the freshness lifetime. This is calculated as {@code fresh = freshnessLifetime > age} + * where {@code freshnessLifetime = expires - currentTime}. + * + *

This method is scheduled for removal in version 3.0.0. + * + * @param key the key for the entry being queried + * @param unit the unit that {@code age} is expressed in + * @return the age if the entry is present in the cache + */ + OptionalLong ageOf(K key, TimeUnit unit); + + /** + * Returns the age of the entry based on the expiration policy. The entry's age is the cache's + * estimate of the amount of time since the entry's expiration was last reset. + * + *

An expiration policy uses the age to determine if an entry is fresh or stale by comparing + * it to the freshness lifetime. This is calculated as {@code fresh = freshnessLifetime > age} + * where {@code freshnessLifetime = expires - currentTime}. + * + * @param key the key for the entry being queried + * @return the age if the entry is present in the cache + */ + default Optional ageOf(K key) { + // This method will be abstract in version 3.0.0 + OptionalLong duration = ageOf(key, TimeUnit.NANOSECONDS); + return duration.isPresent() + ? Optional.of(Duration.ofNanos(duration.getAsLong())) + : Optional.empty(); + } + + /** + * Returns the fixed duration used to determine if an entry should be automatically removed due + * to elapsing this time bound. An entry is considered fresh if its age is less than this + * duration, and stale otherwise. The expiration policy determines when the entry's age is + * reset. + * + *

This method is scheduled for removal in version 3.0.0. + * + * @param unit the unit that duration is expressed in + * @return the length of time after which an entry should be automatically removed + */ + long getExpiresAfter(TimeUnit unit); + + /** + * Returns the fixed duration used to determine if an entry should be automatically removed due + * to elapsing this time bound. An entry is considered fresh if its age is less than this + * duration, and stale otherwise. The expiration policy determines when the entry's age is + * reset. + * + * @return the length of time after which an entry should be automatically removed + */ + default Duration getExpiresAfter() { + // This method will be abstract in version 3.0.0 + return Duration.ofNanos(getExpiresAfter(TimeUnit.NANOSECONDS)); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed + * duration has elapsed. The expiration policy determines when the entry's age is reset. + * + *

This method is scheduled for removal in version 3.0.0. + * + * @param duration the length of time after which an entry should be automatically removed + * @param unit the unit that {@code duration} is expressed in + * @throws IllegalArgumentException if {@code duration} is negative + */ + void setExpiresAfter(long duration, TimeUnit unit); + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed + * duration has elapsed. The expiration policy determines when the entry's age is reset. + * + * @param duration the length of time after which an entry should be automatically removed + * @throws IllegalArgumentException if {@code duration} is negative + */ + default void setExpiresAfter(Duration duration) { + // This method will be abstract in version 3.0.0 + setExpiresAfter(duration.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Returns an unmodifiable snapshot {@link Map} view of the cache with ordered traversal. The + * order of iteration is from the entries most likely to expire (oldest) to the entries least + * likely to expire (youngest). This order is determined by the expiration policy's best guess + * at the time of creating this snapshot view. + * + *

Beware that obtaining the mappings is NOT a constant-time operation. Because of + * the asynchronous nature of the page replacement policy, determining the retention ordering + * requires a traversal of the entries. + * + * @param limit the maximum size of the returned map (use {@link Integer#MAX_VALUE} to disregard + * the limit) + * @return a snapshot view of the cache from oldest entry to the youngest + */ + Map oldest(int limit); + + /** + * Returns an unmodifiable snapshot {@link Map} view of the cache with ordered traversal. The + * order of iteration is from the entries least likely to expire (youngest) to the entries most + * likely to expire (oldest). This order is determined by the expiration policy's best guess at + * the time of creating this snapshot view. + * + *

Beware that obtaining the mappings is NOT a constant-time operation. Because of + * the asynchronous nature of the page replacement policy, determining the retention ordering + * requires a traversal of the entries. + * + * @param limit the maximum size of the returned map (use {@link Integer#MAX_VALUE} to disregard + * the limit) + * @return a snapshot view of the cache from youngest entry to the oldest + */ + Map youngest(int limit); + } + + /** The low-level operations for a cache with a variable expiration policy. */ + interface VarExpiration { + + /** + * Returns the duration until the entry should be automatically removed. The expiration policy + * determines when the entry's duration is reset. + * + *

This method is scheduled for removal in version 3.0.0. + * + * @param key the key for the entry being queried + * @param unit the unit that {@code age} is expressed in + * @return the duration if the entry is present in the cache + */ + OptionalLong getExpiresAfter(K key, TimeUnit unit); + + /** + * Returns the duration until the entry should be automatically removed. The expiration policy + * determines when the entry's duration is reset. + * + * @param key the key for the entry being queried + * @return the duration if the entry is present in the cache + */ + default Optional getExpiresAfter(K key) { + // This method will be abstract in version 3.0.0 + OptionalLong duration = getExpiresAfter(key, TimeUnit.NANOSECONDS); + return duration.isPresent() + ? Optional.of(Duration.ofNanos(duration.getAsLong())) + : Optional.empty(); + } + + /** + * Specifies that the entry should be automatically removed from the cache once the duration has + * elapsed. The expiration policy determines when the entry's age is reset. + * + * @param key the key for the entry being set + * @param duration the length of time from now when the entry should be automatically removed + * @param unit the unit that {@code duration} is expressed in + * @throws IllegalArgumentException if {@code duration} is negative + * @throws NullPointerException if the unit is null + */ + void setExpiresAfter(K key, long duration, TimeUnit unit); + + /** + * Specifies that the entry should be automatically removed from the cache once the duration has + * elapsed. The expiration policy determines when the entry's age is reset. + * + * @param key the key for the entry being set + * @param duration the length of time from now when the entry should be automatically removed + * @throws IllegalArgumentException if {@code duration} is negative + */ + default void setExpiresAfter(K key, Duration duration) { + // This method will be abstract in version 3.0.0 + setExpiresAfter(key, duration.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Associates the {@code value} with the {@code key} in this cache if the specified key is not + * already associated with a value. This method differs from {@link Map#putIfAbsent} by + * substituting the configured {@link Expiry} with the specified write duration, has no effect + * on the duration if the entry was present, and returns the success rather than a value. + * + *

This method is scheduled for removal in version 3.0.0. + * + * @param key the key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @param duration the length of time from now when the entry should be automatically removed + * @param unit the unit that {@code duration} is expressed in + * @return {@code true} if this cache did not already contain the specified entry + * @throws IllegalArgumentException if {@code duration} is negative + */ + default boolean putIfAbsent(K key, V value, long duration, TimeUnit unit) { + // This method was added & implemented in version 2.6.0 + throw new UnsupportedOperationException(); + } + + /** + * Associates the {@code value} with the {@code key} in this cache if the specified key is not + * already associated with a value. This method differs from {@link Map#putIfAbsent} by + * substituting the configured {@link Expiry} with the specified write duration, has no effect + * on the duration if the entry was present, and returns the success rather than a value. + * + * @param key the key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @param duration the length of time from now when the entry should be automatically removed + * @return {@code true} if this cache did not already contain the specified entry + * @throws IllegalArgumentException if {@code duration} is negative + */ + default boolean putIfAbsent(K key, V value, Duration duration) { + // This method will be abstract in version 3.0.0 + return putIfAbsent(key, value, duration.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Associates the {@code value} with the {@code key} in this cache. If the cache previously + * contained a value associated with the {@code key}, the old value is replaced by the new + * {@code value}. This method differs from {@link Cache#put} by substituting the configured + * {@link Expiry} with the specified write duration. + * + *

This method is scheduled for removal in version 3.0.0. + * + * @param key the key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @param duration the length of time from now when the entry should be automatically removed + * @param unit the unit that {@code duration} is expressed in + * @throws IllegalArgumentException if {@code duration} is negative + * @throws NullPointerException if the specified key or value is null + */ + default void put(K key, V value, long duration, TimeUnit unit) { + // This method was added & implemented in version 2.6.0 + throw new UnsupportedOperationException(); + } + + /** + * Associates the {@code value} with the {@code key} in this cache. If the cache previously + * contained a value associated with the {@code key}, the old value is replaced by the new + * {@code value}. This method differs from {@link Cache#put} by substituting the configured + * {@link Expiry} with the specified write duration. + * + * @param key the key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @param duration the length of time from now when the entry should be automatically removed + * @throws IllegalArgumentException if {@code duration} is negative + */ + default void put(K key, V value, Duration duration) { + // This method will be abstract in version 3.0.0 + put(key, value, duration.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Returns an unmodifiable snapshot {@link Map} view of the cache with ordered traversal. The + * order of iteration is from the entries most likely to expire (oldest) to the entries least + * likely to expire (youngest). This order is determined by the expiration policy's best guess + * at the time of creating this snapshot view. + * + *

Beware that obtaining the mappings is NOT a constant-time operation. Because of + * the asynchronous nature of the page replacement policy, determining the retention ordering + * requires a traversal of the entries. + * + * @param limit the maximum size of the returned map (use {@link Integer#MAX_VALUE} to disregard + * the limit) + * @return a snapshot view of the cache from oldest entry to the youngest + */ + Map oldest(int limit); + + /** + * Returns an unmodifiable snapshot {@link Map} view of the cache with ordered traversal. The + * order of iteration is from the entries least likely to expire (youngest) to the entries most + * likely to expire (oldest). This order is determined by the expiration policy's best guess at + * the time of creating this snapshot view. + * + *

Beware that obtaining the mappings is NOT a constant-time operation. Because of + * the asynchronous nature of the page replacement policy, determining the retention ordering + * requires a traversal of the entries. + * + * @param limit the maximum size of the returned map (use {@link Integer#MAX_VALUE} to disregard + * the limit) + * @return a snapshot view of the cache from youngest entry to the oldest + */ + Map youngest(int limit); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/References.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/References.java new file mode 100644 index 0000000000000..e9931048da8c9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/References.java @@ -0,0 +1,210 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; + +import static java.util.Objects.requireNonNull; + +/** + * Static utility methods and classes pertaining to weak and soft references. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass") +final class References { + + private References() {} + + /** A weak or soft reference that includes the entry's key reference. */ + interface InternalReference { + + /** + * Returns this reference object's referent. If this reference object has been cleared, either + * by the program or by the garbage collector, then this method returns {@code null}. + * + * @return The object to which this reference refers, or {@code null} if this reference object + * has been cleared + */ + E get(); + + /** + * Returns the key that is associated to the cache entry holding this reference. If the cache + * holds keys strongly, this is that key instance. Otherwise the cache holds keys weakly and the + * {@link WeakKeyReference} is returned. + * + * @return the key that is associated to the cached entry + */ + Object getKeyReference(); + + /** + * Returns {@code true} if the arguments is an {@linkplain InternalReference} that holds the + * same element. A weakly or softly held element is compared using identity equality. + * + * @param object the reference object with which to compare + * @return {@code true} if this object is the same as the argument; {@code false} otherwise + */ + default boolean referenceEquals(Object object) { + if (object == this) { + return true; + } else if (object instanceof InternalReference) { + InternalReference referent = (InternalReference) object; + return (get() == referent.get()); + } + return false; + } + } + + /** + * A short-lived adapter used for looking up an entry in the cache where the keys are weakly held. + * This {@linkplain InternalReference} implementation is not suitable for storing in the cache as + * the key is strongly held. + */ + static final class LookupKeyReference implements InternalReference { + private final int hashCode; + private final E e; + + public LookupKeyReference(E e) { + this.hashCode = System.identityHashCode(e); + this.e = requireNonNull(e); + } + + @Override + public E get() { + return e; + } + + @Override + public Object getKeyReference() { + return this; + } + + @Override + public boolean equals(Object object) { + return referenceEquals(object); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + /** + * The key in a cache that holds keys weakly. This class retains the key's identity hash code in + * the advent that the key is reclaimed so that the entry can be removed from the cache in + * constant time. + */ + static class WeakKeyReference extends WeakReference implements InternalReference { + private final int hashCode; + + public WeakKeyReference(K key, ReferenceQueue queue) { + super(key, queue); + hashCode = System.identityHashCode(key); + } + + @Override + public Object getKeyReference() { + return this; + } + + @Override + public boolean equals(Object object) { + return referenceEquals(object); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + /** + * The value in a cache that holds values weakly. This class retains a reference to the key in the + * advent that the value is reclaimed so that the entry can be removed from the cache in constant + * time. + */ + static final class WeakValueReference extends WeakReference + implements InternalReference { + private Object keyReference; + + public WeakValueReference(Object keyReference, V value, ReferenceQueue queue) { + super(value, queue); + this.keyReference = keyReference; + } + + @Override + public Object getKeyReference() { + return keyReference; + } + + public void setKeyReference(Object keyReference) { + this.keyReference = keyReference; + } + + @Override + public boolean equals(Object object) { + return referenceEquals(object); + } + + @Override + @SuppressWarnings("PMD.UselessOverridingMethod") + public int hashCode() { + return super.hashCode(); + } + } + + /** + * The value in a cache that holds values softly. This class retains a reference to the key in the + * advent that the value is reclaimed so that the entry can be removed from the cache in constant + * time. + */ + static final class SoftValueReference extends SoftReference + implements InternalReference { + private Object keyReference; + + public SoftValueReference(Object keyReference, V value, ReferenceQueue queue) { + super(value, queue); + this.keyReference = keyReference; + } + + @Override + public Object getKeyReference() { + return keyReference; + } + + public void setKeyReference(Object keyReference) { + this.keyReference = keyReference; + } + + @Override + public boolean equals(Object object) { + return referenceEquals(object); + } + + @Override + @SuppressWarnings("PMD.UselessOverridingMethod") + public int hashCode() { + return super.hashCode(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/RemovalCause.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/RemovalCause.java new file mode 100644 index 0000000000000..9b45608a278f9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/RemovalCause.java @@ -0,0 +1,128 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * The reason why a cached entry was removed. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public enum RemovalCause { + + /** + * The entry was manually removed by the user. This can result from the user invoking any of the + * following methods on the cache or map view. + * + *

    + *
  • {@link Cache#invalidate} + *
  • {@link Cache#invalidateAll(Iterable)} + *
  • {@link Cache#invalidateAll()} + *
  • {@link java.util.Map#remove} + *
  • {@link java.util.Map#computeIfPresent} + *
  • {@link java.util.Map#compute} + *
  • {@link java.util.Map#merge} + *
  • {@link java.util.concurrent.ConcurrentMap#remove} + *
+ * + * A manual removal may also be performed through the key, value, or entry collections views by + * the user invoking any of the following methods. + * + *
    + *
  • {@link java.util.Collection#remove} + *
  • {@link java.util.Collection#removeAll} + *
  • {@link java.util.Collection#removeIf} + *
  • {@link java.util.Collection#retainAll} + *
  • {@link java.util.Iterator#remove} + *
+ */ + EXPLICIT { + @Override + public boolean wasEvicted() { + return false; + } + }, + + /** + * The entry itself was not actually removed, but its value was replaced by the user. This can + * result from the user invoking any of the following methods on the cache or map view. + * + *
    + *
  • {@link Cache#put} + *
  • {@link Cache#putAll} + *
  • {@link LoadingCache#getAll} + *
  • {@link LoadingCache#refresh} + *
  • {@link java.util.Map#put} + *
  • {@link java.util.Map#putAll} + *
  • {@link java.util.Map#replace} + *
  • {@link java.util.Map#computeIfPresent} + *
  • {@link java.util.Map#compute} + *
  • {@link java.util.Map#merge} + *
+ */ + REPLACED { + @Override + public boolean wasEvicted() { + return false; + } + }, + + /** + * The entry was removed automatically because its key or value was garbage-collected. This can + * occur when using {@link Caffeine#weakKeys}, {@link Caffeine#weakValues}, or {@link + * Caffeine#softValues}. + */ + COLLECTED { + @Override + public boolean wasEvicted() { + return true; + } + }, + + /** + * The entry's expiration timestamp has passed. This can occur when using {@link + * Caffeine#expireAfterWrite}, {@link Caffeine#expireAfterAccess}, or {@link + * Caffeine#expireAfter(Expiry)}. + */ + EXPIRED { + @Override + public boolean wasEvicted() { + return true; + } + }, + + /** + * The entry was evicted due to size constraints. This can occur when using {@link + * Caffeine#maximumSize} or {@link Caffeine#maximumWeight}. + */ + SIZE { + @Override + public boolean wasEvicted() { + return true; + } + }; + + /** + * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither + * {@link #EXPLICIT} nor {@link #REPLACED}). + * + * @return if the entry was automatically removed due to eviction + */ + public abstract boolean wasEvicted(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/RemovalListener.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/RemovalListener.java new file mode 100644 index 0000000000000..b55272f7e215c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/RemovalListener.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * An object that can receive a notification when an entry is removed from a cache. The removal + * resulting in notification could have occurred to an entry being manually removed or replaced, or + * due to eviction resulting from timed expiration, exceeding a maximum size, or garbage collection. + * + *

An instance may be called concurrently by multiple threads to process different entries. + * Implementations of this interface should avoid performing blocking calls or synchronizing on + * shared resources. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the most general type of keys this listener can listen for; for example {@code Object} + * if any key is acceptable + * @param the most general type of values this listener can listen for; for example {@code + * Object} if any value is acceptable + */ +@FunctionalInterface +public interface RemovalListener { + + /** + * Notifies the listener that a removal occurred at some point in the past. + * + *

This does not always signify that the key is now absent from the cache, as it may have + * already been re-added. + * + * @param key the key represented by this entry, or {@code null} if collected + * @param value the value represented by this entry, or {@code null} if collected + * @param cause the reason for which the entry was removed + */ + void onRemoval(K key, V value, RemovalCause cause); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SI.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SI.java new file mode 100644 index 0000000000000..19c996fbea09a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SI.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • StrongKeys + *
  • InfirmValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SI extends BoundedLocalCache { + final ReferenceQueue valueReferenceQueue = new ReferenceQueue(); + + SI(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, (CacheLoader) cacheLoader, async); + } + + protected final ReferenceQueue valueReferenceQueue() { + return valueReferenceQueue; + } + + protected final boolean collectValues() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIA.java new file mode 100644 index 0000000000000..f5a0333cd156c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIA.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIA extends SI { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SIA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAR.java new file mode 100644 index 0000000000000..97bf83dd581a6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIAR extends SIA { + volatile long refreshAfterWriteNanos; + + SIAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAW.java new file mode 100644 index 0000000000000..72a4e99d50e53 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAW.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIAW extends SIA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SIAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAWR.java new file mode 100644 index 0000000000000..7aa812845998d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIAWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIAWR extends SIAW { + volatile long refreshAfterWriteNanos; + + SIAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIL.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIL.java new file mode 100644 index 0000000000000..4ee4acb1be9d0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIL.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Listening + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIL extends SI { + final RemovalListener removalListener; + + SIL(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.removalListener = builder.getRemovalListener(async); + } + + public final RemovalListener removalListener() { + return removalListener; + } + + public final boolean hasRemovalListener() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILA.java new file mode 100644 index 0000000000000..c7d68f7ab3d68 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILA extends SIL { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SILA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAR.java new file mode 100644 index 0000000000000..fce4fb0e6aa93 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILAR extends SILA { + volatile long refreshAfterWriteNanos; + + SILAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAW.java new file mode 100644 index 0000000000000..e63c5b4182cd3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILAW extends SILA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SILAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAWR.java new file mode 100644 index 0000000000000..8cc973b9c55f5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILAWR extends SILAW { + volatile long refreshAfterWriteNanos; + + SILAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMS.java new file mode 100644 index 0000000000000..81db59e6a6290 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMS.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMS extends SIL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SILMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSA.java new file mode 100644 index 0000000000000..3b80cf2910088 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMSA extends SILMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SILMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAR.java new file mode 100644 index 0000000000000..082ebbe255d88 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMSAR extends SILMSA { + volatile long refreshAfterWriteNanos; + + SILMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAW.java new file mode 100644 index 0000000000000..5cb9e45543016 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMSAW extends SILMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SILMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAWR.java new file mode 100644 index 0000000000000..18e475033e7ad --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMSAWR extends SILMSAW { + volatile long refreshAfterWriteNanos; + + SILMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSR.java new file mode 100644 index 0000000000000..298fe7ebd0b56 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMSR extends SILMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SILMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSW.java new file mode 100644 index 0000000000000..d2b9d34915396 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMSW extends SILMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SILMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSWR.java new file mode 100644 index 0000000000000..8c844a0b1be06 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMSWR extends SILMSW { + volatile long refreshAfterWriteNanos; + + SILMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMW.java new file mode 100644 index 0000000000000..d5108f3f66860 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMW.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMW extends SIL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SILMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWA.java new file mode 100644 index 0000000000000..cd1499282e3fb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMWA extends SILMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SILMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAR.java new file mode 100644 index 0000000000000..99d88105c3799 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMWAR extends SILMWA { + volatile long refreshAfterWriteNanos; + + SILMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAW.java new file mode 100644 index 0000000000000..4d6930268119d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMWAW extends SILMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SILMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAWR.java new file mode 100644 index 0000000000000..2ca46435fec10 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMWAWR extends SILMWAW { + volatile long refreshAfterWriteNanos; + + SILMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWR.java new file mode 100644 index 0000000000000..fbf3f5de85af5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMWR extends SILMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SILMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWW.java new file mode 100644 index 0000000000000..3cc45604537ba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILMWW extends SILMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SILMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWWR.java new file mode 100644 index 0000000000000..24254898c9762 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILMWWR extends SILMWW { + volatile long refreshAfterWriteNanos; + + SILMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILR.java new file mode 100644 index 0000000000000..84291b25abfad --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILR extends SIL { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SILR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILS.java new file mode 100644 index 0000000000000..14bd11c57af15 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILS.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILS extends SIL { + final StatsCounter statsCounter; + + SILS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSA.java new file mode 100644 index 0000000000000..ae51f13feacf6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSA.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSA extends SILS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SILSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAR.java new file mode 100644 index 0000000000000..0c8e7e6592fe7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSAR extends SILSA { + volatile long refreshAfterWriteNanos; + + SILSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAW.java new file mode 100644 index 0000000000000..f2ca88bece6ac --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSAW extends SILSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SILSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAWR.java new file mode 100644 index 0000000000000..9814e440509ef --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSAWR extends SILSAW { + volatile long refreshAfterWriteNanos; + + SILSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMS.java new file mode 100644 index 0000000000000..8045f89ddc440 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMS.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMS extends SILS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SILSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSA.java new file mode 100644 index 0000000000000..49afa9a5c413f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSA.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMSA extends SILSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SILSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAR.java new file mode 100644 index 0000000000000..dd81da949d3a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMSAR extends SILSMSA { + volatile long refreshAfterWriteNanos; + + SILSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAW.java new file mode 100644 index 0000000000000..521a52e0844a0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMSAW extends SILSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SILSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAWR.java new file mode 100644 index 0000000000000..1690e625083a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMSAWR extends SILSMSAW { + volatile long refreshAfterWriteNanos; + + SILSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSR.java new file mode 100644 index 0000000000000..629a18bd97658 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMSR extends SILSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SILSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSW.java new file mode 100644 index 0000000000000..96278eece907e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMSW extends SILSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SILSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSWR.java new file mode 100644 index 0000000000000..eaa6c37b68db7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMSWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMSWR extends SILSMSW { + volatile long refreshAfterWriteNanos; + + SILSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMW.java new file mode 100644 index 0000000000000..dfbb0b24dfccd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMW.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMW extends SILS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SILSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWA.java new file mode 100644 index 0000000000000..60ea60bf5765d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWA.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMWA extends SILSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SILSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAR.java new file mode 100644 index 0000000000000..7413da6808342 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMWAR extends SILSMWA { + volatile long refreshAfterWriteNanos; + + SILSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAW.java new file mode 100644 index 0000000000000..c3fa0a738c0c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMWAW extends SILSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SILSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAWR.java new file mode 100644 index 0000000000000..af93a12994f04 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMWAWR extends SILSMWAW { + volatile long refreshAfterWriteNanos; + + SILSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWR.java new file mode 100644 index 0000000000000..c754922cde5c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMWR extends SILSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SILSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWW.java new file mode 100644 index 0000000000000..21a8a69d9c0d5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSMWW extends SILSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SILSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWWR.java new file mode 100644 index 0000000000000..2582d325d2c35 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSMWWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSMWWR extends SILSMWW { + volatile long refreshAfterWriteNanos; + + SILSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSR.java new file mode 100644 index 0000000000000..a33d84432f025 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSR extends SILS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SILSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSW.java new file mode 100644 index 0000000000000..3a82e994814b3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILSW extends SILS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SILSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSWR.java new file mode 100644 index 0000000000000..00efd6925cbe3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILSWR extends SILSW { + volatile long refreshAfterWriteNanos; + + SILSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILW.java new file mode 100644 index 0000000000000..3e16820889c64 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SILW extends SIL { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SILW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILWR.java new file mode 100644 index 0000000000000..347fedcab095f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SILWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SILWR extends SILW { + volatile long refreshAfterWriteNanos; + + SILWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMS.java new file mode 100644 index 0000000000000..a91e23fade88e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMS.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMS extends SI { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SIMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSA.java new file mode 100644 index 0000000000000..ef218daa789cc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSA.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMSA extends SIMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SIMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAR.java new file mode 100644 index 0000000000000..df176332330b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMSAR extends SIMSA { + volatile long refreshAfterWriteNanos; + + SIMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAW.java new file mode 100644 index 0000000000000..1a65ae41b53a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMSAW extends SIMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SIMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAWR.java new file mode 100644 index 0000000000000..aff315bad2f72 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMSAWR extends SIMSAW { + volatile long refreshAfterWriteNanos; + + SIMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSR.java new file mode 100644 index 0000000000000..95c67d839ddbd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMSR extends SIMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SIMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSW.java new file mode 100644 index 0000000000000..f050d765bf44c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMSW extends SIMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SIMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSWR.java new file mode 100644 index 0000000000000..efc4d5bfb7027 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMSWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMSWR extends SIMSW { + volatile long refreshAfterWriteNanos; + + SIMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMW.java new file mode 100644 index 0000000000000..821e5ac20cc65 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMW.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMW extends SI { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SIMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWA.java new file mode 100644 index 0000000000000..61b5beea9fd38 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWA.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMWA extends SIMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SIMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAR.java new file mode 100644 index 0000000000000..6220cab9eaf69 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMWAR extends SIMWA { + volatile long refreshAfterWriteNanos; + + SIMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAW.java new file mode 100644 index 0000000000000..0ff48c45669f7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMWAW extends SIMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SIMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAWR.java new file mode 100644 index 0000000000000..2c0ee4c77ee49 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMWAWR extends SIMWAW { + volatile long refreshAfterWriteNanos; + + SIMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWR.java new file mode 100644 index 0000000000000..81e6bd0ca0165 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMWR extends SIMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SIMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWW.java new file mode 100644 index 0000000000000..7f09a6aca6cd2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIMWW extends SIMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SIMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWWR.java new file mode 100644 index 0000000000000..d0001aa21cd91 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIMWWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIMWWR extends SIMWW { + volatile long refreshAfterWriteNanos; + + SIMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIR.java new file mode 100644 index 0000000000000..4b40f9d75c9d8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIR.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIR extends SI { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SIR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIS.java new file mode 100644 index 0000000000000..65167735136f8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIS.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIS extends SI { + final StatsCounter statsCounter; + + SIS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISA.java new file mode 100644 index 0000000000000..b7a5a234fe7de --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISA extends SIS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SISA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAR.java new file mode 100644 index 0000000000000..fa1045b47ea7c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISAR extends SISA { + volatile long refreshAfterWriteNanos; + + SISAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAW.java new file mode 100644 index 0000000000000..9e1ac7d7db4f8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISAW extends SISA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SISAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAWR.java new file mode 100644 index 0000000000000..70a7915384148 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISAWR extends SISAW { + volatile long refreshAfterWriteNanos; + + SISAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMS.java new file mode 100644 index 0000000000000..c3ea555120ca1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMS.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMS extends SIS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SISMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSA.java new file mode 100644 index 0000000000000..cc9a723f7dce1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMSA extends SISMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SISMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAR.java new file mode 100644 index 0000000000000..6be5d5a9d353b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMSAR extends SISMSA { + volatile long refreshAfterWriteNanos; + + SISMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAW.java new file mode 100644 index 0000000000000..2c7f5fe0849cd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMSAW extends SISMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SISMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAWR.java new file mode 100644 index 0000000000000..dc7c8250c5e9f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMSAWR extends SISMSAW { + volatile long refreshAfterWriteNanos; + + SISMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSR.java new file mode 100644 index 0000000000000..6a3483d922880 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMSR extends SISMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SISMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSW.java new file mode 100644 index 0000000000000..50e5040f6d50a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMSW extends SISMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SISMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSWR.java new file mode 100644 index 0000000000000..c822ae03a3649 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMSWR extends SISMSW { + volatile long refreshAfterWriteNanos; + + SISMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMW.java new file mode 100644 index 0000000000000..e46217fadda41 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMW.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMW extends SIS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SISMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWA.java new file mode 100644 index 0000000000000..b9d918dd914e0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMWA extends SISMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SISMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAR.java new file mode 100644 index 0000000000000..b1bd4034a6a12 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMWAR extends SISMWA { + volatile long refreshAfterWriteNanos; + + SISMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAW.java new file mode 100644 index 0000000000000..f8122d5321234 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMWAW extends SISMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SISMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAWR.java new file mode 100644 index 0000000000000..c5e53a1c20f93 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMWAWR extends SISMWAW { + volatile long refreshAfterWriteNanos; + + SISMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWR.java new file mode 100644 index 0000000000000..094f91f77981c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMWR extends SISMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SISMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWW.java new file mode 100644 index 0000000000000..2fd360feed133 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISMWW extends SISMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SISMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWWR.java new file mode 100644 index 0000000000000..6aa439d1f33d5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISMWWR extends SISMWW { + volatile long refreshAfterWriteNanos; + + SISMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISR.java new file mode 100644 index 0000000000000..d10bb257a6b67 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISR extends SIS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SISR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISW.java new file mode 100644 index 0000000000000..3a4b864728323 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SISW extends SIS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SISW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISWR.java new file mode 100644 index 0000000000000..4ddcd9a1f629a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SISWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SISWR extends SISW { + volatile long refreshAfterWriteNanos; + + SISWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIW.java new file mode 100644 index 0000000000000..f05bb033f2b50 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIW.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SIW extends SI { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SIW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIWR.java new file mode 100644 index 0000000000000..ab0bc997d6656 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SIWR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SIWR extends SIW { + volatile long refreshAfterWriteNanos; + + SIWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SS.java new file mode 100644 index 0000000000000..d77e9de031710 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SS.java @@ -0,0 +1,39 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • StrongKeys + *
  • StrongValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SS extends BoundedLocalCache { + SS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, (CacheLoader) cacheLoader, async); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSA.java new file mode 100644 index 0000000000000..10c94d430e5d7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSA.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSA extends SS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAR.java new file mode 100644 index 0000000000000..a3bfd5934beea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSAR extends SSA { + volatile long refreshAfterWriteNanos; + + SSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAW.java new file mode 100644 index 0000000000000..0e5d18a5ef967 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAW.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSAW extends SSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAWR.java new file mode 100644 index 0000000000000..46a1828a080fd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSAWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSAWR extends SSAW { + volatile long refreshAfterWriteNanos; + + SSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSL.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSL.java new file mode 100644 index 0000000000000..9cc9a3e0670e3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSL.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Listening + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSL extends SS { + final RemovalListener removalListener; + + SSL(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.removalListener = builder.getRemovalListener(async); + } + + public final RemovalListener removalListener() { + return removalListener; + } + + public final boolean hasRemovalListener() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLA.java new file mode 100644 index 0000000000000..5df487970a85d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLA extends SSL { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSLA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAR.java new file mode 100644 index 0000000000000..b64630a9059ef --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLAR extends SSLA { + volatile long refreshAfterWriteNanos; + + SSLAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAW.java new file mode 100644 index 0000000000000..922b1845e6720 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLAW extends SSLA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSLAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAWR.java new file mode 100644 index 0000000000000..981021915227d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLAWR extends SSLAW { + volatile long refreshAfterWriteNanos; + + SSLAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMS.java new file mode 100644 index 0000000000000..3561c7351b4b0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMS.java @@ -0,0 +1,194 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMS extends SSL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSLMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSA.java new file mode 100644 index 0000000000000..3f62ff7676764 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSA.java @@ -0,0 +1,96 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMSA extends SSLMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSLMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAR.java new file mode 100644 index 0000000000000..ebeff435234ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMSAR extends SSLMSA { + volatile long refreshAfterWriteNanos; + + SSLMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAW.java new file mode 100644 index 0000000000000..ca82fc62dfff0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMSAW extends SSLMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSLMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAWR.java new file mode 100644 index 0000000000000..2bb6d3ccfcc1a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMSAWR extends SSLMSAW { + volatile long refreshAfterWriteNanos; + + SSLMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSR.java new file mode 100644 index 0000000000000..f6114fbeac585 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMSR extends SSLMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSLMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSW.java new file mode 100644 index 0000000000000..687bc29ac6161 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMSW extends SSLMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSLMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSWR.java new file mode 100644 index 0000000000000..055d0fc89a173 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMSWR extends SSLMSW { + volatile long refreshAfterWriteNanos; + + SSLMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMW.java new file mode 100644 index 0000000000000..ef27dfd2d5941 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMW.java @@ -0,0 +1,194 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMW extends SSL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSLMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWA.java new file mode 100644 index 0000000000000..9769a23f70174 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWA.java @@ -0,0 +1,96 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMWA extends SSLMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSLMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAR.java new file mode 100644 index 0000000000000..f3690a5b90104 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMWAR extends SSLMWA { + volatile long refreshAfterWriteNanos; + + SSLMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAW.java new file mode 100644 index 0000000000000..e5200f8d1a8cd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMWAW extends SSLMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSLMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAWR.java new file mode 100644 index 0000000000000..1f20e95f816ee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMWAWR extends SSLMWAW { + volatile long refreshAfterWriteNanos; + + SSLMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWR.java new file mode 100644 index 0000000000000..051134ffe4923 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMWR extends SSLMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSLMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWW.java new file mode 100644 index 0000000000000..58920527054f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLMWW extends SSLMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSLMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWWR.java new file mode 100644 index 0000000000000..a5a7ba5772c70 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLMWWR extends SSLMWW { + volatile long refreshAfterWriteNanos; + + SSLMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLR.java new file mode 100644 index 0000000000000..e2c050077e98e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLR extends SSL { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSLR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLS.java new file mode 100644 index 0000000000000..08a851451195b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLS.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLS extends SSL { + final StatsCounter statsCounter; + + SSLS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSA.java new file mode 100644 index 0000000000000..883cd94def6d2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSA.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSA extends SSLS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSLSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAR.java new file mode 100644 index 0000000000000..4cedaf5e84de4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSAR extends SSLSA { + volatile long refreshAfterWriteNanos; + + SSLSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAW.java new file mode 100644 index 0000000000000..6e1673d83a7c5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSAW extends SSLSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSLSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAWR.java new file mode 100644 index 0000000000000..fe2c6337f5067 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSAWR extends SSLSAW { + volatile long refreshAfterWriteNanos; + + SSLSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMS.java new file mode 100644 index 0000000000000..dc422496273a2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMS.java @@ -0,0 +1,195 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMS extends SSLS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSLSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSA.java new file mode 100644 index 0000000000000..53935415854a4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSA.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMSA extends SSLSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSLSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAR.java new file mode 100644 index 0000000000000..424358c00d297 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMSAR extends SSLSMSA { + volatile long refreshAfterWriteNanos; + + SSLSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAW.java new file mode 100644 index 0000000000000..2c9a90da1e013 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMSAW extends SSLSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSLSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAWR.java new file mode 100644 index 0000000000000..659b04437c8bf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMSAWR extends SSLSMSAW { + volatile long refreshAfterWriteNanos; + + SSLSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSR.java new file mode 100644 index 0000000000000..b308fa0669ee2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMSR extends SSLSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSLSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSW.java new file mode 100644 index 0000000000000..268d5baf602bd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMSW extends SSLSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSLSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSWR.java new file mode 100644 index 0000000000000..68b59a03877be --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMSWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMSWR extends SSLSMSW { + volatile long refreshAfterWriteNanos; + + SSLSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMW.java new file mode 100644 index 0000000000000..363f942977242 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMW.java @@ -0,0 +1,195 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMW extends SSLS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSLSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWA.java new file mode 100644 index 0000000000000..d52fbe0b3e861 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWA.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMWA extends SSLSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSLSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAR.java new file mode 100644 index 0000000000000..20282db8409a9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMWAR extends SSLSMWA { + volatile long refreshAfterWriteNanos; + + SSLSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAW.java new file mode 100644 index 0000000000000..2df84a3d618d4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMWAW extends SSLSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSLSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAWR.java new file mode 100644 index 0000000000000..cbae0448fd424 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMWAWR extends SSLSMWAW { + volatile long refreshAfterWriteNanos; + + SSLSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWR.java new file mode 100644 index 0000000000000..cd72e7dabb7e5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMWR extends SSLSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSLSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWW.java new file mode 100644 index 0000000000000..2e622dbd1c514 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSMWW extends SSLSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSLSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWWR.java new file mode 100644 index 0000000000000..30ead35b66e3b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSMWWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSMWWR extends SSLSMWW { + volatile long refreshAfterWriteNanos; + + SSLSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSR.java new file mode 100644 index 0000000000000..9293592ff2b3d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSR extends SSLS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSLSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSW.java new file mode 100644 index 0000000000000..ef004031ee71b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLSW extends SSLS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSLSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSWR.java new file mode 100644 index 0000000000000..93523e238a40e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLSWR extends SSLSW { + volatile long refreshAfterWriteNanos; + + SSLSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLW.java new file mode 100644 index 0000000000000..1a8470bbc4287 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSLW extends SSL { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSLW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLWR.java new file mode 100644 index 0000000000000..dab7223235efe --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSLWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSLWR extends SSLW { + volatile long refreshAfterWriteNanos; + + SSLWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMS.java new file mode 100644 index 0000000000000..95c1e3ef36f76 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMS.java @@ -0,0 +1,193 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMS extends SS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSA.java new file mode 100644 index 0000000000000..4b38b1fc31b45 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSA.java @@ -0,0 +1,95 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMSA extends SSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAR.java new file mode 100644 index 0000000000000..e86711ac40e15 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMSAR extends SSMSA { + volatile long refreshAfterWriteNanos; + + SSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAW.java new file mode 100644 index 0000000000000..1b6bc6afb5ad4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMSAW extends SSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAWR.java new file mode 100644 index 0000000000000..522cc20e4d6c8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMSAWR extends SSMSAW { + volatile long refreshAfterWriteNanos; + + SSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSR.java new file mode 100644 index 0000000000000..37f54f8c1e79f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMSR extends SSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSW.java new file mode 100644 index 0000000000000..240d5c9abee67 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMSW extends SSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSWR.java new file mode 100644 index 0000000000000..85358d1938455 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMSWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMSWR extends SSMSW { + volatile long refreshAfterWriteNanos; + + SSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMW.java new file mode 100644 index 0000000000000..902c2af8641dd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMW.java @@ -0,0 +1,193 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMW extends SS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWA.java new file mode 100644 index 0000000000000..9a6a634d2f603 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWA.java @@ -0,0 +1,95 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMWA extends SSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAR.java new file mode 100644 index 0000000000000..0815321cc5681 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMWAR extends SSMWA { + volatile long refreshAfterWriteNanos; + + SSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAW.java new file mode 100644 index 0000000000000..6178b35af16a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMWAW extends SSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAWR.java new file mode 100644 index 0000000000000..f10f4b1356150 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMWAWR extends SSMWAW { + volatile long refreshAfterWriteNanos; + + SSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWR.java new file mode 100644 index 0000000000000..5d40b53a78a47 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMWR extends SSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWW.java new file mode 100644 index 0000000000000..4a976da7d51fa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSMWW extends SSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWWR.java new file mode 100644 index 0000000000000..6a0df00b1c482 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSMWWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSMWWR extends SSMWW { + volatile long refreshAfterWriteNanos; + + SSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSR.java new file mode 100644 index 0000000000000..e4646b4fc16ca --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSR.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSR extends SS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSS.java new file mode 100644 index 0000000000000..926f1ba47a217 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSS.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSS extends SS { + final StatsCounter statsCounter; + + SSS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSA.java new file mode 100644 index 0000000000000..3078b8469198f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSA extends SSS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAR.java new file mode 100644 index 0000000000000..b58e0512d1f1d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSAR extends SSSA { + volatile long refreshAfterWriteNanos; + + SSSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAW.java new file mode 100644 index 0000000000000..d6af6e57246e9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSAW extends SSSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAWR.java new file mode 100644 index 0000000000000..f5cf555b354d7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSAWR extends SSSAW { + volatile long refreshAfterWriteNanos; + + SSSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMS.java new file mode 100644 index 0000000000000..6b57c10acdd49 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMS.java @@ -0,0 +1,194 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMS extends SSS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSA.java new file mode 100644 index 0000000000000..b50e5edd27e27 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSA.java @@ -0,0 +1,96 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMSA extends SSSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAR.java new file mode 100644 index 0000000000000..62bc0d8af6549 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMSAR extends SSSMSA { + volatile long refreshAfterWriteNanos; + + SSSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAW.java new file mode 100644 index 0000000000000..215c97375a041 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMSAW extends SSSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAWR.java new file mode 100644 index 0000000000000..a0137788d8c3d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMSAWR extends SSSMSAW { + volatile long refreshAfterWriteNanos; + + SSSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSR.java new file mode 100644 index 0000000000000..96653b10da353 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMSR extends SSSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSW.java new file mode 100644 index 0000000000000..6e546fc8f86d3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMSW extends SSSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSWR.java new file mode 100644 index 0000000000000..a2a14d736406f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMSWR extends SSSMSW { + volatile long refreshAfterWriteNanos; + + SSSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMW.java new file mode 100644 index 0000000000000..d4b76937e4cb6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMW.java @@ -0,0 +1,194 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMW extends SSS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + SSSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected boolean fastpath() { + return true; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWA.java new file mode 100644 index 0000000000000..b2372025454b0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWA.java @@ -0,0 +1,96 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMWA extends SSSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + SSSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected boolean fastpath() { + return false; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAR.java new file mode 100644 index 0000000000000..e084a42be7082 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMWAR extends SSSMWA { + volatile long refreshAfterWriteNanos; + + SSSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAW.java new file mode 100644 index 0000000000000..96eaa2f47f94b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMWAW extends SSSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + SSSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAWR.java new file mode 100644 index 0000000000000..6108f7b1fb3ad --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMWAWR extends SSSMWAW { + volatile long refreshAfterWriteNanos; + + SSSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWR.java new file mode 100644 index 0000000000000..e22c1f6ec8761 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMWR extends SSSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWW.java new file mode 100644 index 0000000000000..7ff923e122e5a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSMWW extends SSSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWWR.java new file mode 100644 index 0000000000000..e470b236fac25 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSMWWR extends SSSMWW { + volatile long refreshAfterWriteNanos; + + SSSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSR.java new file mode 100644 index 0000000000000..c8b8ab891d577 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSR extends SSS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + SSSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSW.java new file mode 100644 index 0000000000000..2b08b34374cae --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSSW extends SSS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSWR.java new file mode 100644 index 0000000000000..8e0c44f53af93 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSSWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSSWR extends SSSW { + volatile long refreshAfterWriteNanos; + + SSSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSW.java new file mode 100644 index 0000000000000..f95793617be1a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSW.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class SSW extends SS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + SSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSWR.java new file mode 100644 index 0000000000000..c8396736176c8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SSWR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • StrongKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class SSWR extends SSW { + volatile long refreshAfterWriteNanos; + + SSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Scheduler.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Scheduler.java new file mode 100644 index 0000000000000..ce5513d50d2a9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Scheduler.java @@ -0,0 +1,230 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * A scheduler that submits a task to an executor after a given delay. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@FunctionalInterface +public interface Scheduler { + + /** + * Returns a future that will submit the task to the given executor after the given delay. + * + * @param executor the executor to run the task + * @param command the runnable task to schedule + * @param delay how long to delay, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the {@code delay} parameter + * @return a scheduled future representing pending submission of the task + */ + Future schedule(Executor executor, Runnable command, long delay, TimeUnit unit); + + /** + * Returns a scheduler that always returns a successfully completed future. + * + * @return a scheduler that always returns a successfully completed future + */ + static Scheduler disabledScheduler() { + return DisabledScheduler.INSTANCE; + } + + /** + * Returns a scheduler that uses the system-wide scheduling thread if available, or else returns + * {@link #disabledScheduler()} if not present. This scheduler is provided in Java 9 or above by + * using {@link CompletableFuture} {@code delayedExecutor}. + * + * @return a scheduler that uses the system-wide scheduling thread if available, or else a + * disabled scheduler + */ + static Scheduler systemScheduler() { + return SystemScheduler.isPresent() ? SystemScheduler.INSTANCE : disabledScheduler(); + } + + /** + * Returns a scheduler that delegates to the a {@link ScheduledExecutorService}. + * + * @param scheduledExecutorService the executor to schedule on + * @return a scheduler that delegates to the a {@link ScheduledExecutorService} + */ + static Scheduler forScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) { + return new ExecutorServiceScheduler(scheduledExecutorService); + } + + /** + * Returns a scheduler that suppresses and logs any exception thrown by the delegate {@code + * scheduler}. + * + * @param scheduler the scheduler to delegate to + * @return an scheduler that suppresses and logs any exception thrown by the delegate + */ + static Scheduler guardedScheduler(Scheduler scheduler) { + return (scheduler instanceof GuardedScheduler) ? scheduler : new GuardedScheduler(scheduler); + } +} + +enum SystemScheduler implements Scheduler { + INSTANCE; + + static final Method delayedExecutor = getDelayedExecutorMethod(); + + @Override + @SuppressWarnings("NullAway") + public Future schedule(Executor executor, Runnable command, long delay, TimeUnit unit) { + requireNonNull(executor); + requireNonNull(command); + requireNonNull(unit); + + try { + Executor scheduler = + (Executor) delayedExecutor.invoke(CompletableFuture.class, delay, unit, executor); + return CompletableFuture.runAsync(command, scheduler); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + static Method getDelayedExecutorMethod() { + try { + return CompletableFuture.class.getMethod( + "delayedExecutor", long.class, TimeUnit.class, Executor.class); + } catch (NoSuchMethodException | SecurityException e) { + return null; + } + } + + static boolean isPresent() { + return (delayedExecutor != null); + } +} + +final class ExecutorServiceScheduler implements Scheduler, Serializable { + static final Logger logger = Logger.getLogger(ExecutorServiceScheduler.class.getName()); + static final long serialVersionUID = 1; + + final ScheduledExecutorService scheduledExecutorService; + + ExecutorServiceScheduler(ScheduledExecutorService scheduledExecutorService) { + this.scheduledExecutorService = requireNonNull(scheduledExecutorService); + } + + @Override + public Future schedule(Executor executor, Runnable command, long delay, TimeUnit unit) { + requireNonNull(executor); + requireNonNull(command); + requireNonNull(unit); + + if (scheduledExecutorService.isShutdown()) { + return DisabledFuture.INSTANCE; + } + return scheduledExecutorService.schedule( + () -> { + try { + executor.execute(command); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown when submitting scheduled task", t); + throw t; + } + }, + delay, + unit); + } +} + +final class GuardedScheduler implements Scheduler, Serializable { + static final Logger logger = Logger.getLogger(GuardedScheduler.class.getName()); + static final long serialVersionUID = 1; + + final Scheduler delegate; + + GuardedScheduler(Scheduler delegate) { + this.delegate = requireNonNull(delegate); + } + + @Override + public Future schedule(Executor executor, Runnable command, long delay, TimeUnit unit) { + try { + Future future = delegate.schedule(executor, command, delay, unit); + return (future == null) ? DisabledFuture.INSTANCE : future; + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by scheduler; discarded task", t); + return DisabledFuture.INSTANCE; + } + } +} + +enum DisabledScheduler implements Scheduler { + INSTANCE; + + @Override + public Future schedule(Executor executor, Runnable command, long delay, TimeUnit unit) { + requireNonNull(executor); + requireNonNull(command); + requireNonNull(unit); + return DisabledFuture.INSTANCE; + } +} + +enum DisabledFuture implements Future { + INSTANCE; + + @Override + public boolean isDone() { + return true; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public Void get() throws InterruptedException, ExecutionException { + return null; + } + + @Override + public Void get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + requireNonNull(unit); + return null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SerializationProxy.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SerializationProxy.java new file mode 100644 index 0000000000000..3f35033cceae2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/SerializationProxy.java @@ -0,0 +1,128 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.CacheWriterAdapter; + +import java.io.Serializable; +import java.util.concurrent.TimeUnit; + +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.UNSET_INT; + +/** + * Serializes the configuration of the cache, reconsitituting it as a {@link Cache}, {@link + * LoadingCache}, or {@link AsyncLoadingCache} using {@link Caffeine} upon deserialization. The data + * held by the cache is not retained. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +final class SerializationProxy implements Serializable { + private static final long serialVersionUID = 1; + + boolean async; + boolean weakKeys; + boolean weakValues; + boolean softValues; + boolean isRecordingStats; + long refreshAfterWriteNanos; + long expiresAfterWriteNanos; + long expiresAfterAccessNanos; + long maximumSize = UNSET_INT; + long maximumWeight = UNSET_INT; + + Ticker ticker; + Expiry expiry; + Weigher weigher; + + @SuppressWarnings("deprecation") + CacheWriter writer; + + AsyncCacheLoader loader; + RemovalListener removalListener; + + @SuppressWarnings({"unchecked", "PreferJavaTimeOverload", "deprecation"}) + Caffeine recreateCaffeine() { + Caffeine builder = Caffeine.newBuilder(); + if (ticker != null) { + builder.ticker(ticker); + } + if (isRecordingStats) { + builder.recordStats(); + } + if (maximumSize != UNSET_INT) { + builder.maximumSize(maximumSize); + } + if (weigher != null) { + builder.maximumWeight(maximumWeight); + builder.weigher((Weigher) weigher); + } + if (expiry != null) { + builder.expireAfter(expiry); + } + if (expiresAfterWriteNanos > 0) { + builder.expireAfterWrite(expiresAfterWriteNanos, TimeUnit.NANOSECONDS); + } + if (expiresAfterAccessNanos > 0) { + builder.expireAfterAccess(expiresAfterAccessNanos, TimeUnit.NANOSECONDS); + } + if (refreshAfterWriteNanos > 0) { + builder.refreshAfterWrite(refreshAfterWriteNanos, TimeUnit.NANOSECONDS); + } + if (weakKeys) { + builder.weakKeys(); + } + if (weakValues) { + builder.weakValues(); + } + if (softValues) { + builder.softValues(); + } + if (removalListener != null) { + builder.removalListener((RemovalListener) removalListener); + } + if ((writer != null) && (writer != CacheWriter.disabledWriter())) { + if (writer instanceof CacheWriterAdapter) { + builder.evictionListener(((CacheWriterAdapter) writer).delegate); + } else { + builder.writer((CacheWriter) writer); + } + } + return builder; + } + + Object readResolve() { + Caffeine builder = recreateCaffeine(); + if (async) { + if (loader == null) { + return builder.buildAsync(); + } + @SuppressWarnings("unchecked") + AsyncCacheLoader cacheLoader = (AsyncCacheLoader) loader; + return builder.buildAsync(cacheLoader); + } + + if (loader == null) { + return builder.build(); + } + @SuppressWarnings("unchecked") + CacheLoader cacheLoader = (CacheLoader) loader; + return builder.build(cacheLoader); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/StripedBuffer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/StripedBuffer.java new file mode 100644 index 0000000000000..125df4cd49066 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/StripedBuffer.java @@ -0,0 +1,276 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; + +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.ceilingPowerOfTwo; + +/** + * A base class providing the mechanics for supporting dynamic striping of bounded buffers. This + * implementation is an adaption of the numeric 64-bit {@link java.util.concurrent.atomic.Striped64} + * class, which is used by atomic counters. The approach was modified to lazily grow an array of + * buffers in order to minimize memory usage for caches that are not heavily contended on. + * + * @author dl@cs.oswego.edu (Doug Lea) + * @author ben.manes@gmail.com (Ben Manes) + */ +abstract class StripedBuffer implements Buffer { + /* + * This class maintains a lazily-initialized table of atomically updated buffers. The table size + * is a power of two. Indexing uses masked per-thread hash codes. Nearly all declarations in this + * class are package-private, accessed directly by subclasses. + * + * Table entries are of class Buffer and should be padded to reduce cache contention. Padding is + * overkill for most atomics because they are usually irregularly scattered in memory and thus + * don't interfere much with each other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share cache lines (with a huge negative + * performance impact) without this precaution. + * + * In part because Buffers are relatively large, we avoid creating them until they are needed. + * When there is no contention, all updates are made to a single buffer. Upon contention (a failed + * CAS inserting into the buffer), the table is expanded to size 2. The table size is doubled upon + * further contention until reaching the nearest power of two greater than or equal to the number + * of CPUS. Table slots remain empty (null) until they are needed. + * + * A single spinlock ("tableBusy") is used for initializing and resizing the table, as well as + * populating slots with new Buffers. There is no need for a blocking lock; when the lock is not + * available, threads try other slots. During these retries, there is increased contention and + * reduced locality, which is still better than alternatives. + * + * The Thread probe fields maintained via ThreadLocalRandom serve as per-thread hash codes. We let + * them remain uninitialized as zero (if they come in this way) until they contend at slot 0. They + * are then initialized to values that typically do not often conflict with others. Contention + * and/or table collisions are indicated by failed CASes when performing an update operation. Upon + * a collision, if the table size is less than the capacity, it is doubled in size unless some + * other thread holds the lock. If a hashed slot is empty, and lock is available, a new Buffer is + * created. Otherwise, if the slot exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a free slot. + * + * The table size is capped because, when there are more threads than CPUs, supposing that each + * thread were bound to a CPU, there would exist a perfect hash function mapping threads to slots + * that eliminates collisions. When we reach capacity, we search for this mapping by randomly + * varying the hash codes of colliding threads. Because search is random, and collisions only + * become known via CAS failures, convergence can be slow, and because threads are typically not + * bound to CPUS forever, may not occur at all. However, despite these limitations, observed + * contention rates are typically low in these cases. + * + * It is possible for a Buffer to become unused when threads that once hashed to it terminate, as + * well as in the case where doubling the table causes no thread to hash to it under expanded + * mask. We do not try to detect or remove buffers, under the assumption that for long-running + * instances, observed contention levels will recur, so the buffers will eventually be needed + * again; and for short-lived ones, it does not matter. + */ + + static final long TABLE_BUSY = UnsafeAccess.objectFieldOffset(StripedBuffer.class, "tableBusy"); + static final long PROBE = UnsafeAccess.objectFieldOffset(Thread.class, "threadLocalRandomProbe"); + + /** Number of CPUS. */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** The bound on the table size. */ + static final int MAXIMUM_TABLE_SIZE = 4 * ceilingPowerOfTwo(NCPU); + + /** The maximum number of attempts when trying to expand the table. */ + static final int ATTEMPTS = 3; + + /** Table of buffers. When non-null, size is a power of 2. */ + transient volatile Buffer[] table; + + /** Spinlock (locked via CAS) used when resizing and/or creating Buffers. */ + transient volatile int tableBusy; + + /** CASes the tableBusy field from 0 to 1 to acquire lock. */ + final boolean casTableBusy() { + return UnsafeAccess.UNSAFE.compareAndSwapInt(this, TABLE_BUSY, 0, 1); + } + + /** + * Returns the probe value for the current thread. Duplicated from ThreadLocalRandom because of + * packaging restrictions. + */ + static final int getProbe() { + return UnsafeAccess.UNSAFE.getInt(Thread.currentThread(), PROBE); + } + + /** + * Pseudo-randomly advances and records the given probe value for the given thread. Duplicated + * from ThreadLocalRandom because of packaging restrictions. + */ + static final int advanceProbe(int probe) { + probe ^= probe << 13; // xorshift + probe ^= probe >>> 17; + probe ^= probe << 5; + UnsafeAccess.UNSAFE.putInt(Thread.currentThread(), PROBE, probe); + return probe; + } + + /** + * Creates a new buffer instance after resizing to accommodate a producer. + * + * @param e the producer's element + * @return a newly created buffer populated with a single element + */ + protected abstract Buffer create(E e); + + @Override + public int offer(E e) { + int mask; + int result = 0; + Buffer buffer; + boolean uncontended = true; + Buffer[] buffers = table; + if ((buffers == null) + || (mask = buffers.length - 1) < 0 + || (buffer = buffers[getProbe() & mask]) == null + || !(uncontended = ((result = buffer.offer(e)) != Buffer.FAILED))) { + expandOrRetry(e, uncontended); + } + return result; + } + + @Override + public void drainTo(Consumer consumer) { + Buffer[] buffers = table; + if (buffers == null) { + return; + } + for (Buffer buffer : buffers) { + if (buffer != null) { + buffer.drainTo(consumer); + } + } + } + + @Override + public int reads() { + Buffer[] buffers = table; + if (buffers == null) { + return 0; + } + int reads = 0; + for (Buffer buffer : buffers) { + if (buffer != null) { + reads += buffer.reads(); + } + } + return reads; + } + + @Override + public int writes() { + Buffer[] buffers = table; + if (buffers == null) { + return 0; + } + int writes = 0; + for (Buffer buffer : buffers) { + if (buffer != null) { + writes += buffer.writes(); + } + } + return writes; + } + + /** + * Handles cases of updates involving initialization, resizing, creating new Buffers, and/or + * contention. See above for explanation. This method suffers the usual non-modularity problems of + * optimistic retry code, relying on rechecked sets of reads. + * + * @param e the element to add + * @param wasUncontended false if CAS failed before call + */ + @SuppressWarnings("PMD.ConfusingTernary") + final void expandOrRetry(E e, boolean wasUncontended) { + int h; + if ((h = getProbe()) == 0) { + ThreadLocalRandom.current(); // force initialization + h = getProbe(); + wasUncontended = true; + } + boolean collide = false; // True if last slot nonempty + for (int attempt = 0; attempt < ATTEMPTS; attempt++) { + Buffer[] buffers; + Buffer buffer; + int n; + if (((buffers = table) != null) && ((n = buffers.length) > 0)) { + if ((buffer = buffers[(n - 1) & h]) == null) { + if ((tableBusy == 0) && casTableBusy()) { // Try to attach new Buffer + boolean created = false; + try { // Recheck under lock + Buffer[] rs; + int mask, j; + if (((rs = table) != null) + && ((mask = rs.length) > 0) + && (rs[j = (mask - 1) & h] == null)) { + rs[j] = create(e); + created = true; + } + } finally { + tableBusy = 0; + } + if (created) { + break; + } + continue; // Slot is now non-empty + } + collide = false; + } else if (!wasUncontended) { // CAS already known to fail + wasUncontended = true; // Continue after rehash + } else if (buffer.offer(e) != Buffer.FAILED) { + break; + } else if (n >= MAXIMUM_TABLE_SIZE || table != buffers) { + collide = false; // At max size or stale + } else if (!collide) { + collide = true; + } else if (tableBusy == 0 && casTableBusy()) { + try { + if (table == buffers) { // Expand table unless stale + table = Arrays.copyOf(buffers, n << 1); + } + } finally { + tableBusy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h = advanceProbe(h); + } else if ((tableBusy == 0) && (table == buffers) && casTableBusy()) { + boolean init = false; + try { // Initialize table + if (table == buffers) { + @SuppressWarnings({"unchecked", "rawtypes"}) + Buffer[] rs = new Buffer[1]; + rs[0] = create(e); + table = rs; + init = true; + } + } finally { + tableBusy = 0; + } + if (init) { + break; + } + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Ticker.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Ticker.java new file mode 100644 index 0000000000000..6705ba616ff17 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Ticker.java @@ -0,0 +1,72 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * A time source that returns a time value representing the number of nanoseconds elapsed since some + * fixed but arbitrary point in time. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public interface Ticker { + + /** + * Returns the number of nanoseconds elapsed since this ticker's fixed point of reference. + * + * @return the number of nanoseconds elapsed since this ticker's fixed point of reference + */ + long read(); + + /** + * Returns a ticker that reads the current time using {@link System#nanoTime}. + * + * @return a ticker that reads the current time using {@link System#nanoTime} + */ + static Ticker systemTicker() { + return SystemTicker.INSTANCE; + } + + /** + * Returns a ticker that always returns {@code 0}. + * + * @return a ticker that always returns {@code 0} + */ + static Ticker disabledTicker() { + return DisabledTicker.INSTANCE; + } +} + +enum SystemTicker implements Ticker { + INSTANCE; + + @Override + public long read() { + return System.nanoTime(); + } +} + +enum DisabledTicker implements Ticker { + INSTANCE; + + @Override + public long read() { + return 0L; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/TimerWheel.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/TimerWheel.java new file mode 100644 index 0000000000000..3930b2ce3e38d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/TimerWheel.java @@ -0,0 +1,434 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.ceilingPowerOfTwo; +import static org.apache.iotdb.github.benmanes.caffeine.cache.Caffeine.requireArgument; + +/** + * A hierarchical timer wheel to add, remove, and fire expiration events in amortized O(1) time. The + * expiration events are deferred until the timer is advanced, which is performed as part of the + * cache's maintenance cycle. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings("GuardedBy") +final class TimerWheel { + + /* + * A timer wheel [1] stores timer events in buckets on a circular buffer. A bucket represents a + * coarse time span, e.g. one minute, and holds a doubly-linked list of events. The wheels are + * structured in a hierarchy (seconds, minutes, hours, days) so that events scheduled in the + * distant future are cascaded to lower buckets when the wheels rotate. This allows for events + * to be added, removed, and expired in O(1) time, where expiration occurs for the entire bucket, + * and penalty of cascading is amortized by the rotations. + * + * [1] Hashed and Hierarchical Timing Wheels + * http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf + */ + + static final int[] BUCKETS = {64, 64, 32, 4, 1}; + static final long[] SPANS = { + ceilingPowerOfTwo(TimeUnit.SECONDS.toNanos(1)), // 1.07s + ceilingPowerOfTwo(TimeUnit.MINUTES.toNanos(1)), // 1.14m + ceilingPowerOfTwo(TimeUnit.HOURS.toNanos(1)), // 1.22h + ceilingPowerOfTwo(TimeUnit.DAYS.toNanos(1)), // 1.63d + BUCKETS[3] * ceilingPowerOfTwo(TimeUnit.DAYS.toNanos(1)), // 6.5d + BUCKETS[3] * ceilingPowerOfTwo(TimeUnit.DAYS.toNanos(1)), // 6.5d + }; + static final long[] SHIFT = { + Long.numberOfTrailingZeros(SPANS[0]), + Long.numberOfTrailingZeros(SPANS[1]), + Long.numberOfTrailingZeros(SPANS[2]), + Long.numberOfTrailingZeros(SPANS[3]), + Long.numberOfTrailingZeros(SPANS[4]), + }; + + final BoundedLocalCache cache; + final Node[][] wheel; + + long nanos; + + @SuppressWarnings({"rawtypes", "unchecked"}) + TimerWheel(BoundedLocalCache cache) { + this.cache = requireNonNull(cache); + + wheel = new Node[BUCKETS.length][1]; + for (int i = 0; i < wheel.length; i++) { + wheel[i] = new Node[BUCKETS[i]]; + for (int j = 0; j < wheel[i].length; j++) { + wheel[i][j] = new Sentinel<>(); + } + } + } + + /** + * Advances the timer and evicts entries that have expired. + * + * @param currentTimeNanos the current time, in nanoseconds + */ + public void advance(long currentTimeNanos) { + long previousTimeNanos = nanos; + try { + nanos = currentTimeNanos; + + // If wrapping, temporarily shift the clock for a positive comparison + if ((previousTimeNanos < 0) && (currentTimeNanos > 0)) { + previousTimeNanos += Long.MAX_VALUE; + currentTimeNanos += Long.MAX_VALUE; + } + + for (int i = 0; i < SHIFT.length; i++) { + long previousTicks = (previousTimeNanos >>> SHIFT[i]); + long currentTicks = (currentTimeNanos >>> SHIFT[i]); + if ((currentTicks - previousTicks) <= 0L) { + break; + } + expire(i, previousTicks, currentTicks); + } + } catch (Throwable t) { + nanos = previousTimeNanos; + throw t; + } + } + + /** + * Expires entries or reschedules into the proper bucket if still active. + * + * @param index the wheel being operated on + * @param previousTicks the previous number of ticks + * @param currentTicks the current number of ticks + */ + void expire(int index, long previousTicks, long currentTicks) { + Node[] timerWheel = wheel[index]; + int mask = timerWheel.length - 1; + + int steps = Math.min(1 + Math.abs((int) (currentTicks - previousTicks)), timerWheel.length); + int start = (int) (previousTicks & mask); + int end = start + steps; + + for (int i = start; i < end; i++) { + Node sentinel = timerWheel[i & mask]; + Node prev = sentinel.getPreviousInVariableOrder(); + Node node = sentinel.getNextInVariableOrder(); + sentinel.setPreviousInVariableOrder(sentinel); + sentinel.setNextInVariableOrder(sentinel); + + while (node != sentinel) { + Node next = node.getNextInVariableOrder(); + node.setPreviousInVariableOrder(null); + node.setNextInVariableOrder(null); + + try { + if (((node.getVariableTime() - nanos) > 0) + || !cache.evictEntry(node, RemovalCause.EXPIRED, nanos)) { + schedule(node); + } + node = next; + } catch (Throwable t) { + node.setPreviousInVariableOrder(sentinel.getPreviousInVariableOrder()); + node.setNextInVariableOrder(next); + sentinel.getPreviousInVariableOrder().setNextInVariableOrder(node); + sentinel.setPreviousInVariableOrder(prev); + throw t; + } + } + } + } + + /** + * Schedules a timer event for the node. + * + * @param node the entry in the cache + */ + public void schedule(Node node) { + Node sentinel = findBucket(node.getVariableTime()); + link(sentinel, node); + } + + /** + * Reschedules an active timer event for the node. + * + * @param node the entry in the cache + */ + public void reschedule(Node node) { + if (node.getNextInVariableOrder() != null) { + unlink(node); + schedule(node); + } + } + + /** + * Removes a timer event for this entry if present. + * + * @param node the entry in the cache + */ + public void deschedule(Node node) { + unlink(node); + node.setNextInVariableOrder(null); + node.setPreviousInVariableOrder(null); + } + + /** + * Determines the bucket that the timer event should be added to. + * + * @param time the time when the event fires + * @return the sentinel at the head of the bucket + */ + Node findBucket(long time) { + long duration = time - nanos; + int length = wheel.length - 1; + for (int i = 0; i < length; i++) { + if (duration < SPANS[i + 1]) { + long ticks = (time >>> SHIFT[i]); + int index = (int) (ticks & (wheel[i].length - 1)); + return wheel[i][index]; + } + } + return wheel[length][0]; + } + + /** Adds the entry at the tail of the bucket's list. */ + void link(Node sentinel, Node node) { + node.setPreviousInVariableOrder(sentinel.getPreviousInVariableOrder()); + node.setNextInVariableOrder(sentinel); + + sentinel.getPreviousInVariableOrder().setNextInVariableOrder(node); + sentinel.setPreviousInVariableOrder(node); + } + + /** Removes the entry from its bucket, if scheduled. */ + void unlink(Node node) { + Node next = node.getNextInVariableOrder(); + if (next != null) { + Node prev = node.getPreviousInVariableOrder(); + next.setPreviousInVariableOrder(prev); + prev.setNextInVariableOrder(next); + } + } + + /** Returns the duration until the next bucket expires, or {@link Long.MAX_VALUE} if none. */ + @SuppressWarnings("IntLongMath") + public long getExpirationDelay() { + for (int i = 0; i < SHIFT.length; i++) { + Node[] timerWheel = wheel[i]; + long ticks = (nanos >>> SHIFT[i]); + + long spanMask = SPANS[i] - 1; + int start = (int) (ticks & spanMask); + int end = start + timerWheel.length; + int mask = timerWheel.length - 1; + for (int j = start; j < end; j++) { + Node sentinel = timerWheel[(j & mask)]; + Node next = sentinel.getNextInVariableOrder(); + if (next == sentinel) { + continue; + } + long buckets = (j - start); + long delay = (buckets << SHIFT[i]) - (nanos & spanMask); + delay = (delay > 0) ? delay : SPANS[i]; + + for (int k = i + 1; k < SHIFT.length; k++) { + long nextDelay = peekAhead(k); + delay = Math.min(delay, nextDelay); + } + + return delay; + } + } + return Long.MAX_VALUE; + } + + /** + * Returns the duration when the wheel's next bucket expires, or {@link Long.MAX_VALUE} if empty. + */ + long peekAhead(int i) { + long ticks = (nanos >>> SHIFT[i]); + Node[] timerWheel = wheel[i]; + + long spanMask = SPANS[i] - 1; + int mask = timerWheel.length - 1; + int probe = (int) ((ticks + 1) & mask); + Node sentinel = timerWheel[probe]; + Node next = sentinel.getNextInVariableOrder(); + return (next == sentinel) ? Long.MAX_VALUE : (SPANS[i] - (nanos & spanMask)); + } + + /** + * Returns an unmodifiable snapshot map roughly ordered by the expiration time. The wheels are + * evaluated in order, but the timers that fall within the bucket's range are not sorted. Beware + * that obtaining the mappings is NOT a constant-time operation. + * + * @param ascending the direction + * @param limit the maximum number of entries + * @param transformer a function that unwraps the value + * @return an unmodifiable snapshot in the desired order + */ + public Map snapshot(boolean ascending, int limit, Function transformer) { + requireArgument(limit >= 0); + + Map map = new LinkedHashMap<>(Math.min(limit, cache.size())); + int startLevel = ascending ? 0 : wheel.length - 1; + for (int i = 0; i < wheel.length; i++) { + int indexOffset = ascending ? i : -i; + int index = startLevel + indexOffset; + + int ticks = (int) (nanos >>> SHIFT[index]); + int bucketMask = (wheel[index].length - 1); + int startBucket = (ticks & bucketMask) + (ascending ? 1 : 0); + for (int j = 0; j < wheel[index].length; j++) { + int bucketOffset = ascending ? j : -j; + Node sentinel = wheel[index][(startBucket + bucketOffset) & bucketMask]; + + for (Node node = traverse(ascending, sentinel); + node != sentinel; + node = traverse(ascending, node)) { + if (map.size() >= limit) { + break; + } + + K key = node.getKey(); + V value = transformer.apply(node.getValue()); + if ((key != null) && (value != null) && node.isAlive()) { + map.put(key, value); + } + } + } + } + return Collections.unmodifiableMap(map); + } + + static Node traverse(boolean ascending, Node node) { + return ascending ? node.getNextInVariableOrder() : node.getPreviousInVariableOrder(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < wheel.length; i++) { + Map> buckets = new TreeMap<>(); + for (int j = 0; j < wheel[i].length; j++) { + List events = new ArrayList<>(); + for (Node node = wheel[i][j].getNextInVariableOrder(); + node != wheel[i][j]; + node = node.getNextInVariableOrder()) { + events.add(node.getKey()); + } + if (!events.isEmpty()) { + buckets.put(j, events); + } + } + builder.append("Wheel #").append(i + 1).append(": ").append(buckets).append('\n'); + } + return builder.deleteCharAt(builder.length() - 1).toString(); + } + + /** A sentinel for the doubly-linked list in the bucket. */ + static final class Sentinel extends Node { + Node prev; + Node next; + + Sentinel() { + prev = next = this; + } + + @Override + public Node getPreviousInVariableOrder() { + return prev; + } + + @SuppressWarnings("NullAway") + @Override + public void setPreviousInVariableOrder(Node prev) { + this.prev = prev; + } + + @Override + public Node getNextInVariableOrder() { + return next; + } + + @SuppressWarnings("NullAway") + @Override + public void setNextInVariableOrder(Node next) { + this.next = next; + } + + @Override + public K getKey() { + return null; + } + + @Override + public Object getKeyReference() { + throw new UnsupportedOperationException(); + } + + @Override + public V getValue() { + return null; + } + + @Override + public Object getValueReference() { + throw new UnsupportedOperationException(); + } + + @Override + public void setValue(V value, ReferenceQueue referenceQueue) {} + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public boolean isAlive() { + return false; + } + + @Override + public boolean isRetired() { + return false; + } + + @Override + public boolean isDead() { + return false; + } + + @Override + public void retire() {} + + @Override + public void die() {} + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/UnboundedLocalCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/UnboundedLocalCache.java new file mode 100644 index 0000000000000..bdb9698fb4f3d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/UnboundedLocalCache.java @@ -0,0 +1,1140 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.Spliterator; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.github.benmanes.caffeine.cache.LocalLoadingCache.newBulkMappingFunction; +import static org.apache.iotdb.github.benmanes.caffeine.cache.LocalLoadingCache.newMappingFunction; + +/** + * An in-memory cache that has no capabilities for bounding the map. This implementation provides a + * lightweight wrapper on top of {@link ConcurrentHashMap}. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings("deprecation") +final class UnboundedLocalCache implements LocalCache { + final RemovalListener removalListener; + final ConcurrentHashMap data; + final StatsCounter statsCounter; + final boolean isRecordingStats; + final CacheWriter writer; + final Executor executor; + final Ticker ticker; + + transient Set keySet; + transient Collection values; + transient Set> entrySet; + + UnboundedLocalCache(Caffeine builder, boolean async) { + this.data = new ConcurrentHashMap<>(builder.getInitialCapacity()); + this.statsCounter = builder.getStatsCounterSupplier().get(); + this.removalListener = builder.getRemovalListener(async); + this.isRecordingStats = builder.isRecordingStats(); + this.writer = builder.getCacheWriter(async); + this.executor = builder.getExecutor(); + this.ticker = builder.getTicker(); + } + + @Override + public boolean hasWriteTime() { + return false; + } + + /* --------------- Cache --------------- */ + + @Override + public V getIfPresent(Object key, boolean recordStats) { + V value = data.get(key); + + if (recordStats) { + if (value == null) { + statsCounter.recordMisses(1); + } else { + statsCounter.recordHits(1); + } + } + return value; + } + + @Override + public V getIfPresentQuietly(Object key, long[ /* 1 */] writeTime) { + return data.get(key); + } + + @Override + public long estimatedSize() { + return data.mappingCount(); + } + + @Override + public Map getAllPresent(Iterable keys) { + Set uniqueKeys = new LinkedHashSet<>(); + for (Object key : keys) { + uniqueKeys.add(key); + } + + int misses = 0; + Map result = new LinkedHashMap<>(uniqueKeys.size()); + for (Object key : uniqueKeys) { + Object value = data.get(key); + if (value == null) { + misses++; + } else { + result.put(key, value); + } + } + statsCounter.recordMisses(misses); + statsCounter.recordHits(result.size()); + + @SuppressWarnings("unchecked") + Map castedResult = (Map) result; + return Collections.unmodifiableMap(castedResult); + } + + @Override + public void cleanUp() {} + + @Override + public StatsCounter statsCounter() { + return statsCounter; + } + + @Override + public boolean hasRemovalListener() { + return (removalListener != null); + } + + @Override + @SuppressWarnings("NullAway") + public RemovalListener removalListener() { + return removalListener; + } + + @Override + public void notifyRemoval(K key, V value, RemovalCause cause) { + requireNonNull(removalListener(), "Notification should be guarded with a check"); + executor.execute(() -> removalListener().onRemoval(key, value, cause)); + } + + @Override + public boolean isRecordingStats() { + return isRecordingStats; + } + + @Override + public Executor executor() { + return executor; + } + + @Override + public Ticker expirationTicker() { + return Ticker.disabledTicker(); + } + + @Override + public Ticker statsTicker() { + return ticker; + } + + /* --------------- JDK8+ Map extensions --------------- */ + + @Override + public void forEach(BiConsumer action) { + data.forEach(action); + } + + @Override + public void replaceAll(BiFunction function) { + requireNonNull(function); + + // ensures that the removal notification is processed after the removal has completed + @SuppressWarnings({"unchecked", "rawtypes"}) + K[] notificationKey = (K[]) new Object[1]; + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] notificationValue = (V[]) new Object[1]; + data.replaceAll( + (key, value) -> { + if (notificationKey[0] != null) { + notifyRemoval(notificationKey[0], notificationValue[0], RemovalCause.REPLACED); + notificationValue[0] = null; + notificationKey[0] = null; + } + + V newValue = requireNonNull(function.apply(key, value)); + if (newValue != value) { + writer.write(key, newValue); + } + if (hasRemovalListener() && (newValue != value)) { + notificationKey[0] = key; + notificationValue[0] = value; + } + + return newValue; + }); + if (notificationKey[0] != null) { + notifyRemoval(notificationKey[0], notificationValue[0], RemovalCause.REPLACED); + } + } + + @Override + public V computeIfAbsent( + K key, + Function mappingFunction, + boolean recordStats, + boolean recordLoad) { + requireNonNull(mappingFunction); + + // optimistic fast path due to computeIfAbsent always locking + V value = data.get(key); + if (value != null) { + if (recordStats) { + statsCounter.recordHits(1); + } + return value; + } + + boolean[] missed = new boolean[1]; + value = + data.computeIfAbsent( + key, + k -> { + // Do not communicate to CacheWriter on a load + missed[0] = true; + return recordStats + ? statsAware(mappingFunction, recordLoad).apply(key) + : mappingFunction.apply(key); + }); + if (!missed[0] && recordStats) { + statsCounter.recordHits(1); + } + return value; + } + + @Override + public V computeIfPresent( + K key, BiFunction remappingFunction) { + requireNonNull(remappingFunction); + + // optimistic fast path due to computeIfAbsent always locking + if (!data.containsKey(key)) { + return null; + } + + // ensures that the removal notification is processed after the removal has completed + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + RemovalCause[] cause = new RemovalCause[1]; + V nv = + data.computeIfPresent( + key, + (K k, V value) -> { + BiFunction function = + statsAware( + remappingFunction, + /* recordMiss */ false, /* recordLoad */ + true, /* recordLoadFailure */ + true); + V newValue = function.apply(k, value); + + cause[0] = (newValue == null) ? RemovalCause.EXPLICIT : RemovalCause.REPLACED; + if (hasRemovalListener() && (newValue != value)) { + oldValue[0] = value; + } + + return newValue; + }); + if (oldValue[0] != null) { + notifyRemoval(key, oldValue[0], cause[0]); + } + return nv; + } + + @Override + public V compute( + K key, + BiFunction remappingFunction, + boolean recordMiss, + boolean recordLoad, + boolean recordLoadFailure) { + requireNonNull(remappingFunction); + return remap(key, statsAware(remappingFunction, recordMiss, recordLoad, recordLoadFailure)); + } + + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + requireNonNull(remappingFunction); + requireNonNull(value); + + return remap( + key, + (k, oldValue) -> + (oldValue == null) ? value : statsAware(remappingFunction).apply(oldValue, value)); + } + + /** + * A {@link Map#compute(Object, BiFunction)} that does not directly record any cache statistics. + * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + */ + V remap(K key, BiFunction remappingFunction) { + // ensures that the removal notification is processed after the removal has completed + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + RemovalCause[] cause = new RemovalCause[1]; + V nv = + data.compute( + key, + (K k, V value) -> { + V newValue = remappingFunction.apply(k, value); + if ((value == null) && (newValue == null)) { + return null; + } + + cause[0] = (newValue == null) ? RemovalCause.EXPLICIT : RemovalCause.REPLACED; + if (hasRemovalListener() && (value != null) && (newValue != value)) { + oldValue[0] = value; + } + + return newValue; + }); + if (oldValue[0] != null) { + notifyRemoval(key, oldValue[0], cause[0]); + } + return nv; + } + + /* --------------- Concurrent Map --------------- */ + + @Override + public boolean isEmpty() { + return data.isEmpty(); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public void clear() { + if (!hasRemovalListener() && (writer == CacheWriter.disabledWriter())) { + data.clear(); + return; + } + for (K key : data.keySet()) { + remove(key); + } + } + + @Override + public boolean containsKey(Object key) { + return data.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return data.containsValue(value); + } + + @Override + public V get(Object key) { + return getIfPresent(key, /* recordStats */ false); + } + + @Override + public V put(K key, V value) { + return put(key, value, /* notifyWriter */ true); + } + + @Override + public V put(K key, V value, boolean notifyWriter) { + requireNonNull(value); + + // ensures that the removal notification is processed after the removal has completed + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + if ((writer == CacheWriter.disabledWriter()) || !notifyWriter) { + oldValue[0] = data.put(key, value); + } else { + data.compute( + key, + (k, v) -> { + if (value != v) { + writer.write(key, value); + } + oldValue[0] = v; + return value; + }); + } + + if (hasRemovalListener() && (oldValue[0] != null) && (oldValue[0] != value)) { + notifyRemoval(key, oldValue[0], RemovalCause.REPLACED); + } + + return oldValue[0]; + } + + @Override + public V putIfAbsent(K key, V value) { + requireNonNull(value); + + boolean[] wasAbsent = new boolean[1]; + V val = + data.computeIfAbsent( + key, + k -> { + writer.write(key, value); + wasAbsent[0] = true; + return value; + }); + return wasAbsent[0] ? null : val; + } + + @Override + public void putAll(Map map) { + if (!hasRemovalListener() && (writer == CacheWriter.disabledWriter())) { + data.putAll(map); + return; + } + map.forEach(this::put); + } + + @Override + public V remove(Object key) { + @SuppressWarnings("unchecked") + K castKey = (K) key; + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + + if (writer == CacheWriter.disabledWriter()) { + oldValue[0] = data.remove(key); + } else { + data.computeIfPresent( + castKey, + (k, v) -> { + writer.delete(castKey, v, RemovalCause.EXPLICIT); + oldValue[0] = v; + return null; + }); + } + + if (hasRemovalListener() && (oldValue[0] != null)) { + notifyRemoval(castKey, oldValue[0], RemovalCause.EXPLICIT); + } + + return oldValue[0]; + } + + @Override + public boolean remove(Object key, Object value) { + if (value == null) { + requireNonNull(key); + return false; + } + + @SuppressWarnings("unchecked") + K castKey = (K) key; + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + + data.computeIfPresent( + castKey, + (k, v) -> { + if (v.equals(value)) { + writer.delete(castKey, v, RemovalCause.EXPLICIT); + oldValue[0] = v; + return null; + } + return v; + }); + + boolean removed = (oldValue[0] != null); + if (hasRemovalListener() && removed) { + notifyRemoval(castKey, oldValue[0], RemovalCause.EXPLICIT); + } + return removed; + } + + @Override + public V replace(K key, V value) { + requireNonNull(value); + + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] oldValue = (V[]) new Object[1]; + data.computeIfPresent( + key, + (k, v) -> { + if (value != v) { + writer.write(key, value); + } + oldValue[0] = v; + return value; + }); + + if (hasRemovalListener() && (oldValue[0] != null) && (oldValue[0] != value)) { + notifyRemoval(key, value, RemovalCause.REPLACED); + } + return oldValue[0]; + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + requireNonNull(oldValue); + requireNonNull(newValue); + + @SuppressWarnings({"unchecked", "rawtypes"}) + V[] prev = (V[]) new Object[1]; + data.computeIfPresent( + key, + (k, v) -> { + if (v.equals(oldValue)) { + if (newValue != v) { + writer.write(key, newValue); + } + prev[0] = v; + return newValue; + } + return v; + }); + + boolean replaced = (prev[0] != null); + if (hasRemovalListener() && replaced && (prev[0] != newValue)) { + notifyRemoval(key, prev[0], RemovalCause.REPLACED); + } + return replaced; + } + + @Override + public boolean equals(Object o) { + return data.equals(o); + } + + @Override + public int hashCode() { + return data.hashCode(); + } + + @Override + public String toString() { + return data.toString(); + } + + @Override + public Set keySet() { + final Set ks = keySet; + return (ks == null) ? (keySet = new KeySetView<>(this)) : ks; + } + + @Override + public Collection values() { + final Collection vs = values; + return (vs == null) ? (values = new ValuesView<>(this)) : vs; + } + + @Override + public Set> entrySet() { + final Set> es = entrySet; + return (es == null) ? (entrySet = new EntrySetView<>(this)) : es; + } + + /** An adapter to safely externalize the keys. */ + static final class KeySetView extends AbstractSet { + final UnboundedLocalCache cache; + + KeySetView(UnboundedLocalCache cache) { + this.cache = requireNonNull(cache); + } + + @Override + public boolean isEmpty() { + return cache.isEmpty(); + } + + @Override + public int size() { + return cache.size(); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean contains(Object o) { + return cache.containsKey(o); + } + + @Override + public boolean remove(Object obj) { + return (cache.remove(obj) != null); + } + + @Override + public Iterator iterator() { + return new KeyIterator<>(cache); + } + + @Override + public Spliterator spliterator() { + return cache.data.keySet().spliterator(); + } + } + + /** An adapter to safely externalize the key iterator. */ + static final class KeyIterator implements Iterator { + final UnboundedLocalCache cache; + final Iterator iterator; + K current; + + KeyIterator(UnboundedLocalCache cache) { + this.cache = requireNonNull(cache); + this.iterator = cache.data.keySet().iterator(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public K next() { + current = iterator.next(); + return current; + } + + @Override + public void remove() { + if (current == null) { + throw new IllegalStateException(); + } + cache.remove(current); + current = null; + } + } + + /** An adapter to safely externalize the values. */ + static final class ValuesView extends AbstractCollection { + final UnboundedLocalCache cache; + + ValuesView(UnboundedLocalCache cache) { + this.cache = requireNonNull(cache); + } + + @Override + public boolean isEmpty() { + return cache.isEmpty(); + } + + @Override + public int size() { + return cache.size(); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean contains(Object o) { + return cache.containsValue(o); + } + + @Override + public boolean removeIf(Predicate filter) { + requireNonNull(filter); + boolean removed = false; + for (Entry entry : cache.data.entrySet()) { + if (filter.test(entry.getValue())) { + removed |= cache.remove(entry.getKey(), entry.getValue()); + } + } + return removed; + } + + @Override + public Iterator iterator() { + return new ValuesIterator<>(cache); + } + + @Override + public Spliterator spliterator() { + return cache.data.values().spliterator(); + } + } + + /** An adapter to safely externalize the value iterator. */ + static final class ValuesIterator implements Iterator { + final UnboundedLocalCache cache; + final Iterator> iterator; + Entry entry; + + ValuesIterator(UnboundedLocalCache cache) { + this.cache = requireNonNull(cache); + this.iterator = cache.data.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public V next() { + entry = iterator.next(); + return entry.getValue(); + } + + @Override + public void remove() { + if (entry == null) { + throw new IllegalStateException(); + } + cache.remove(entry.getKey()); + entry = null; + } + } + + /** An adapter to safely externalize the entries. */ + static final class EntrySetView extends AbstractSet> { + final UnboundedLocalCache cache; + + EntrySetView(UnboundedLocalCache cache) { + this.cache = requireNonNull(cache); + } + + @Override + public boolean isEmpty() { + return cache.isEmpty(); + } + + @Override + public int size() { + return cache.size(); + } + + @Override + public void clear() { + cache.clear(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry entry = (Entry) o; + Object key = entry.getKey(); + Object value = entry.getValue(); + if ((key == null) || (value == null)) { + return false; + } + V cachedValue = cache.get(key); + return (cachedValue != null) && cachedValue.equals(value); + } + + @Override + public boolean remove(Object obj) { + if (!(obj instanceof Entry)) { + return false; + } + Entry entry = (Entry) obj; + return cache.remove(entry.getKey(), entry.getValue()); + } + + @Override + public boolean removeIf(Predicate> filter) { + requireNonNull(filter); + boolean removed = false; + for (Entry entry : cache.data.entrySet()) { + if (filter.test(entry)) { + removed |= cache.remove(entry.getKey(), entry.getValue()); + } + } + return removed; + } + + @Override + public Iterator> iterator() { + return new EntryIterator<>(cache); + } + + @Override + public Spliterator> spliterator() { + return new EntrySpliterator<>(cache); + } + } + + /** An adapter to safely externalize the entry iterator. */ + static final class EntryIterator implements Iterator> { + final UnboundedLocalCache cache; + final Iterator> iterator; + Entry entry; + + EntryIterator(UnboundedLocalCache cache) { + this.cache = requireNonNull(cache); + this.iterator = cache.data.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Entry next() { + entry = iterator.next(); + return new WriteThroughEntry<>(cache, entry.getKey(), entry.getValue()); + } + + @Override + public void remove() { + if (entry == null) { + throw new IllegalStateException(); + } + cache.remove(entry.getKey()); + entry = null; + } + } + + /** An adapter to safely externalize the entry spliterator. */ + static final class EntrySpliterator implements Spliterator> { + final Spliterator> spliterator; + final UnboundedLocalCache cache; + + EntrySpliterator(UnboundedLocalCache cache) { + this(cache, cache.data.entrySet().spliterator()); + } + + EntrySpliterator(UnboundedLocalCache cache, Spliterator> spliterator) { + this.spliterator = requireNonNull(spliterator); + this.cache = requireNonNull(cache); + } + + @Override + public void forEachRemaining(Consumer> action) { + requireNonNull(action); + spliterator.forEachRemaining( + entry -> { + Entry e = new WriteThroughEntry<>(cache, entry.getKey(), entry.getValue()); + action.accept(e); + }); + } + + @Override + public boolean tryAdvance(Consumer> action) { + requireNonNull(action); + return spliterator.tryAdvance( + entry -> { + Entry e = new WriteThroughEntry<>(cache, entry.getKey(), entry.getValue()); + action.accept(e); + }); + } + + @Override + public EntrySpliterator trySplit() { + Spliterator> split = spliterator.trySplit(); + return (split == null) ? null : new EntrySpliterator<>(cache, split); + } + + @Override + public long estimateSize() { + return spliterator.estimateSize(); + } + + @Override + public int characteristics() { + return spliterator.characteristics(); + } + } + + /* --------------- Manual Cache --------------- */ + + static class UnboundedLocalManualCache implements LocalManualCache, Serializable { + private static final long serialVersionUID = 1; + + final UnboundedLocalCache cache; + Policy policy; + + UnboundedLocalManualCache(Caffeine builder) { + cache = new UnboundedLocalCache<>(builder, /* async */ false); + } + + @Override + public UnboundedLocalCache cache() { + return cache; + } + + @Override + public Policy policy() { + return (policy == null) + ? (policy = new UnboundedPolicy<>(cache, Function.identity())) + : policy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + Object writeReplace() { + SerializationProxy proxy = new SerializationProxy<>(); + proxy.isRecordingStats = cache.isRecordingStats; + proxy.removalListener = cache.removalListener; + proxy.ticker = cache.ticker; + proxy.writer = cache.writer; + return proxy; + } + } + + /** An eviction policy that supports no boundings. */ + static final class UnboundedPolicy implements Policy { + final UnboundedLocalCache cache; + final Function transformer; + + UnboundedPolicy(UnboundedLocalCache cache, Function transformer) { + this.transformer = transformer; + this.cache = cache; + } + + @Override + public boolean isRecordingStats() { + return cache.isRecordingStats; + } + + @Override + public V getIfPresentQuietly(Object key) { + return transformer.apply(cache.data.get(key)); + } + + @Override + public Optional> eviction() { + return Optional.empty(); + } + + @Override + public Optional> expireAfterAccess() { + return Optional.empty(); + } + + @Override + public Optional> expireAfterWrite() { + return Optional.empty(); + } + + @Override + public Optional> refreshAfterWrite() { + return Optional.empty(); + } + } + + /* --------------- Loading Cache --------------- */ + + static final class UnboundedLocalLoadingCache extends UnboundedLocalManualCache + implements LocalLoadingCache { + private static final long serialVersionUID = 1; + + final Function mappingFunction; + final CacheLoader loader; + final Function, Map> bulkMappingFunction; + + UnboundedLocalLoadingCache(Caffeine builder, CacheLoader loader) { + super(builder); + this.loader = loader; + this.mappingFunction = newMappingFunction(loader); + this.bulkMappingFunction = newBulkMappingFunction(loader); + } + + @Override + public CacheLoader cacheLoader() { + return loader; + } + + @Override + public Function mappingFunction() { + return mappingFunction; + } + + @Override + public Function, Map> bulkMappingFunction() { + return bulkMappingFunction; + } + + @Override + Object writeReplace() { + @SuppressWarnings("unchecked") + SerializationProxy proxy = (SerializationProxy) super.writeReplace(); + proxy.loader = loader; + return proxy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + } + + /* --------------- Async Cache --------------- */ + + static final class UnboundedLocalAsyncCache implements LocalAsyncCache, Serializable { + private static final long serialVersionUID = 1; + + final UnboundedLocalCache> cache; + + ConcurrentMap> mapView; + CacheView cacheView; + Policy policy; + + @SuppressWarnings("unchecked") + UnboundedLocalAsyncCache(Caffeine builder) { + cache = + new UnboundedLocalCache<>((Caffeine>) builder, /* async */ true); + } + + @Override + public UnboundedLocalCache> cache() { + return cache; + } + + @Override + public ConcurrentMap> asMap() { + return (mapView == null) ? (mapView = new AsyncAsMapView<>(this)) : mapView; + } + + @Override + public Cache synchronous() { + return (cacheView == null) ? (cacheView = new CacheView<>(this)) : cacheView; + } + + @Override + public Policy policy() { + @SuppressWarnings("unchecked") + UnboundedLocalCache castCache = (UnboundedLocalCache) cache; + Function, V> transformer = Async::getIfReady; + @SuppressWarnings("unchecked") + Function castTransformer = (Function) transformer; + return (policy == null) + ? (policy = new UnboundedPolicy<>(castCache, castTransformer)) + : policy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + Object writeReplace() { + SerializationProxy proxy = new SerializationProxy<>(); + proxy.isRecordingStats = cache.isRecordingStats; + proxy.removalListener = cache.removalListener; + proxy.ticker = cache.ticker; + proxy.writer = cache.writer; + proxy.async = true; + return proxy; + } + } + + /* --------------- Async Loading Cache --------------- */ + + static final class UnboundedLocalAsyncLoadingCache extends LocalAsyncLoadingCache + implements Serializable { + private static final long serialVersionUID = 1; + + final UnboundedLocalCache> cache; + + ConcurrentMap> mapView; + Policy policy; + + @SuppressWarnings("unchecked") + UnboundedLocalAsyncLoadingCache(Caffeine builder, AsyncCacheLoader loader) { + super(loader); + cache = + new UnboundedLocalCache<>((Caffeine>) builder, /* async */ true); + } + + @Override + public LocalCache> cache() { + return cache; + } + + @Override + public ConcurrentMap> asMap() { + return (mapView == null) ? (mapView = new AsyncAsMapView<>(this)) : mapView; + } + + @Override + public Policy policy() { + @SuppressWarnings("unchecked") + UnboundedLocalCache castCache = (UnboundedLocalCache) cache; + Function, V> transformer = Async::getIfReady; + @SuppressWarnings("unchecked") + Function castTransformer = (Function) transformer; + return (policy == null) + ? (policy = new UnboundedPolicy<>(castCache, castTransformer)) + : policy; + } + + @SuppressWarnings("UnusedVariable") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + Object writeReplace() { + SerializationProxy proxy = new SerializationProxy<>(); + proxy.isRecordingStats = cache.isRecordingStats(); + proxy.removalListener = cache.removalListener(); + proxy.ticker = cache.ticker; + proxy.writer = cache.writer; + proxy.loader = loader; + proxy.async = true; + return proxy; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/UnsafeAccess.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/UnsafeAccess.java new file mode 100644 index 0000000000000..da2657a18ddc2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/UnsafeAccess.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import sun.misc.Unsafe; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +/** + * Static access to {@link Unsafe} and convenient utility methods for performing low-level, unsafe + * operations. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings("restriction") +final class UnsafeAccess { + static final String ANDROID = "THE_ONE"; + static final String OPEN_JDK = "theUnsafe"; + + /** The Unsafe instance. */ + public static final Unsafe UNSAFE; + + static { + try { + UNSAFE = load(OPEN_JDK, ANDROID); + } catch (Exception e) { + throw new Error("Failed to load sun.misc.Unsafe", e); + } + } + + /** + * Returns the location of a given static field. + * + * @param clazz the class containing the field + * @param fieldName the name of the field + * @return the address offset of the field + */ + public static long objectFieldOffset(Class clazz, String fieldName) { + try { + return UNSAFE.objectFieldOffset(clazz.getDeclaredField(fieldName)); + } catch (NoSuchFieldException | SecurityException e) { + throw new Error(e); + } + } + + static Unsafe load(String openJdk, String android) + throws NoSuchMethodException, + InstantiationException, + IllegalAccessException, + InvocationTargetException { + Field field; + try { + // try OpenJDK field name + field = Unsafe.class.getDeclaredField(openJdk); + } catch (NoSuchFieldException e) { + try { + // try Android field name + field = Unsafe.class.getDeclaredField(android); + } catch (NoSuchFieldException e2) { + // try to create a new instance + Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor(); + unsafeConstructor.setAccessible(true); + return unsafeConstructor.newInstance(); + } + } + field.setAccessible(true); + return (Unsafe) field.get(null); + } + + private UnsafeAccess() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WI.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WI.java new file mode 100644 index 0000000000000..924f657407304 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WI.java @@ -0,0 +1,61 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • WeakKeys + *
  • InfirmValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WI extends BoundedLocalCache { + final ReferenceQueue keyReferenceQueue = new ReferenceQueue(); + + final ReferenceQueue valueReferenceQueue = new ReferenceQueue(); + + WI(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, (CacheLoader) cacheLoader, async); + } + + protected final ReferenceQueue keyReferenceQueue() { + return keyReferenceQueue; + } + + protected final boolean collectKeys() { + return true; + } + + protected final ReferenceQueue valueReferenceQueue() { + return valueReferenceQueue; + } + + protected final boolean collectValues() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIA.java new file mode 100644 index 0000000000000..1e8141d1fc169 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIA.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIA extends WI { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WIA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAR.java new file mode 100644 index 0000000000000..a73a56c4093f6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIAR extends WIA { + volatile long refreshAfterWriteNanos; + + WIAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAW.java new file mode 100644 index 0000000000000..fc60c7607a173 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAW.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIAW extends WIA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WIAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAWR.java new file mode 100644 index 0000000000000..64ca9e929a33b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIAWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIAWR extends WIAW { + volatile long refreshAfterWriteNanos; + + WIAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIL.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIL.java new file mode 100644 index 0000000000000..673b950d0a72c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIL.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Listening + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIL extends WI { + final RemovalListener removalListener; + + WIL(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.removalListener = builder.getRemovalListener(async); + } + + public final RemovalListener removalListener() { + return removalListener; + } + + public final boolean hasRemovalListener() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILA.java new file mode 100644 index 0000000000000..a9288895a069d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILA extends WIL { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WILA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAR.java new file mode 100644 index 0000000000000..1ca152bb5f746 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILAR extends WILA { + volatile long refreshAfterWriteNanos; + + WILAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAW.java new file mode 100644 index 0000000000000..64b70807c8a50 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILAW extends WILA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WILAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAWR.java new file mode 100644 index 0000000000000..558bbab5db70f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILAWR extends WILAW { + volatile long refreshAfterWriteNanos; + + WILAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMS.java new file mode 100644 index 0000000000000..3486b54f37197 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMS.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMS extends WIL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WILMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSA.java new file mode 100644 index 0000000000000..19feaf0ca9c71 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMSA extends WILMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WILMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAR.java new file mode 100644 index 0000000000000..690ad7500c657 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMSAR extends WILMSA { + volatile long refreshAfterWriteNanos; + + WILMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAW.java new file mode 100644 index 0000000000000..e5e5563e18795 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMSAW extends WILMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WILMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAWR.java new file mode 100644 index 0000000000000..2a3403844ddd6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMSAWR extends WILMSAW { + volatile long refreshAfterWriteNanos; + + WILMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSR.java new file mode 100644 index 0000000000000..5108a2488ef72 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMSR extends WILMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WILMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSW.java new file mode 100644 index 0000000000000..335938b780ea9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMSW extends WILMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WILMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSWR.java new file mode 100644 index 0000000000000..2d476bb5045b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMSWR extends WILMSW { + volatile long refreshAfterWriteNanos; + + WILMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMW.java new file mode 100644 index 0000000000000..7c1674e7c90cc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMW.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMW extends WIL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WILMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWA.java new file mode 100644 index 0000000000000..1029494dc21ca --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMWA extends WILMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WILMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAR.java new file mode 100644 index 0000000000000..57274ecf0a51e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMWAR extends WILMWA { + volatile long refreshAfterWriteNanos; + + WILMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAW.java new file mode 100644 index 0000000000000..cbebaf0210961 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMWAW extends WILMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WILMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAWR.java new file mode 100644 index 0000000000000..d96589fa6a453 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMWAWR extends WILMWAW { + volatile long refreshAfterWriteNanos; + + WILMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWR.java new file mode 100644 index 0000000000000..81ae2899b5fee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMWR extends WILMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WILMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWW.java new file mode 100644 index 0000000000000..08675b00fa0e7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILMWW extends WILMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WILMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWWR.java new file mode 100644 index 0000000000000..4b28a40fdf808 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILMWWR extends WILMWW { + volatile long refreshAfterWriteNanos; + + WILMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILR.java new file mode 100644 index 0000000000000..16bf7fb0c19ec --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILR extends WIL { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WILR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILS.java new file mode 100644 index 0000000000000..2ccb97215e121 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILS.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILS extends WIL { + final StatsCounter statsCounter; + + WILS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSA.java new file mode 100644 index 0000000000000..8be428b14ecba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSA.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSA extends WILS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WILSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAR.java new file mode 100644 index 0000000000000..8726f9043e6a3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSAR extends WILSA { + volatile long refreshAfterWriteNanos; + + WILSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAW.java new file mode 100644 index 0000000000000..ad9613625b64c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSAW extends WILSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WILSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAWR.java new file mode 100644 index 0000000000000..a16bb0e541cfc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSAWR extends WILSAW { + volatile long refreshAfterWriteNanos; + + WILSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMS.java new file mode 100644 index 0000000000000..f01e6c327db3b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMS.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMS extends WILS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WILSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSA.java new file mode 100644 index 0000000000000..a32a9b498e88f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSA.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMSA extends WILSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WILSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAR.java new file mode 100644 index 0000000000000..b3f47c1b7062a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMSAR extends WILSMSA { + volatile long refreshAfterWriteNanos; + + WILSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAW.java new file mode 100644 index 0000000000000..346f17fd8d94e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMSAW extends WILSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WILSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAWR.java new file mode 100644 index 0000000000000..b301480a2ad15 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMSAWR extends WILSMSAW { + volatile long refreshAfterWriteNanos; + + WILSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSR.java new file mode 100644 index 0000000000000..4dba0a0945ab9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMSR extends WILSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WILSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSW.java new file mode 100644 index 0000000000000..458f199ca726a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMSW extends WILSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WILSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSWR.java new file mode 100644 index 0000000000000..2ab49b50acc5e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMSWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMSWR extends WILSMSW { + volatile long refreshAfterWriteNanos; + + WILSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMW.java new file mode 100644 index 0000000000000..dd6ca29cb161f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMW.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMW extends WILS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WILSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWA.java new file mode 100644 index 0000000000000..45445c50f7fa5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWA.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMWA extends WILSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WILSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAR.java new file mode 100644 index 0000000000000..20945fffc25c1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMWAR extends WILSMWA { + volatile long refreshAfterWriteNanos; + + WILSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAW.java new file mode 100644 index 0000000000000..75c35f3bf1b7a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMWAW extends WILSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WILSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAWR.java new file mode 100644 index 0000000000000..d27917252041a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMWAWR extends WILSMWAW { + volatile long refreshAfterWriteNanos; + + WILSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWR.java new file mode 100644 index 0000000000000..17307edbf66c8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMWR extends WILSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WILSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWW.java new file mode 100644 index 0000000000000..9c6fac3f739c2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSMWW extends WILSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WILSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWWR.java new file mode 100644 index 0000000000000..5f64a2fa9f876 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSMWWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSMWWR extends WILSMWW { + volatile long refreshAfterWriteNanos; + + WILSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSR.java new file mode 100644 index 0000000000000..b0e1dfdad9387 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSR extends WILS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WILSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSW.java new file mode 100644 index 0000000000000..272a4407fb000 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILSW extends WILS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WILSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSWR.java new file mode 100644 index 0000000000000..3f81bb7c168c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILSWR extends WILSW { + volatile long refreshAfterWriteNanos; + + WILSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILW.java new file mode 100644 index 0000000000000..2bb0999dabe6c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WILW extends WIL { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WILW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILWR.java new file mode 100644 index 0000000000000..2b9fc35af5600 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WILWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Listening (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WILWR extends WILW { + volatile long refreshAfterWriteNanos; + + WILWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMS.java new file mode 100644 index 0000000000000..13b6116239a81 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMS.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMS extends WI { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WIMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSA.java new file mode 100644 index 0000000000000..ad7906bd0cb08 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSA.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMSA extends WIMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WIMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAR.java new file mode 100644 index 0000000000000..9a8aa15f07720 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMSAR extends WIMSA { + volatile long refreshAfterWriteNanos; + + WIMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAW.java new file mode 100644 index 0000000000000..94423e01674e9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMSAW extends WIMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WIMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAWR.java new file mode 100644 index 0000000000000..20a1901caec00 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMSAWR extends WIMSAW { + volatile long refreshAfterWriteNanos; + + WIMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSR.java new file mode 100644 index 0000000000000..f7d2f18de017e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMSR extends WIMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WIMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSW.java new file mode 100644 index 0000000000000..e04d749f4577d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMSW extends WIMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WIMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSWR.java new file mode 100644 index 0000000000000..23e7ac52bba5d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMSWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMSWR extends WIMSW { + volatile long refreshAfterWriteNanos; + + WIMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMW.java new file mode 100644 index 0000000000000..0413f687b8155 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMW.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMW extends WI { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WIMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWA.java new file mode 100644 index 0000000000000..46758e1bca150 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWA.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMWA extends WIMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WIMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAR.java new file mode 100644 index 0000000000000..c1c418282472a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMWAR extends WIMWA { + volatile long refreshAfterWriteNanos; + + WIMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAW.java new file mode 100644 index 0000000000000..8d84d873e2156 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMWAW extends WIMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WIMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAWR.java new file mode 100644 index 0000000000000..b1bc84e689e52 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMWAWR extends WIMWAW { + volatile long refreshAfterWriteNanos; + + WIMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWR.java new file mode 100644 index 0000000000000..93a873108a962 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMWR extends WIMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WIMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWW.java new file mode 100644 index 0000000000000..8cce6fb1d881a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIMWW extends WIMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WIMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWWR.java new file mode 100644 index 0000000000000..51781709e46f8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIMWWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIMWWR extends WIMWW { + volatile long refreshAfterWriteNanos; + + WIMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIR.java new file mode 100644 index 0000000000000..5d12cf700f50d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIR.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIR extends WI { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WIR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIS.java new file mode 100644 index 0000000000000..dfdaaf94aece4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIS.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIS extends WI { + final StatsCounter statsCounter; + + WIS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISA.java new file mode 100644 index 0000000000000..99cca2a8008d6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISA extends WIS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WISA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAR.java new file mode 100644 index 0000000000000..f94e9b3cb6ba7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISAR extends WISA { + volatile long refreshAfterWriteNanos; + + WISAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAW.java new file mode 100644 index 0000000000000..d96ac5a2767df --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISAW extends WISA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WISAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAWR.java new file mode 100644 index 0000000000000..f3750591f767e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISAWR extends WISAW { + volatile long refreshAfterWriteNanos; + + WISAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMS.java new file mode 100644 index 0000000000000..94e2d4b9c39b9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMS.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMS extends WIS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WISMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSA.java new file mode 100644 index 0000000000000..8eed0ee1b00b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMSA extends WISMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WISMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAR.java new file mode 100644 index 0000000000000..49bdf626a9883 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMSAR extends WISMSA { + volatile long refreshAfterWriteNanos; + + WISMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAW.java new file mode 100644 index 0000000000000..cef790a49db62 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMSAW extends WISMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WISMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAWR.java new file mode 100644 index 0000000000000..1f823478ca660 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMSAWR extends WISMSAW { + volatile long refreshAfterWriteNanos; + + WISMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSR.java new file mode 100644 index 0000000000000..35fdf27bf85cb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMSR extends WISMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WISMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSW.java new file mode 100644 index 0000000000000..fbb22b2d1fda2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMSW extends WISMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WISMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSWR.java new file mode 100644 index 0000000000000..c19254508bf80 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMSWR extends WISMSW { + volatile long refreshAfterWriteNanos; + + WISMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMW.java new file mode 100644 index 0000000000000..e7902441e0bab --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMW.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMW extends WIS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WISMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWA.java new file mode 100644 index 0000000000000..89d8f7fa23d58 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMWA extends WISMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WISMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAR.java new file mode 100644 index 0000000000000..d68ad1655c059 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMWAR extends WISMWA { + volatile long refreshAfterWriteNanos; + + WISMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAW.java new file mode 100644 index 0000000000000..97bb4006c23d6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMWAW extends WISMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WISMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAWR.java new file mode 100644 index 0000000000000..d5bc4e5bc0171 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMWAWR extends WISMWAW { + volatile long refreshAfterWriteNanos; + + WISMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWR.java new file mode 100644 index 0000000000000..df1dea5e0150e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMWR extends WISMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WISMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWW.java new file mode 100644 index 0000000000000..e2ec9a2b33d2b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISMWW extends WISMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WISMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWWR.java new file mode 100644 index 0000000000000..2f0a75502df4e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISMWWR extends WISMWW { + volatile long refreshAfterWriteNanos; + + WISMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISR.java new file mode 100644 index 0000000000000..e7676a26be057 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISR extends WIS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WISR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISW.java new file mode 100644 index 0000000000000..df4daab56c998 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WISW extends WIS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WISW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISWR.java new file mode 100644 index 0000000000000..0306ebd7401ad --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WISWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WISWR extends WISW { + volatile long refreshAfterWriteNanos; + + WISWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIW.java new file mode 100644 index 0000000000000..37ddcf0263fde --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIW.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WIW extends WI { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WIW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIWR.java new file mode 100644 index 0000000000000..d735c6a823e2c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WIWR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • InfirmValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WIWR extends WIW { + volatile long refreshAfterWriteNanos; + + WIWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WS.java new file mode 100644 index 0000000000000..244e1aa0e4ba6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WS.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.lang.ref.ReferenceQueue; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • WeakKeys + *
  • StrongValues + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WS extends BoundedLocalCache { + final ReferenceQueue keyReferenceQueue = new ReferenceQueue(); + + WS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, (CacheLoader) cacheLoader, async); + } + + protected final ReferenceQueue keyReferenceQueue() { + return keyReferenceQueue; + } + + protected final boolean collectKeys() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSA.java new file mode 100644 index 0000000000000..ea3e22c979dce --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSA.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSA extends WS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAR.java new file mode 100644 index 0000000000000..bede433a2cf62 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSAR extends WSA { + volatile long refreshAfterWriteNanos; + + WSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAW.java new file mode 100644 index 0000000000000..81b346d03cc3c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAW.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSAW extends WSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAWR.java new file mode 100644 index 0000000000000..0de0921d3dc04 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSAWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSAWR extends WSAW { + volatile long refreshAfterWriteNanos; + + WSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSL.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSL.java new file mode 100644 index 0000000000000..a54b5c8080666 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSL.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Listening + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSL extends WS { + final RemovalListener removalListener; + + WSL(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.removalListener = builder.getRemovalListener(async); + } + + public final RemovalListener removalListener() { + return removalListener; + } + + public final boolean hasRemovalListener() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLA.java new file mode 100644 index 0000000000000..ed328cfe6124f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLA extends WSL { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSLA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAR.java new file mode 100644 index 0000000000000..93315e94ab812 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLAR extends WSLA { + volatile long refreshAfterWriteNanos; + + WSLAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAW.java new file mode 100644 index 0000000000000..b4da6b727117d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLAW extends WSLA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSLAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAWR.java new file mode 100644 index 0000000000000..811529c6fbc35 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLAWR extends WSLAW { + volatile long refreshAfterWriteNanos; + + WSLAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMS.java new file mode 100644 index 0000000000000..ce7df857d3b40 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMS.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMS extends WSL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSLMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSA.java new file mode 100644 index 0000000000000..da986e0af15e4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMSA extends WSLMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSLMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAR.java new file mode 100644 index 0000000000000..2079b6b55f220 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMSAR extends WSLMSA { + volatile long refreshAfterWriteNanos; + + WSLMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAW.java new file mode 100644 index 0000000000000..537c8646f3f1c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMSAW extends WSLMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSLMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAWR.java new file mode 100644 index 0000000000000..d152e5258cd33 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMSAWR extends WSLMSAW { + volatile long refreshAfterWriteNanos; + + WSLMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSR.java new file mode 100644 index 0000000000000..5fe2aa7561973 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMSR extends WSLMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSLMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSW.java new file mode 100644 index 0000000000000..8bf7404555d7a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMSW extends WSLMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSLMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSWR.java new file mode 100644 index 0000000000000..50f663104d6de --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMSWR extends WSLMSW { + volatile long refreshAfterWriteNanos; + + WSLMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMW.java new file mode 100644 index 0000000000000..d05cf31b5d505 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMW.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMW extends WSL { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSLMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWA.java new file mode 100644 index 0000000000000..8911d3ac81990 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMWA extends WSLMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSLMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAR.java new file mode 100644 index 0000000000000..6abf232014da7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMWAR extends WSLMWA { + volatile long refreshAfterWriteNanos; + + WSLMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAW.java new file mode 100644 index 0000000000000..d8cc2abf8935f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMWAW extends WSLMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSLMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAWR.java new file mode 100644 index 0000000000000..e8156078cc6bb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMWAWR extends WSLMWAW { + volatile long refreshAfterWriteNanos; + + WSLMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWR.java new file mode 100644 index 0000000000000..257b694d3bbc6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMWR extends WSLMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSLMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWW.java new file mode 100644 index 0000000000000..f33ec238c0e30 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLMWW extends WSLMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSLMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWWR.java new file mode 100644 index 0000000000000..3b310cd55f42b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLMWWR extends WSLMWW { + volatile long refreshAfterWriteNanos; + + WSLMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLR.java new file mode 100644 index 0000000000000..7c7b2be27d9eb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLR extends WSL { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSLR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLS.java new file mode 100644 index 0000000000000..1f064136d4a03 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLS.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLS extends WSL { + final StatsCounter statsCounter; + + WSLS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSA.java new file mode 100644 index 0000000000000..ebee6965316b0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSA.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSA extends WSLS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSLSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAR.java new file mode 100644 index 0000000000000..ba695af358a9c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSAR extends WSLSA { + volatile long refreshAfterWriteNanos; + + WSLSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAW.java new file mode 100644 index 0000000000000..873f526c3abb9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSAW extends WSLSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSLSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAWR.java new file mode 100644 index 0000000000000..01163edc7e63d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSAWR extends WSLSAW { + volatile long refreshAfterWriteNanos; + + WSLSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMS.java new file mode 100644 index 0000000000000..4af476664d3d1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMS.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMS extends WSLS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSLSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSA.java new file mode 100644 index 0000000000000..120c2d65461cd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSA.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMSA extends WSLSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSLSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAR.java new file mode 100644 index 0000000000000..f05b06826051d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMSAR extends WSLSMSA { + volatile long refreshAfterWriteNanos; + + WSLSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAW.java new file mode 100644 index 0000000000000..66a59fcb6a239 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMSAW extends WSLSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSLSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAWR.java new file mode 100644 index 0000000000000..5ce385790a4ef --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMSAWR extends WSLSMSAW { + volatile long refreshAfterWriteNanos; + + WSLSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSR.java new file mode 100644 index 0000000000000..ca0b7927d86ae --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMSR extends WSLSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSLSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSW.java new file mode 100644 index 0000000000000..dda3bbab37d3a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMSW extends WSLSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSLSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSWR.java new file mode 100644 index 0000000000000..f1aacc066ab29 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMSWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMSWR extends WSLSMSW { + volatile long refreshAfterWriteNanos; + + WSLSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMW.java new file mode 100644 index 0000000000000..0d2fe7f23fd1f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMW.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMW extends WSLS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSLSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWA.java new file mode 100644 index 0000000000000..6eaad7182c64e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWA.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMWA extends WSLSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSLSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAR.java new file mode 100644 index 0000000000000..12c1893d77d9b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMWAR extends WSLSMWA { + volatile long refreshAfterWriteNanos; + + WSLSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAW.java new file mode 100644 index 0000000000000..f1dd16fbccf16 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAW.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMWAW extends WSLSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSLSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAWR.java new file mode 100644 index 0000000000000..464615e6285ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWAWR.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMWAWR extends WSLSMWAW { + volatile long refreshAfterWriteNanos; + + WSLSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWR.java new file mode 100644 index 0000000000000..c65450574ab7a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWR.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMWR extends WSLSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSLSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWW.java new file mode 100644 index 0000000000000..9395dfd93a8c0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWW.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSMWW extends WSLSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSLSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWWR.java new file mode 100644 index 0000000000000..62db55c928370 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSMWWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSMWWR extends WSLSMWW { + volatile long refreshAfterWriteNanos; + + WSLSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSR.java new file mode 100644 index 0000000000000..e5c28dde2c47e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSR extends WSLS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSLSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSW.java new file mode 100644 index 0000000000000..81be36d76ead1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLSW extends WSLS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSLSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSWR.java new file mode 100644 index 0000000000000..879849c62f387 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLSWR extends WSLSW { + volatile long refreshAfterWriteNanos; + + WSLSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLW.java new file mode 100644 index 0000000000000..99c10d94eb5a0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSLW extends WSL { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSLW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLWR.java new file mode 100644 index 0000000000000..d9b9cd1545cc6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSLWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Listening (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSLWR extends WSLW { + volatile long refreshAfterWriteNanos; + + WSLWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMS.java new file mode 100644 index 0000000000000..6fe2961421da7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMS.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMS extends WS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSA.java new file mode 100644 index 0000000000000..433c52f832844 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSA.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMSA extends WSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAR.java new file mode 100644 index 0000000000000..6df42ba010569 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMSAR extends WSMSA { + volatile long refreshAfterWriteNanos; + + WSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAW.java new file mode 100644 index 0000000000000..84191e44e5954 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMSAW extends WSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAWR.java new file mode 100644 index 0000000000000..51bd262fb1e4d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMSAWR extends WSMSAW { + volatile long refreshAfterWriteNanos; + + WSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSR.java new file mode 100644 index 0000000000000..b3cdb6e2f1b27 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMSR extends WSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSW.java new file mode 100644 index 0000000000000..8baf8411add56 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMSW extends WSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSWR.java new file mode 100644 index 0000000000000..5e3d2e666525b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMSWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMSWR extends WSMSW { + volatile long refreshAfterWriteNanos; + + WSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMW.java new file mode 100644 index 0000000000000..b3954a139a0b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMW.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMW extends WS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWA.java new file mode 100644 index 0000000000000..2b5ac84aed659 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWA.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMWA extends WSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAR.java new file mode 100644 index 0000000000000..942b16b8e48c0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMWAR extends WSMWA { + volatile long refreshAfterWriteNanos; + + WSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAW.java new file mode 100644 index 0000000000000..9b6ebfa9b8db1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMWAW extends WSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAWR.java new file mode 100644 index 0000000000000..b42a8a20b27f7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMWAWR extends WSMWAW { + volatile long refreshAfterWriteNanos; + + WSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWR.java new file mode 100644 index 0000000000000..82831d4723e31 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMWR extends WSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWW.java new file mode 100644 index 0000000000000..9a039af22b722 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSMWW extends WSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWWR.java new file mode 100644 index 0000000000000..b8629e1a4ea90 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSMWWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSMWWR extends WSMWW { + volatile long refreshAfterWriteNanos; + + WSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSR.java new file mode 100644 index 0000000000000..54b4bc30ebb57 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSR.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSR extends WS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSS.java new file mode 100644 index 0000000000000..152dd1ad75d3e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSS.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.stats.StatsCounter; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • Stats + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSS extends WS { + final StatsCounter statsCounter; + + WSS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.statsCounter = builder.getStatsCounterSupplier().get(); + } + + public final boolean isRecordingStats() { + return true; + } + + public final Ticker statsTicker() { + return Ticker.systemTicker(); + } + + public final StatsCounter statsCounter() { + return statsCounter; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSA.java new file mode 100644 index 0000000000000..fc9b3d6b7156e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSA.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSA extends WSS { + final Ticker ticker; + + final AccessOrderDeque> accessOrderWindowDeque; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAR.java new file mode 100644 index 0000000000000..b2990cc1e1c1f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSAR extends WSSA { + volatile long refreshAfterWriteNanos; + + WSSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAW.java new file mode 100644 index 0000000000000..e9f05b643b1cb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAW.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSAW extends WSSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAWR.java new file mode 100644 index 0000000000000..74ea0486431cf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSAWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSAWR extends WSSAW { + volatile long refreshAfterWriteNanos; + + WSSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMS.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMS.java new file mode 100644 index 0000000000000..96e7c34f3cfdd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMS.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumSize + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMS extends WSS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSSMS(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSA.java new file mode 100644 index 0000000000000..49f002b9148a9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMSA extends WSSMS { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSSMSA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAR.java new file mode 100644 index 0000000000000..efde604cebca5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMSAR extends WSSMSA { + volatile long refreshAfterWriteNanos; + + WSSMSAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAW.java new file mode 100644 index 0000000000000..3dd906bd614a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMSAW extends WSSMSA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSSMSAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAWR.java new file mode 100644 index 0000000000000..c11f09c9fb143 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMSAWR extends WSSMSAW { + volatile long refreshAfterWriteNanos; + + WSSMSAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSR.java new file mode 100644 index 0000000000000..167cc29a520b5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMSR extends WSSMS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSSMSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSW.java new file mode 100644 index 0000000000000..6fef9cc24a46e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMSW extends WSSMS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSSMSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSWR.java new file mode 100644 index 0000000000000..2ef18d9b4ec22 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMSWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumSize (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMSWR extends WSSMSW { + volatile long refreshAfterWriteNanos; + + WSSMSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMW.java new file mode 100644 index 0000000000000..33b11f918a47d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMW.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • MaximumWeight + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMW extends WSS { + long maximum; + + long weightedSize; + + long windowMaximum; + + long windowWeightedSize; + + long mainProtectedMaximum; + + long mainProtectedWeightedSize; + + double stepSize; + + long adjustment; + + int hitsInSample; + + int missesInSample; + + double previousSampleHitRate; + + final FrequencySketch sketch; + + final AccessOrderDeque> accessOrderWindowDeque; + + final AccessOrderDeque> accessOrderProbationDeque; + + final AccessOrderDeque> accessOrderProtectedDeque; + + WSSMW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.sketch = new FrequencySketch(); + if (builder.hasInitialCapacity()) { + long capacity = Math.min(builder.getMaximum(), builder.getInitialCapacity()); + this.sketch.ensureCapacity(capacity); + } + this.accessOrderWindowDeque = + builder.evicts() || builder.expiresAfterAccess() + ? new AccessOrderDeque>() + : null; + this.accessOrderProbationDeque = new AccessOrderDeque>(); + this.accessOrderProtectedDeque = new AccessOrderDeque>(); + } + + protected final boolean evicts() { + return true; + } + + protected final long maximum() { + return maximum; + } + + protected final void setMaximum(long maximum) { + this.maximum = maximum; + } + + protected final long weightedSize() { + return weightedSize; + } + + protected final void setWeightedSize(long weightedSize) { + this.weightedSize = weightedSize; + } + + protected final long windowMaximum() { + return windowMaximum; + } + + protected final void setWindowMaximum(long windowMaximum) { + this.windowMaximum = windowMaximum; + } + + protected final long windowWeightedSize() { + return windowWeightedSize; + } + + protected final void setWindowWeightedSize(long windowWeightedSize) { + this.windowWeightedSize = windowWeightedSize; + } + + protected final long mainProtectedMaximum() { + return mainProtectedMaximum; + } + + protected final void setMainProtectedMaximum(long mainProtectedMaximum) { + this.mainProtectedMaximum = mainProtectedMaximum; + } + + protected final long mainProtectedWeightedSize() { + return mainProtectedWeightedSize; + } + + protected final void setMainProtectedWeightedSize(long mainProtectedWeightedSize) { + this.mainProtectedWeightedSize = mainProtectedWeightedSize; + } + + protected final double stepSize() { + return stepSize; + } + + protected final void setStepSize(double stepSize) { + this.stepSize = stepSize; + } + + protected final long adjustment() { + return adjustment; + } + + protected final void setAdjustment(long adjustment) { + this.adjustment = adjustment; + } + + protected final int hitsInSample() { + return hitsInSample; + } + + protected final void setHitsInSample(int hitsInSample) { + this.hitsInSample = hitsInSample; + } + + protected final int missesInSample() { + return missesInSample; + } + + protected final void setMissesInSample(int missesInSample) { + this.missesInSample = missesInSample; + } + + protected final double previousSampleHitRate() { + return previousSampleHitRate; + } + + protected final void setPreviousSampleHitRate(double previousSampleHitRate) { + this.previousSampleHitRate = previousSampleHitRate; + } + + protected final FrequencySketch frequencySketch() { + return sketch; + } + + protected final AccessOrderDeque> accessOrderWindowDeque() { + return accessOrderWindowDeque; + } + + protected final AccessOrderDeque> accessOrderProbationDeque() { + return accessOrderProbationDeque; + } + + protected final AccessOrderDeque> accessOrderProtectedDeque() { + return accessOrderProtectedDeque; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWA.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWA.java new file mode 100644 index 0000000000000..18d34c988866f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWA.java @@ -0,0 +1,92 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireAccess + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMWA extends WSSMW { + final Ticker ticker; + + final Expiry expiry; + + final TimerWheel timerWheel; + + volatile long expiresAfterAccessNanos; + + final Pacer pacer; + + WSSMWA(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.expiry = builder.getExpiry(isAsync); + this.timerWheel = builder.expiresVariable() ? new TimerWheel(this) : null; + this.expiresAfterAccessNanos = builder.getExpiresAfterAccessNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final boolean expiresVariable() { + return (timerWheel != null); + } + + protected final Expiry expiry() { + return expiry; + } + + protected final TimerWheel timerWheel() { + return timerWheel; + } + + protected final boolean expiresAfterAccess() { + return (timerWheel == null); + } + + protected final long expiresAfterAccessNanos() { + return expiresAfterAccessNanos; + } + + protected final void setExpiresAfterAccessNanos(long expiresAfterAccessNanos) { + this.expiresAfterAccessNanos = expiresAfterAccessNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAR.java new file mode 100644 index 0000000000000..ebc21bd75abdb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMWAR extends WSSMWA { + volatile long refreshAfterWriteNanos; + + WSSMWAR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAW.java new file mode 100644 index 0000000000000..b8a9fa22ba156 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAW.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMWAW extends WSSMWA { + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + WSSMWAW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAWR.java new file mode 100644 index 0000000000000..b3c3423784a94 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWAWR.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireAccess (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMWAWR extends WSSMWAW { + volatile long refreshAfterWriteNanos; + + WSSMWAWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWR.java new file mode 100644 index 0000000000000..5f33389b3baa0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWR.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMWR extends WSSMW { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSSMWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWW.java new file mode 100644 index 0000000000000..f9cf9cc40aef6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWW.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSMWW extends WSSMW { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSSMWW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWWR.java new file mode 100644 index 0000000000000..39e7871d36198 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSMWWR.java @@ -0,0 +1,58 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • MaximumWeight (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSMWWR extends WSSMWW { + volatile long refreshAfterWriteNanos; + + WSSMWWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSR.java new file mode 100644 index 0000000000000..c0c2cf8884aeb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSR.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSR extends WSS { + final Ticker ticker; + + volatile long refreshAfterWriteNanos; + + WSSR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + public Ticker expirationTicker() { + return ticker; + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSW.java new file mode 100644 index 0000000000000..c1642a6f198ee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSW.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSSW extends WSS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSWR.java new file mode 100644 index 0000000000000..bfd8875afb755 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSSWR.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • Stats (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSSWR extends WSSW { + volatile long refreshAfterWriteNanos; + + WSSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSW.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSW.java new file mode 100644 index 0000000000000..8dbc762ea00fb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSW.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • ExpireWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +class WSW extends WS { + final Ticker ticker; + + final WriteOrderDeque> writeOrderDeque; + + volatile long expiresAfterWriteNanos; + + final Pacer pacer; + + WSW(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.ticker = builder.getTicker(); + this.writeOrderDeque = new WriteOrderDeque>(); + this.expiresAfterWriteNanos = builder.getExpiresAfterWriteNanos(); + this.pacer = + (builder.getScheduler() == Scheduler.disabledScheduler()) + ? null + : new Pacer(builder.getScheduler()); + } + + public final Ticker expirationTicker() { + return ticker; + } + + protected final WriteOrderDeque> writeOrderDeque() { + return writeOrderDeque; + } + + protected final boolean expiresAfterWrite() { + return true; + } + + protected final long expiresAfterWriteNanos() { + return expiresAfterWriteNanos; + } + + protected final void setExpiresAfterWriteNanos(long expiresAfterWriteNanos) { + this.expiresAfterWriteNanos = expiresAfterWriteNanos; + } + + public final Pacer pacer() { + return pacer; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSWR.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSWR.java new file mode 100644 index 0000000000000..312957a60a7d0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WSWR.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +/** + * WARNING: GENERATED CODE + * + *

A cache that provides the following features: + * + *

    + *
  • RefreshWrite + *
  • WeakKeys (inherited) + *
  • StrongValues (inherited) + *
  • ExpireWrite (inherited) + *
+ * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings({"unchecked", "MissingOverride", "NullAway"}) +final class WSWR extends WSW { + volatile long refreshAfterWriteNanos; + + WSWR(Caffeine builder, CacheLoader cacheLoader, boolean async) { + super(builder, cacheLoader, async); + this.refreshAfterWriteNanos = builder.getRefreshAfterWriteNanos(); + } + + protected boolean refreshAfterWrite() { + return true; + } + + protected long refreshAfterWriteNanos() { + return refreshAfterWriteNanos; + } + + protected void setRefreshAfterWriteNanos(long refreshAfterWriteNanos) { + this.refreshAfterWriteNanos = refreshAfterWriteNanos; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Weigher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Weigher.java new file mode 100644 index 0000000000000..3486606d13795 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/Weigher.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.io.Serializable; + +import static java.util.Objects.requireNonNull; + +/** + * Calculates the weights of cache entries. The total weight threshold is used to determine when an + * eviction is required. + * + * @param the type of keys + * @param the type of values + * @author ben.manes@gmail.com (Ben Manes) + */ +@FunctionalInterface +public interface Weigher { + + /** + * Returns the weight of a cache entry. There is no unit for entry weights; rather they are simply + * relative to each other. + * + * @param key the key to weigh + * @param value the value to weigh + * @return the weight of the entry; must be non-negative + */ + int weigh(K key, V value); + + /** + * Returns a weigher where an entry has a weight of {@code 1}. + * + * @param the type of keys + * @param the type of values + * @return a weigher where an entry has a weight of {@code 1} + */ + static Weigher singletonWeigher() { + @SuppressWarnings("unchecked") + Weigher self = (Weigher) SingletonWeigher.INSTANCE; + return self; + } + + /** + * Returns a weigher that enforces that the weight is non-negative. + * + * @param delegate the weigher to weighs the entry + * @param the type of keys + * @param the type of values + * @return a weigher that enforces that the weight is non-negative + */ + static Weigher boundedWeigher(Weigher delegate) { + return new BoundedWeigher<>(delegate); + } +} + +enum SingletonWeigher implements Weigher { + INSTANCE; + + @Override + public int weigh(Object key, Object value) { + return 1; + } +} + +final class BoundedWeigher implements Weigher, Serializable { + static final long serialVersionUID = 1; + final Weigher delegate; + + BoundedWeigher(Weigher delegate) { + this.delegate = requireNonNull(delegate); + } + + @Override + public int weigh(K key, V value) { + int weight = delegate.weigh(key, value); + Caffeine.requireArgument(weight >= 0); + return weight; + } + + Object writeReplace() { + return delegate; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WriteOrderDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WriteOrderDeque.java new file mode 100644 index 0000000000000..0ef6713ae8930 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WriteOrderDeque.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import org.apache.iotdb.github.benmanes.caffeine.cache.WriteOrderDeque.WriteOrder; + +import java.util.Deque; + +/** + * A linked deque implementation used to represent a write-order queue. + * + * @author ben.manes@gmail.com (Ben Manes) + * @param the type of elements held in this collection + */ +final class WriteOrderDeque> extends AbstractLinkedDeque { + + @Override + public boolean contains(Object o) { + return (o instanceof WriteOrder) && contains((WriteOrder) o); + } + + // A fast-path containment check + boolean contains(WriteOrder e) { + return (e.getPreviousInWriteOrder() != null) + || (e.getNextInWriteOrder() != null) + || (e == first); + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + return (o instanceof WriteOrder) && remove((E) o); + } + + // A fast-path removal + public boolean remove(E e) { + if (contains(e)) { + unlink(e); + return true; + } + return false; + } + + @Override + public E getPrevious(E e) { + return e.getPreviousInWriteOrder(); + } + + @Override + public void setPrevious(E e, E prev) { + e.setPreviousInWriteOrder(prev); + } + + @Override + public E getNext(E e) { + return e.getNextInWriteOrder(); + } + + @Override + public void setNext(E e, E next) { + e.setNextInWriteOrder(next); + } + + /** An element that is linked on the {@link Deque}. */ + interface WriteOrder> { + + /** + * Retrieves the previous element or null if either the element is unlinked or the + * first element on the deque. + */ + T getPreviousInWriteOrder(); + + /** Sets the previous element or null if there is no link. */ + void setPreviousInWriteOrder(T prev); + + /** + * Retrieves the next element or null if either the element is unlinked or the last + * element on the deque. + */ + T getNextInWriteOrder(); + + /** Sets the next element or null if there is no link. */ + void setNextInWriteOrder(T next); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WriteThroughEntry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WriteThroughEntry.java new file mode 100644 index 0000000000000..c88496d3f5818 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/WriteThroughEntry.java @@ -0,0 +1,52 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; + +import java.util.AbstractMap.SimpleEntry; +import java.util.concurrent.ConcurrentMap; + +import static java.util.Objects.requireNonNull; + +/** + * An entry that allows updates to write through to the backing map. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +final class WriteThroughEntry extends SimpleEntry { + static final long serialVersionUID = 1; + + private final ConcurrentMap map; + + WriteThroughEntry(ConcurrentMap map, K key, V value) { + super(key, value); + this.map = requireNonNull(map); + } + + @Override + @SuppressWarnings("PMD.LinguisticNaming") + public V setValue(V value) { + map.put(getKey(), value); + return super.setValue(value); + } + + Object writeReplace() { + return new SimpleEntry<>(this); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/package-info.java new file mode 100644 index 0000000000000..7133a6ba25f5d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/CacheStats.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/CacheStats.java new file mode 100644 index 0000000000000..5e89867ca3b4f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/CacheStats.java @@ -0,0 +1,474 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache.stats; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.LoadingCache; + +import java.util.Objects; + +/** + * Statistics about the performance of a {@link Cache}. + * + *

Cache statistics are incremented according to the following rules: + * + *

    + *
  • When a cache lookup encounters an existing cache entry {@code hitCount} is incremented. + *
  • When a cache lookup first encounters a missing cache entry, a new entry is loaded. + *
      + *
    • After successfully loading an entry {@code missCount} and {@code loadSuccessCount} + * are incremented, and the total loading time, in nanoseconds, is added to {@code + * totalLoadTime}. + *
    • When an exception is thrown while loading an entry, {@code missCount} and {@code + * loadFailureCount} are incremented, and the total loading time, in nanoseconds, is + * added to {@code totalLoadTime}. + *
    • Cache lookups that encounter a missing cache entry that is still loading will wait + * for loading to complete (whether successful or not) and then increment {@code + * missCount}. + *
    + *
  • When an entry is computed through the {@linkplain Cache#asMap asMap} the {@code + * loadSuccessCount} or {@code loadFailureCount} is incremented. + *
  • When an entry is evicted from the cache, {@code evictionCount} is incremented and the + * weight added to {@code evictionWeight}. + *
  • No stats are modified when a cache entry is invalidated or manually removed. + *
  • No stats are modified by non-computing operations invoked on the {@linkplain Cache#asMap + * asMap} view of the cache. + *
+ * + *

A lookup is specifically defined as an invocation of one of the methods {@link + * LoadingCache#get(Object)}, {@link Cache#get(Object, java.util.function.Function)}, or {@link + * LoadingCache#getAll(Iterable)}. + * + *

This is a value-based class; use of identity-sensitive operations (including + * reference equality ({@code ==}), identity hash code, or synchronization) on instances of {@code + * CacheStats} may have unpredictable results and should be avoided. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public final class CacheStats { + private static final CacheStats EMPTY_STATS = CacheStats.of(0L, 0L, 0L, 0L, 0L, 0L, 0L); + + private final long hitCount; + private final long missCount; + private final long loadSuccessCount; + private final long loadFailureCount; + private final long totalLoadTime; + private final long evictionCount; + private final long evictionWeight; + + /** + * Constructs a new {@code CacheStats} instance. + * + * @param hitCount the number of cache hits + * @param missCount the number of cache misses + * @param loadSuccessCount the number of successful cache loads + * @param loadFailureCount the number of failed cache loads + * @param totalLoadTime the total load time (success and failure) + * @param evictionCount the number of entries evicted from the cache + * @deprecated This constructor is scheduled for removal in version 3.0.0. + */ + @Deprecated + public CacheStats( + long hitCount, + long missCount, + long loadSuccessCount, + long loadFailureCount, + long totalLoadTime, + long evictionCount) { + this(hitCount, missCount, loadSuccessCount, loadFailureCount, totalLoadTime, evictionCount, 0L); + } + + /** + * Constructs a new {@code CacheStats} instance. + * + *

Many parameters of the same type in a row is a bad thing, but this class is not constructed + * by end users and is too fine-grained for a builder. + * + * @param hitCount the number of cache hits + * @param missCount the number of cache misses + * @param loadSuccessCount the number of successful cache loads + * @param loadFailureCount the number of failed cache loads + * @param totalLoadTime the total load time (success and failure) + * @param evictionCount the number of entries evicted from the cache + * @param evictionWeight the sum of weights of entries evicted from the cache + */ + @Deprecated + public CacheStats( + long hitCount, + long missCount, + long loadSuccessCount, + long loadFailureCount, + long totalLoadTime, + long evictionCount, + long evictionWeight) { + if ((hitCount < 0) + || (missCount < 0) + || (loadSuccessCount < 0) + || (loadFailureCount < 0) + || (totalLoadTime < 0) + || (evictionCount < 0) + || (evictionWeight < 0)) { + throw new IllegalArgumentException(); + } + this.hitCount = hitCount; + this.missCount = missCount; + this.loadSuccessCount = loadSuccessCount; + this.loadFailureCount = loadFailureCount; + this.totalLoadTime = totalLoadTime; + this.evictionCount = evictionCount; + this.evictionWeight = evictionWeight; + } + + /** + * Returns a {@code CacheStats} representing the specified statistics. + * + * @param hitCount the number of cache hits + * @param missCount the number of cache misses + * @param loadSuccessCount the number of successful cache loads + * @param loadFailureCount the number of failed cache loads + * @param totalLoadTime the total load time (success and failure) + * @param evictionCount the number of entries evicted from the cache + * @param evictionWeight the sum of weights of entries evicted from the cache + */ + public static CacheStats of( + long hitCount, + long missCount, + long loadSuccessCount, + long loadFailureCount, + long totalLoadTime, + long evictionCount, + long evictionWeight) { + // Many parameters of the same type in a row is a bad thing, but this class is not constructed + // by end users and is too fine-grained for a builder. + return new CacheStats( + hitCount, + missCount, + loadSuccessCount, + loadFailureCount, + totalLoadTime, + evictionCount, + evictionWeight); + } + + /** + * Returns a statistics instance where no cache events have been recorded. + * + * @return an empty statistics instance + */ + public static CacheStats empty() { + return EMPTY_STATS; + } + + /** + * Returns the number of times {@link Cache} lookup methods have returned either a cached or + * uncached value. This is defined as {@code hitCount + missCount}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @return the {@code hitCount + missCount} + */ + public long requestCount() { + return saturatedAdd(hitCount, missCount); + } + + /** + * Returns the number of times {@link Cache} lookup methods have returned a cached value. + * + * @return the number of times {@link Cache} lookup methods have returned a cached value + */ + public long hitCount() { + return hitCount; + } + + /** + * Returns the ratio of cache requests which were hits. This is defined as {@code hitCount / + * requestCount}, or {@code 1.0} when {@code requestCount == 0}. Note that {@code hitRate + + * missRate =~ 1.0}. + * + * @return the ratio of cache requests which were hits + */ + public double hitRate() { + long requestCount = requestCount(); + return (requestCount == 0) ? 1.0 : (double) hitCount / requestCount; + } + + /** + * Returns the number of times {@link Cache} lookup methods have returned an uncached (newly + * loaded) value, or null. Multiple concurrent calls to {@link Cache} lookup methods on an absent + * value can result in multiple misses, all returning the results of a single cache load + * operation. + * + * @return the number of times {@link Cache} lookup methods have returned an uncached (newly + * loaded) value, or null + */ + public long missCount() { + return missCount; + } + + /** + * Returns the ratio of cache requests which were misses. This is defined as {@code missCount / + * requestCount}, or {@code 0.0} when {@code requestCount == 0}. Note that {@code hitRate + + * missRate =~ 1.0}. Cache misses include all requests which weren't cache hits, including + * requests which resulted in either successful or failed loading attempts, and requests which + * waited for other threads to finish loading. It is thus the case that {@code missCount >= + * loadSuccessCount + loadFailureCount}. Multiple concurrent misses for the same key will result + * in a single load operation. + * + * @return the ratio of cache requests which were misses + */ + public double missRate() { + long requestCount = requestCount(); + return (requestCount == 0) ? 0.0 : (double) missCount / requestCount; + } + + /** + * Returns the total number of times that {@link Cache} lookup methods attempted to load new + * values. This includes both successful load operations, as well as those that threw exceptions. + * This is defined as {@code loadSuccessCount + loadFailureCount}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @return the {@code loadSuccessCount + loadFailureCount} + */ + public long loadCount() { + return saturatedAdd(loadSuccessCount, loadFailureCount); + } + + /** + * Returns the number of times {@link Cache} lookup methods have successfully loaded a new value. + * This is always incremented in conjunction with {@link #missCount}, though {@code missCount} is + * also incremented when an exception is encountered during cache loading (see {@link + * #loadFailureCount}). Multiple concurrent misses for the same key will result in a single load + * operation. + * + * @return the number of times {@link Cache} lookup methods have successfully loaded a new value + */ + public long loadSuccessCount() { + return loadSuccessCount; + } + + /** + * Returns the number of times {@link Cache} lookup methods failed to load a new value, either + * because no value was found or an exception was thrown while loading. This is always incremented + * in conjunction with {@code missCount}, though {@code missCount} is also incremented when cache + * loading completes successfully (see {@link #loadSuccessCount}). Multiple concurrent misses for + * the same key will result in a single load operation. + * + * @return the number of times {@link Cache} lookup methods failed to load a new value + */ + public long loadFailureCount() { + return loadFailureCount; + } + + /** + * Returns the ratio of cache loading attempts which threw exceptions. This is defined as {@code + * loadFailureCount / (loadSuccessCount + loadFailureCount)}, or {@code 0.0} when {@code + * loadSuccessCount + loadFailureCount == 0}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @return the ratio of cache loading attempts which threw exceptions + */ + public double loadFailureRate() { + long totalLoadCount = saturatedAdd(loadSuccessCount, loadFailureCount); + return (totalLoadCount == 0) ? 0.0 : (double) loadFailureCount / totalLoadCount; + } + + /** + * Returns the total number of nanoseconds the cache has spent loading new values. This can be + * used to calculate the miss penalty. This value is increased every time {@code loadSuccessCount} + * or {@code loadFailureCount} is incremented. + * + * @return the total number of nanoseconds the cache has spent loading new values + */ + public long totalLoadTime() { + return totalLoadTime; + } + + /** + * Returns the average time spent loading new values. This is defined as {@code totalLoadTime / + * (loadSuccessCount + loadFailureCount)}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @return the average time spent loading new values + */ + public double averageLoadPenalty() { + long totalLoadCount = saturatedAdd(loadSuccessCount, loadFailureCount); + return (totalLoadCount == 0) ? 0.0 : (double) totalLoadTime / totalLoadCount; + } + + /** + * Returns the number of times an entry has been evicted. This count does not include manual + * {@linkplain Cache#invalidate invalidations}. + * + * @return the number of times an entry has been evicted + */ + public long evictionCount() { + return evictionCount; + } + + /** + * Returns the sum of weights of evicted entries. This total does not include manual {@linkplain + * Cache#invalidate invalidations}. + * + * @return the sum of weights of evicted entities + */ + public long evictionWeight() { + return evictionWeight; + } + + /** + * Returns a new {@code CacheStats} representing the difference between this {@code CacheStats} + * and {@code other}. Negative values, which aren't supported by {@code CacheStats} will be + * rounded up to zero. + * + * @param other the statistics to subtract with + * @return the difference between this instance and {@code other} + */ + public CacheStats minus(CacheStats other) { + return CacheStats.of( + Math.max(0L, saturatedSubtract(hitCount, other.hitCount)), + Math.max(0L, saturatedSubtract(missCount, other.missCount)), + Math.max(0L, saturatedSubtract(loadSuccessCount, other.loadSuccessCount)), + Math.max(0L, saturatedSubtract(loadFailureCount, other.loadFailureCount)), + Math.max(0L, saturatedSubtract(totalLoadTime, other.totalLoadTime)), + Math.max(0L, saturatedSubtract(evictionCount, other.evictionCount)), + Math.max(0L, saturatedSubtract(evictionWeight, other.evictionWeight))); + } + + /** + * Returns a new {@code CacheStats} representing the sum of this {@code CacheStats} and {@code + * other}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @param other the statistics to add with + * @return the sum of the statistics + */ + public CacheStats plus(CacheStats other) { + return CacheStats.of( + saturatedAdd(hitCount, other.hitCount), + saturatedAdd(missCount, other.missCount), + saturatedAdd(loadSuccessCount, other.loadSuccessCount), + saturatedAdd(loadFailureCount, other.loadFailureCount), + saturatedAdd(totalLoadTime, other.totalLoadTime), + saturatedAdd(evictionCount, other.evictionCount), + saturatedAdd(evictionWeight, other.evictionWeight)); + } + + /** + * Returns the difference of {@code a} and {@code b} unless it would overflow or underflow in + * which case {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively. + */ + @SuppressWarnings("ShortCircuitBoolean") + private static long saturatedSubtract(long a, long b) { + long naiveDifference = a - b; + if ((a ^ b) >= 0 | (a ^ naiveDifference) >= 0) { + // If a and b have the same signs or a has the same sign as the result then there was no + // overflow, return. + return naiveDifference; + } + // we did over/under flow + return Long.MAX_VALUE + ((naiveDifference >>> (Long.SIZE - 1)) ^ 1); + } + + /** + * Returns the sum of {@code a} and {@code b} unless it would overflow or underflow in which case + * {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively. + */ + @SuppressWarnings("ShortCircuitBoolean") + private static long saturatedAdd(long a, long b) { + long naiveSum = a + b; + if ((a ^ b) < 0 | (a ^ naiveSum) >= 0) { + // If a and b have different signs or a has the same sign as the result then there was no + // overflow, return. + return naiveSum; + } + // we did over/under flow, if the sign is negative we should return MAX otherwise MIN + return Long.MAX_VALUE + ((naiveSum >>> (Long.SIZE - 1)) ^ 1); + } + + @Override + public int hashCode() { + return Objects.hash( + hitCount, + missCount, + loadSuccessCount, + loadFailureCount, + totalLoadTime, + evictionCount, + evictionWeight); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (!(o instanceof CacheStats)) { + return false; + } + CacheStats other = (CacheStats) o; + return hitCount == other.hitCount + && missCount == other.missCount + && loadSuccessCount == other.loadSuccessCount + && loadFailureCount == other.loadFailureCount + && totalLoadTime == other.totalLoadTime + && evictionCount == other.evictionCount + && evictionWeight == other.evictionWeight; + } + + @Override + public String toString() { + return getClass().getSimpleName() + + '{' + + "hitCount=" + + hitCount + + ", " + + "missCount=" + + missCount + + ", " + + "loadSuccessCount=" + + loadSuccessCount + + ", " + + "loadFailureCount=" + + loadFailureCount + + ", " + + "totalLoadTime=" + + totalLoadTime + + ", " + + "evictionCount=" + + evictionCount + + ", " + + "evictionWeight=" + + evictionWeight + + '}'; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/ConcurrentStatsCounter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/ConcurrentStatsCounter.java new file mode 100644 index 0000000000000..fa14fe921e660 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/ConcurrentStatsCounter.java @@ -0,0 +1,132 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache.stats; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.RemovalCause; + +import java.util.concurrent.atomic.LongAdder; + +/** + * A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public final class ConcurrentStatsCounter implements StatsCounter { + private final LongAdder hitCount; + private final LongAdder missCount; + private final LongAdder loadSuccessCount; + private final LongAdder loadFailureCount; + private final LongAdder totalLoadTime; + private final LongAdder evictionCount; + private final LongAdder evictionWeight; + + /** Constructs an instance with all counts initialized to zero. */ + public ConcurrentStatsCounter() { + hitCount = new LongAdder(); + missCount = new LongAdder(); + loadSuccessCount = new LongAdder(); + loadFailureCount = new LongAdder(); + totalLoadTime = new LongAdder(); + evictionCount = new LongAdder(); + evictionWeight = new LongAdder(); + } + + @Override + public void recordHits(int count) { + hitCount.add(count); + } + + @Override + public void recordMisses(int count) { + missCount.add(count); + } + + @Override + public void recordLoadSuccess(long loadTime) { + loadSuccessCount.increment(); + totalLoadTime.add(loadTime); + } + + @Override + public void recordLoadFailure(long loadTime) { + loadFailureCount.increment(); + totalLoadTime.add(loadTime); + } + + @Override + @Deprecated + @SuppressWarnings("deprecation") + public void recordEviction() { + evictionCount.increment(); + } + + @Override + @Deprecated + @SuppressWarnings("deprecation") + public void recordEviction(int weight) { + evictionCount.increment(); + evictionWeight.add(weight); + } + + @Override + public void recordEviction(int weight, RemovalCause cause) { + evictionCount.increment(); + evictionWeight.add(weight); + } + + @Override + public CacheStats snapshot() { + return CacheStats.of( + negativeToMaxValue(hitCount.sum()), + negativeToMaxValue(missCount.sum()), + negativeToMaxValue(loadSuccessCount.sum()), + negativeToMaxValue(loadFailureCount.sum()), + negativeToMaxValue(totalLoadTime.sum()), + negativeToMaxValue(evictionCount.sum()), + negativeToMaxValue(evictionWeight.sum())); + } + + /** Returns {@code value}, if non-negative. Otherwise, returns {@link Long#MAX_VALUE}. */ + private static long negativeToMaxValue(long value) { + return (value >= 0) ? value : Long.MAX_VALUE; + } + + /** + * Increments all counters by the values in {@code other}. + * + * @param other the counter to increment from + */ + public void incrementBy(StatsCounter other) { + CacheStats otherStats = other.snapshot(); + hitCount.add(otherStats.hitCount()); + missCount.add(otherStats.missCount()); + loadSuccessCount.add(otherStats.loadSuccessCount()); + loadFailureCount.add(otherStats.loadFailureCount()); + totalLoadTime.add(otherStats.totalLoadTime()); + evictionCount.add(otherStats.evictionCount()); + evictionWeight.add(otherStats.evictionWeight()); + } + + @Override + public String toString() { + return snapshot().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/DisabledStatsCounter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/DisabledStatsCounter.java new file mode 100644 index 0000000000000..deb4963e1964b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/DisabledStatsCounter.java @@ -0,0 +1,55 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache.stats; + +/** + * A {@link StatsCounter} implementation that does not record any cache events. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +enum DisabledStatsCounter implements StatsCounter { + INSTANCE; + + @Override + public void recordHits(int count) {} + + @Override + public void recordMisses(int count) {} + + @Override + public void recordLoadSuccess(long loadTime) {} + + @Override + public void recordLoadFailure(long loadTime) {} + + @Override + @SuppressWarnings("deprecation") + public void recordEviction() {} + + @Override + public CacheStats snapshot() { + return CacheStats.empty(); + } + + @Override + public String toString() { + return snapshot().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/GuardedStatsCounter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/GuardedStatsCounter.java new file mode 100644 index 0000000000000..9207f7630fb7a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/GuardedStatsCounter.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache.stats; + +import org.apache.iotdb.github.benmanes.caffeine.cache.RemovalCause; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link StatsCounter} implementation that suppresses and logs any exception thrown by the + * delegate statsCounter. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +final class GuardedStatsCounter implements StatsCounter { + static final Logger logger = Logger.getLogger(GuardedStatsCounter.class.getName()); + + final StatsCounter delegate; + + GuardedStatsCounter(StatsCounter delegate) { + this.delegate = requireNonNull(delegate); + } + + @Override + public void recordHits(int count) { + try { + delegate.recordHits(count); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + public void recordMisses(int count) { + try { + delegate.recordMisses(count); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + public void recordLoadSuccess(long loadTime) { + try { + delegate.recordLoadSuccess(loadTime); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + public void recordLoadFailure(long loadTime) { + try { + delegate.recordLoadFailure(loadTime); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + @SuppressWarnings("deprecation") + public void recordEviction() { + try { + delegate.recordEviction(); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + @SuppressWarnings("deprecation") + public void recordEviction(int weight) { + try { + delegate.recordEviction(weight); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + public void recordEviction(int weight, RemovalCause cause) { + try { + delegate.recordEviction(weight, cause); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + } + } + + @Override + public CacheStats snapshot() { + try { + return delegate.snapshot(); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown by stats counter", t); + return CacheStats.empty(); + } + } + + @Override + public String toString() { + return delegate.toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/StatsCounter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/StatsCounter.java new file mode 100644 index 0000000000000..d19f11b416653 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/StatsCounter.java @@ -0,0 +1,146 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache.stats; + +import org.apache.iotdb.github.benmanes.caffeine.cache.Cache; +import org.apache.iotdb.github.benmanes.caffeine.cache.RemovalCause; + +import java.util.Map; + +/** + * Accumulates statistics during the operation of a {@link Cache} for presentation by {@link + * Cache#stats}. This is solely intended for consumption by {@code Cache} implementors. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public interface StatsCounter { + + /** + * Records cache hits. This should be called when a cache request returns a cached value. + * + * @param count the number of hits to record + */ + void recordHits(int count); + + /** + * Records cache misses. This should be called when a cache request returns a value that was not + * found in the cache. This method should be called by the loading thread, as well as by threads + * blocking on the load. Multiple concurrent calls to {@link Cache} lookup methods with the same + * key on an absent value should result in a single call to either {@code recordLoadSuccess} or + * {@code recordLoadFailure} and multiple calls to this method, despite all being served by the + * results of a single load operation. + * + * @param count the number of misses to record + */ + void recordMisses(int count); + + /** + * Records the successful load of a new entry. This method should be called when a cache request + * causes an entry to be loaded (such as by {@link Cache#get} or {@link Map#computeIfAbsent}) and + * the loading completes successfully. In contrast to {@link #recordMisses}, this method should + * only be called by the loading thread. + * + * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new value + */ + void recordLoadSuccess(long loadTime); + + /** + * Records the failed load of a new entry. This method should be called when a cache request + * causes an entry to be loaded (such as by {@link Cache#get} or {@link Map#computeIfAbsent}), but + * an exception is thrown while loading the entry or the loading function returns null. In + * contrast to {@link #recordMisses}, this method should only be called by the loading thread. + * + * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new value + * prior to discovering the value doesn't exist or an exception being thrown + */ + void recordLoadFailure(long loadTime); + + /** + * Records the eviction of an entry from the cache. This should only been called when an entry is + * evicted due to the cache's eviction strategy, and not as a result of manual {@link + * Cache#invalidate invalidations}. + * + * @deprecated Use {@link StatsCounter#recordEviction(int, RemovalCause)} instead. This method is + * scheduled for removal in version 3.0.0. + */ + @Deprecated + void recordEviction(); + + /** + * Records the eviction of an entry from the cache. This should only been called when an entry is + * evicted due to the cache's eviction strategy, and not as a result of manual {@link + * Cache#invalidate invalidations}. + * + * @param weight the weight of the evicted entry + * @deprecated Use {@link StatsCounter#recordEviction(int, RemovalCause)} instead. This method is + * scheduled for removal in version 3.0.0. + */ + @Deprecated + default void recordEviction(int weight) { + recordEviction(); + } + + /** + * Records the eviction of an entry from the cache. This should only been called when an entry is + * evicted due to the cache's eviction strategy, and not as a result of manual {@link + * Cache#invalidate invalidations}. + * + * @param weight the weight of the evicted entry + * @param cause the reason for which the entry was removed + */ + default void recordEviction(int weight, RemovalCause cause) { + // This method will be abstract in version 3.0.0 + recordEviction(weight); + } + + /** + * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as it + * may be interleaved with update operations. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @return a snapshot of this counter's values + */ + CacheStats snapshot(); + + /** + * Returns an accumulator that does not record any cache events. + * + * @return an accumulator that does not record metrics + */ + static StatsCounter disabledStatsCounter() { + return DisabledStatsCounter.INSTANCE; + } + + /** + * Returns an accumulator that suppresses and logs any exception thrown by the delegate {@code + * statsCounter}. + * + * @param statsCounter the accumulator to delegate to + * @return an accumulator that suppresses and logs any exception thrown by the delegate + */ + static StatsCounter guardedStatsCounter(StatsCounter statsCounter) { + return (statsCounter instanceof GuardedStatsCounter) + ? statsCounter + : new GuardedStatsCounter(statsCounter); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/package-info.java new file mode 100644 index 0000000000000..91c74b98e8089 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/cache/stats/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.github.benmanes.caffeine.cache.stats; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/package-info.java new file mode 100644 index 0000000000000..cdf2f3265eaee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/github/benmanes/caffeine/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.github.benmanes.caffeine; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/Beta.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/Beta.java new file mode 100644 index 0000000000000..4f07a6c0f1dbf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/Beta.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Signifies that a public API (public class, method or field) is subject to incompatible changes, + * or even removal, in a future release. An API bearing this annotation is exempt from any + * compatibility guarantees made by its containing library. Note that the presence of this + * annotation implies nothing about the quality or performance of the API in question, only the fact + * that it is not "API-frozen." + * + *

It is generally safe for applications to depend on beta APIs, at the cost of some extra + * work during upgrades. However it is generally inadvisable for libraries (which get + * included on users' CLASSPATHs, outside the library developers' control) to do so. + * + * @author Kevin Bourrillion + */ +@Retention(RetentionPolicy.CLASS) +@Target({ + ElementType.ANNOTATION_TYPE, + ElementType.CONSTRUCTOR, + ElementType.FIELD, + ElementType.METHOD, + ElementType.TYPE +}) +@Documented +@GwtCompatible +public @interface Beta {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/GwtCompatible.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/GwtCompatible.java new file mode 100644 index 0000000000000..61d69a23d33b2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/GwtCompatible.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The presence of this annotation on a type indicates that the type may be used with the Google Web Toolkit (GWT). When applied to a method, + * the return type of the method is GWT compatible. It's useful to indicate that an instance created + * by factory methods has a GWT serializable type. In the following example, + * + *

+ * {@literal @}GwtCompatible
+ * class Lists {
+ *   ...
+ *   {@literal @}GwtCompatible(serializable = true)
+ *   {@literal static  List} newArrayList(E... elements) {
+ *     ...
+ *   }
+ * }
+ * 
+ * + *

The return value of {@code Lists.newArrayList(E[])} has GWT serializable type. It is also + * useful in specifying contracts of interface methods. In the following example, + * + *

+ * {@literal @}GwtCompatible
+ * interface ListFactory {
+ *   ...
+ *   {@literal @}GwtCompatible(serializable = true)
+ *   {@literal  List} newArrayList(E... elements);
+ * }
+ * 
+ * + *

The {@code newArrayList(E[])} method of all implementations of {@code ListFactory} is expected + * to return a value with a GWT serializable type. + * + *

Note that a {@code GwtCompatible} type may have some {@link GwtIncompatible} methods. + * + * @author Charles Fry + * @author Hayward Chan + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.METHOD}) +@Documented +@GwtCompatible +public @interface GwtCompatible { + + /** + * When {@code true}, the annotated type or the type of the method return value is GWT + * serializable. + * + * @see + * Documentation about GWT serialization + */ + boolean serializable() default false; + + /** + * When {@code true}, the annotated type is emulated in GWT. The emulated source (also known as + * super-source) is different from the implementation used by the JVM. + * + * @see + * Documentation about GWT emulated source + */ + boolean emulated() default false; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/GwtIncompatible.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/GwtIncompatible.java new file mode 100644 index 0000000000000..72cf9db49f106 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/GwtIncompatible.java @@ -0,0 +1,52 @@ +/* + * 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.iotdb.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The presence of this annotation on an API indicates that the method may not be used with + * the Google Web Toolkit (GWT). + * + *

This annotation behaves identically to the + * {@code @GwtIncompatible} annotation in GWT itself. + * + * @author Charles Fry + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) +@Documented +@GwtCompatible +public @interface GwtIncompatible { + /** + * Describes why the annotated element is incompatible with GWT. Since this is generally due to a + * dependence on a type/method which GWT doesn't support, it is sufficient to simply reference the + * unsupported type/method. E.g. "Class.isInstance". + * + *

As of Guava 20.0, this value is optional. We encourage authors who wish to describe why an + * API is {@code @GwtIncompatible} to instead leave an implementation comment. + */ + String value() default ""; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/J2ktIncompatible.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/J2ktIncompatible.java new file mode 100644 index 0000000000000..2d3fecd23f727 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/J2ktIncompatible.java @@ -0,0 +1,36 @@ +/* + * 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.iotdb.google.common.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The presence of this annotation on an API indicates that the method may not be used with + * J2kt. + * + * @since 32.0.0 + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) +@GwtCompatible +public @interface J2ktIncompatible {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/VisibleForTesting.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/VisibleForTesting.java new file mode 100644 index 0000000000000..b1b465887cbdd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/VisibleForTesting.java @@ -0,0 +1,36 @@ +/* + * 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.iotdb.google.common.annotations; + +/** + * Annotates a program element that exists, or is more widely visible than otherwise necessary, only + * for use in test code. + * + *

Do not use this interface for public or protected declarations: it is a fig leaf for + * bad design, and it does not prevent anyone from using the declaration---and experience has shown + * that they will. If the method breaks the encapsulation of its class, then its internal + * representation will be hard to change. Instead, use RestrictedApiChecker, which enforces + * fine-grained visibility policies. + * + * @author Johannes Henkel + */ +@GwtCompatible +public @interface VisibleForTesting {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/package-info.java new file mode 100644 index 0000000000000..00b81a4cdaee9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/annotations/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.annotations; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Absent.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Absent.java new file mode 100644 index 0000000000000..7eb2ef6b8392a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Absent.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collections; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** Implementation of an {@link Optional} not containing a reference. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class Absent extends Optional { + static final Absent INSTANCE = new Absent<>(); + + @SuppressWarnings("unchecked") // implementation is "fully variant" + static Optional withType() { + return (Optional) INSTANCE; + } + + private Absent() {} + + @Override + public boolean isPresent() { + return false; + } + + @Override + public T get() { + throw new IllegalStateException("Optional.get() cannot be called on an absent value"); + } + + @Override + public T or(T defaultValue) { + return checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); + } + + @SuppressWarnings("unchecked") // safe covariant cast + @Override + public Optional or(Optional secondChoice) { + return (Optional) checkNotNull(secondChoice); + } + + @Override + public T or(Supplier supplier) { + return checkNotNull( + supplier.get(), "use Optional.orNull() instead of a Supplier that returns null"); + } + + @Override + public T orNull() { + return null; + } + + @Override + public Set asSet() { + return Collections.emptySet(); + } + + @Override + public Optional transform(Function function) { + checkNotNull(function); + return Optional.absent(); + } + + @Override + public boolean equals(Object object) { + return object == this; + } + + @Override + public int hashCode() { + return 0x79a31aac; + } + + @Override + public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/AbstractIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/AbstractIterator.java new file mode 100644 index 0000000000000..a0b92edf7f555 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/AbstractIterator.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import static org.apache.iotdb.google.common.base.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * Note this class is a copy of {@link org.apache.iotdb.google.common.collect.AbstractIterator} (for + * dependency reasons). + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractIterator implements Iterator { + private State state = State.NOT_READY; + + protected AbstractIterator() {} + + private enum State { + READY, + NOT_READY, + DONE, + FAILED, + } + + private T next; + + protected abstract T computeNext(); + + protected final T endOfData() { + state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + checkState(state != State.FAILED); + switch (state) { + case DONE: + return false; + case READY: + return true; + default: + } + return tryToComputeNext(); + } + + private boolean tryToComputeNext() { + state = State.FAILED; // temporary pessimism + next = computeNext(); + if (state != State.DONE) { + state = State.READY; + return true; + } + return false; + } + + @Override + @ParametricNullness + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + state = State.NOT_READY; + // Safe because hasNext() ensures that tryToComputeNext() has put a T into `next`. + T result = uncheckedCastNullableTToT(next); + next = null; + return result; + } + + @Override + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Ascii.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Ascii.java new file mode 100644 index 0000000000000..9d493ea02fbec --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Ascii.java @@ -0,0 +1,640 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static methods pertaining to ASCII characters (those in the range of values {@code 0x00} through + * {@code 0x7F}), and to strings containing such characters. + * + *

ASCII utilities also exist in other classes of this package: + * + *

    + * + *
  • {@link Charsets#US_ASCII} specifies the {@code Charset} of ASCII characters. + *
  • {@link CharMatcher#ascii} matches ASCII characters and provides text processing methods + * which operate only on the ASCII characters of a string. + *
+ * + * @author Catherine Berry + * @author Gregory Kick + * @since 7.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Ascii { + + private Ascii() {} + + /* The ASCII control characters, per RFC 20. */ + /** + * Null ('\0'): The all-zeros character which may serve to accomplish time fill and media fill. + * Normally used as a C string terminator. + * + *

Although RFC 20 names this as "Null", note that it is distinct from the C/C++ "NULL" + * pointer. + * + * @since 8.0 + */ + public static final byte NUL = 0; + + /** + * Start of Heading: A communication control character used at the beginning of a sequence of + * characters which constitute a machine-sensible address or routing information. Such a sequence + * is referred to as the "heading." An STX character has the effect of terminating a heading. + * + * @since 8.0 + */ + public static final byte SOH = 1; + + /** + * Start of Text: A communication control character which precedes a sequence of characters that + * is to be treated as an entity and entirely transmitted through to the ultimate destination. + * Such a sequence is referred to as "text." STX may be used to terminate a sequence of characters + * started by SOH. + * + * @since 8.0 + */ + public static final byte STX = 2; + + /** + * End of Text: A communication control character used to terminate a sequence of characters + * started with STX and transmitted as an entity. + * + * @since 8.0 + */ + public static final byte ETX = 3; + + /** + * End of Transmission: A communication control character used to indicate the conclusion of a + * transmission, which may have contained one or more texts and any associated headings. + * + * @since 8.0 + */ + public static final byte EOT = 4; + + /** + * Enquiry: A communication control character used in data communication systems as a request for + * a response from a remote station. It may be used as a "Who Are You" (WRU) to obtain + * identification, or may be used to obtain station status, or both. + * + * @since 8.0 + */ + public static final byte ENQ = 5; + + /** + * Acknowledge: A communication control character transmitted by a receiver as an affirmative + * response to a sender. + * + * @since 8.0 + */ + public static final byte ACK = 6; + + /** + * Bell ('\a'): A character for use when there is a need to call for human attention. It may + * control alarm or attention devices. + * + * @since 8.0 + */ + public static final byte BEL = 7; + + /** + * Backspace ('\b'): A format effector which controls the movement of the printing position one + * printing space backward on the same printing line. (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte BS = 8; + + /** + * Horizontal Tabulation ('\t'): A format effector which controls the movement of the printing + * position to the next in a series of predetermined positions along the printing line. + * (Applicable also to display devices and the skip function on punched cards.) + * + * @since 8.0 + */ + public static final byte HT = 9; + + /** + * Line Feed ('\n'): A format effector which controls the movement of the printing position to the + * next printing line. (Applicable also to display devices.) Where appropriate, this character may + * have the meaning "New Line" (NL), a format effector which controls the movement of the printing + * point to the first printing position on the next printing line. Use of this convention requires + * agreement between sender and recipient of data. + * + * @since 8.0 + */ + public static final byte LF = 10; + + /** + * Alternate name for {@link #LF}. ({@code LF} is preferred.) + * + * @since 8.0 + */ + public static final byte NL = 10; + + /** + * Vertical Tabulation ('\v'): A format effector which controls the movement of the printing + * position to the next in a series of predetermined printing lines. (Applicable also to display + * devices.) + * + * @since 8.0 + */ + public static final byte VT = 11; + + /** + * Form Feed ('\f'): A format effector which controls the movement of the printing position to the + * first pre-determined printing line on the next form or page. (Applicable also to display + * devices.) + * + * @since 8.0 + */ + public static final byte FF = 12; + + /** + * Carriage Return ('\r'): A format effector which controls the movement of the printing position + * to the first printing position on the same printing line. (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte CR = 13; + + /** + * Shift Out: A control character indicating that the code combinations which follow shall be + * interpreted as outside of the character set of the standard code table until a Shift In + * character is reached. + * + * @since 8.0 + */ + public static final byte SO = 14; + + /** + * Shift In: A control character indicating that the code combinations which follow shall be + * interpreted according to the standard code table. + * + * @since 8.0 + */ + public static final byte SI = 15; + + /** + * Data Link Escape: A communication control character which will change the meaning of a limited + * number of contiguously following characters. It is used exclusively to provide supplementary + * controls in data communication networks. + * + * @since 8.0 + */ + public static final byte DLE = 16; + + /** + * Device Control 1. Characters for the control of ancillary devices associated with data + * processing or telecommunication systems, more especially switching devices "on" or "off." (If a + * single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the + * preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC1 = 17; // aka XON + + /** + * Transmission On: Although originally defined as DC1, this ASCII control character is now better + * known as the XON code used for software flow control in serial communications. The main use is + * restarting the transmission after the communication has been stopped by the XOFF control code. + * + * @since 8.0 + */ + public static final byte XON = 17; // aka DC1 + + /** + * Device Control 2. Characters for the control of ancillary devices associated with data + * processing or telecommunication systems, more especially switching devices "on" or "off." (If a + * single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the + * preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC2 = 18; + + /** + * Device Control 3. Characters for the control of ancillary devices associated with data + * processing or telecommunication systems, more especially switching devices "on" or "off." (If a + * single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the + * preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC3 = 19; // aka XOFF + + /** + * Transmission off. See {@link #XON} for explanation. + * + * @since 8.0 + */ + public static final byte XOFF = 19; // aka DC3 + + /** + * Device Control 4. Characters for the control of ancillary devices associated with data + * processing or telecommunication systems, more especially switching devices "on" or "off." (If a + * single "stop" control is required to interrupt or turn off ancillary devices, DC4 is the + * preferred assignment.) + * + * @since 8.0 + */ + public static final byte DC4 = 20; + + /** + * Negative Acknowledge: A communication control character transmitted by a receiver as a negative + * response to the sender. + * + * @since 8.0 + */ + public static final byte NAK = 21; + + /** + * Synchronous Idle: A communication control character used by a synchronous transmission system + * in the absence of any other character to provide a signal from which synchronism may be + * achieved or retained. + * + * @since 8.0 + */ + public static final byte SYN = 22; + + /** + * End of Transmission Block: A communication control character used to indicate the end of a + * block of data for communication purposes. ETB is used for blocking data where the block + * structure is not necessarily related to the processing format. + * + * @since 8.0 + */ + public static final byte ETB = 23; + + /** + * Cancel: A control character used to indicate that the data with which it is sent is in error or + * is to be disregarded. + * + * @since 8.0 + */ + public static final byte CAN = 24; + + /** + * End of Medium: A control character associated with the sent data which may be used to identify + * the physical end of the medium, or the end of the used, or wanted, portion of information + * recorded on a medium. (The position of this character does not necessarily correspond to the + * physical end of the medium.) + * + * @since 8.0 + */ + public static final byte EM = 25; + + /** + * Substitute: A character that may be substituted for a character which is determined to be + * invalid or in error. + * + * @since 8.0 + */ + public static final byte SUB = 26; + + /** + * Escape: A control character intended to provide code extension (supplementary characters) in + * general information interchange. The Escape character itself is a prefix affecting the + * interpretation of a limited number of contiguously following characters. + * + * @since 8.0 + */ + public static final byte ESC = 27; + + /** + * File Separator: These four information separators may be used within data in optional fashion, + * except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then + * RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are + * not specified.) + * + * @since 8.0 + */ + public static final byte FS = 28; + + /** + * Group Separator: These four information separators may be used within data in optional fashion, + * except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then + * RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are + * not specified.) + * + * @since 8.0 + */ + public static final byte GS = 29; + + /** + * Record Separator: These four information separators may be used within data in optional + * fashion, except that their hierarchical relationship shall be: FS is the most inclusive, then + * GS, then RS, and US is least inclusive. (The content and length of a File, Group, Record, or + * Unit are not specified.) + * + * @since 8.0 + */ + public static final byte RS = 30; + + /** + * Unit Separator: These four information separators may be used within data in optional fashion, + * except that their hierarchical relationship shall be: FS is the most inclusive, then GS, then + * RS, and US is least inclusive. (The content and length of a File, Group, Record, or Unit are + * not specified.) + * + * @since 8.0 + */ + public static final byte US = 31; + + /** + * Space: A normally non-printing graphic character used to separate words. It is also a format + * effector which controls the movement of the printing position, one printing position forward. + * (Applicable also to display devices.) + * + * @since 8.0 + */ + public static final byte SP = 32; + + /** + * Alternate name for {@link #SP}. + * + * @since 8.0 + */ + public static final byte SPACE = 32; + + /** + * Delete: This character is used primarily to "erase" or "obliterate" erroneous or unwanted + * characters in perforated tape. + * + * @since 8.0 + */ + public static final byte DEL = 127; + + /** + * The minimum value of an ASCII character. + * + * @since 9.0 (was type {@code int} before 12.0) + */ + public static final char MIN = 0; + + /** + * The maximum value of an ASCII character. + * + * @since 9.0 (was type {@code int} before 12.0) + */ + public static final char MAX = 127; + + /** A bit mask which selects the bit encoding ASCII character case. */ + private static final char CASE_MASK = 0x20; + + /** + * Returns a copy of the input string in which all {@linkplain #isUpperCase(char) uppercase ASCII + * characters} have been converted to lowercase. All other characters are copied without + * modification. + */ + public static String toLowerCase(String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + if (isUpperCase(string.charAt(i))) { + char[] chars = string.toCharArray(); + for (; i < length; i++) { + char c = chars[i]; + if (isUpperCase(c)) { + chars[i] = (char) (c ^ CASE_MASK); + } + } + return String.valueOf(chars); + } + } + return string; + } + + /** + * Returns a copy of the input character sequence in which all {@linkplain #isUpperCase(char) + * uppercase ASCII characters} have been converted to lowercase. All other characters are copied + * without modification. + * + * @since 14.0 + */ + public static String toLowerCase(CharSequence chars) { + if (chars instanceof String) { + return toLowerCase((String) chars); + } + char[] newChars = new char[chars.length()]; + for (int i = 0; i < newChars.length; i++) { + newChars[i] = toLowerCase(chars.charAt(i)); + } + return String.valueOf(newChars); + } + + /** + * If the argument is an {@linkplain #isUpperCase(char) uppercase ASCII character}, returns the + * lowercase equivalent. Otherwise returns the argument. + */ + public static char toLowerCase(char c) { + return isUpperCase(c) ? (char) (c ^ CASE_MASK) : c; + } + + /** + * Returns a copy of the input string in which all {@linkplain #isLowerCase(char) lowercase ASCII + * characters} have been converted to uppercase. All other characters are copied without + * modification. + */ + public static String toUpperCase(String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + if (isLowerCase(string.charAt(i))) { + char[] chars = string.toCharArray(); + for (; i < length; i++) { + char c = chars[i]; + if (isLowerCase(c)) { + chars[i] = (char) (c ^ CASE_MASK); + } + } + return String.valueOf(chars); + } + } + return string; + } + + /** + * Returns a copy of the input character sequence in which all {@linkplain #isLowerCase(char) + * lowercase ASCII characters} have been converted to uppercase. All other characters are copied + * without modification. + * + * @since 14.0 + */ + public static String toUpperCase(CharSequence chars) { + if (chars instanceof String) { + return toUpperCase((String) chars); + } + char[] newChars = new char[chars.length()]; + for (int i = 0; i < newChars.length; i++) { + newChars[i] = toUpperCase(chars.charAt(i)); + } + return String.valueOf(newChars); + } + + /** + * If the argument is a {@linkplain #isLowerCase(char) lowercase ASCII character}, returns the + * uppercase equivalent. Otherwise returns the argument. + */ + public static char toUpperCase(char c) { + return isLowerCase(c) ? (char) (c ^ CASE_MASK) : c; + } + + /** + * Indicates whether {@code c} is one of the twenty-six lowercase ASCII alphabetic characters + * between {@code 'a'} and {@code 'z'} inclusive. All others (including non-ASCII characters) + * return {@code false}. + */ + public static boolean isLowerCase(char c) { + // Note: This was benchmarked against the alternate expression "(char)(c - 'a') < 26" (Nov '13) + // and found to perform at least as well, or better. + return (c >= 'a') && (c <= 'z'); + } + + /** + * Indicates whether {@code c} is one of the twenty-six uppercase ASCII alphabetic characters + * between {@code 'A'} and {@code 'Z'} inclusive. All others (including non-ASCII characters) + * return {@code false}. + */ + public static boolean isUpperCase(char c) { + return (c >= 'A') && (c <= 'Z'); + } + + /** + * Truncates the given character sequence to the given maximum length. If the length of the + * sequence is greater than {@code maxLength}, the returned string will be exactly {@code + * maxLength} chars in length and will end with the given {@code truncationIndicator}. Otherwise, + * the sequence will be returned as a string with no changes to the content. + * + *

Examples: + * + *

{@code
+   * Ascii.truncate("foobar", 7, "..."); // returns "foobar"
+   * Ascii.truncate("foobar", 5, "..."); // returns "fo..."
+   * }
+ * + *

Note: This method may work with certain non-ASCII text but is not safe for use + * with arbitrary Unicode text. It is mostly intended for use with text that is known to be safe + * for use with it (such as all-ASCII text) and for simple debugging text. When using this method, + * consider the following: + * + *

    + *
  • it may split surrogate pairs + *
  • it may split characters and combining characters + *
  • it does not consider word boundaries + *
  • if truncating for display to users, there are other considerations that must be taken + * into account + *
  • the appropriate truncation indicator may be locale-dependent + *
  • it is safe to use non-ASCII characters in the truncation indicator + *
+ * + * @throws IllegalArgumentException if {@code maxLength} is less than the length of {@code + * truncationIndicator} + * @since 16.0 + */ + public static String truncate(CharSequence seq, int maxLength, String truncationIndicator) { + checkNotNull(seq); + + // length to truncate the sequence to, not including the truncation indicator + int truncationLength = maxLength - truncationIndicator.length(); + + // in this worst case, this allows a maxLength equal to the length of the truncationIndicator, + // meaning that a string will be truncated to just the truncation indicator itself + checkArgument( + truncationLength >= 0, + "maxLength (%s) must be >= length of the truncation indicator (%s)", + maxLength, + truncationIndicator.length()); + + if (seq.length() <= maxLength) { + String string = seq.toString(); + if (string.length() <= maxLength) { + return string; + } + // if the length of the toString() result was > maxLength for some reason, truncate that + seq = string; + } + + return new StringBuilder(maxLength) + .append(seq, 0, truncationLength) + .append(truncationIndicator) + .toString(); + } + + /** + * Indicates whether the contents of the given character sequences {@code s1} and {@code s2} are + * equal, ignoring the case of any ASCII alphabetic characters between {@code 'a'} and {@code 'z'} + * or {@code 'A'} and {@code 'Z'} inclusive. + * + *

This method is significantly faster than {@link String#equalsIgnoreCase} and should be used + * in preference if at least one of the parameters is known to contain only ASCII characters. + * + *

Note however that this method does not always behave identically to expressions such as: + * + *

    + *
  • {@code string.toUpperCase().equals("UPPER CASE ASCII")} + *
  • {@code string.toLowerCase().equals("lower case ascii")} + *
+ * + *

due to case-folding of some non-ASCII characters (which does not occur in {@link + * String#equalsIgnoreCase}). However in almost all cases that ASCII strings are used, the author + * probably wanted the behavior provided by this method rather than the subtle and sometimes + * surprising behavior of {@code toUpperCase()} and {@code toLowerCase()}. + * + * @since 16.0 + */ + public static boolean equalsIgnoreCase(CharSequence s1, CharSequence s2) { + // Calling length() is the null pointer check (so do it before we can exit early). + int length = s1.length(); + if (s1 == s2) { + return true; + } + if (length != s2.length()) { + return false; + } + for (int i = 0; i < length; i++) { + char c1 = s1.charAt(i); + char c2 = s2.charAt(i); + if (c1 == c2) { + continue; + } + int alphaIndex = getAlphaIndex(c1); + // This was also benchmarked using '&' to avoid branching (but always evaluate the rhs), + // however this showed no obvious improvement. + if (alphaIndex < 26 && alphaIndex == getAlphaIndex(c2)) { + continue; + } + return false; + } + return true; + } + + /** + * Returns the non-negative index value of the alpha character {@code c}, regardless of case. Ie, + * 'a'/'A' returns 0 and 'z'/'Z' returns 25. Non-alpha characters return a value of 26 or greater. + */ + private static int getAlphaIndex(char c) { + // Fold upper-case ASCII to lower-case and make zero-indexed and unsigned (by casting to char). + return (char) ((c | CASE_MASK) - 'a'); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CaseFormat.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CaseFormat.java new file mode 100644 index 0000000000000..1648f5be68f71 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CaseFormat.java @@ -0,0 +1,222 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Utility class for converting between various ASCII case formats. Behavior is undefined for + * non-ASCII input. + * + * @author Mike Bostock + * @since 1.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public enum CaseFormat { + /** Hyphenated variable naming convention, e.g., "lower-hyphen". */ + LOWER_HYPHEN(CharMatcher.is('-'), "-") { + @Override + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_UNDERSCORE) { + return s.replace('-', '_'); + } + if (format == UPPER_UNDERSCORE) { + return Ascii.toUpperCase(s.replace('-', '_')); + } + return super.convert(format, s); + } + }, + + /** C++ variable naming convention, e.g., "lower_underscore". */ + LOWER_UNDERSCORE(CharMatcher.is('_'), "_") { + @Override + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return s.replace('_', '-'); + } + if (format == UPPER_UNDERSCORE) { + return Ascii.toUpperCase(s); + } + return super.convert(format, s); + } + }, + + /** Java variable naming convention, e.g., "lowerCamel". */ + LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + @Override + String normalizeWord(String word) { + return firstCharOnlyToUpper(word); + } + + @Override + String normalizeFirstWord(String word) { + return Ascii.toLowerCase(word); + } + }, + + /** Java and C++ class naming convention, e.g., "UpperCamel". */ + UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + @Override + String normalizeWord(String word) { + return firstCharOnlyToUpper(word); + } + }, + + /** Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". */ + UPPER_UNDERSCORE(CharMatcher.is('_'), "_") { + @Override + String normalizeWord(String word) { + return Ascii.toUpperCase(word); + } + + @Override + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return Ascii.toLowerCase(s.replace('_', '-')); + } + if (format == LOWER_UNDERSCORE) { + return Ascii.toLowerCase(s); + } + return super.convert(format, s); + } + }; + + private final CharMatcher wordBoundary; + private final String wordSeparator; + + CaseFormat(CharMatcher wordBoundary, String wordSeparator) { + this.wordBoundary = wordBoundary; + this.wordSeparator = wordSeparator; + } + + /** + * Converts the specified {@code String str} from this format to the specified {@code format}. A + * "best effort" approach is taken; if {@code str} does not conform to the assumed format, then + * the behavior of this method is undefined but we make a reasonable effort at converting anyway. + */ + public final String to(CaseFormat format, String str) { + checkNotNull(format); + checkNotNull(str); + return (format == this) ? str : convert(format, str); + } + + /** Enum values can override for performance reasons. */ + String convert(CaseFormat format, String s) { + // deal with camel conversion + StringBuilder out = null; + int i = 0; + int j = -1; + while ((j = wordBoundary.indexIn(s, ++j)) != -1) { + if (i == 0) { + // include some extra space for separators + out = new StringBuilder(s.length() + 4 * format.wordSeparator.length()); + out.append(format.normalizeFirstWord(s.substring(i, j))); + } else { + requireNonNull(out).append(format.normalizeWord(s.substring(i, j))); + } + out.append(format.wordSeparator); + i = j + wordSeparator.length(); + } + return (i == 0) + ? format.normalizeFirstWord(s) + : requireNonNull(out).append(format.normalizeWord(s.substring(i))).toString(); + } + + /** + * Returns a serializable {@code Converter} that converts strings from this format to {@code + * targetFormat}. + * + * @since 16.0 + */ + public Converter converterTo(CaseFormat targetFormat) { + return new StringConverter(this, targetFormat); + } + + private static final class StringConverter extends Converter + implements Serializable { + + private final CaseFormat sourceFormat; + private final CaseFormat targetFormat; + + StringConverter(CaseFormat sourceFormat, CaseFormat targetFormat) { + this.sourceFormat = checkNotNull(sourceFormat); + this.targetFormat = checkNotNull(targetFormat); + } + + @Override + protected String doForward(String s) { + return sourceFormat.to(targetFormat, s); + } + + @Override + protected String doBackward(String s) { + return targetFormat.to(sourceFormat, s); + } + + @Override + public boolean equals(Object object) { + if (object instanceof StringConverter) { + StringConverter that = (StringConverter) object; + return sourceFormat.equals(that.sourceFormat) && targetFormat.equals(that.targetFormat); + } + return false; + } + + @Override + public int hashCode() { + return sourceFormat.hashCode() ^ targetFormat.hashCode(); + } + + @Override + public String toString() { + return sourceFormat + ".converterTo(" + targetFormat + ")"; + } + + private static final long serialVersionUID = 0L; + } + + abstract String normalizeWord(String word); + + String normalizeFirstWord(String word) { + return normalizeWord(word); + } + + private static String firstCharOnlyToUpper(String word) { + return word.isEmpty() + ? word + : Ascii.toUpperCase(word.charAt(0)) + Ascii.toLowerCase(word.substring(1)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CharMatcher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CharMatcher.java new file mode 100644 index 0000000000000..2429e47df3b08 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CharMatcher.java @@ -0,0 +1,1840 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.Arrays; +import java.util.BitSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; + +/** + * Determines a true or false value for any Java {@code char} value, just as {@link Predicate} does + * for any {@link Object}. Also offers basic text processing methods based on this function. + * Implementations are strongly encouraged to be side-effect-free and immutable. + * + *

Throughout the documentation of this class, the phrase "matching character" is used to mean + * "any {@code char} value {@code c} for which {@code this.matches(c)} returns {@code true}". + * + *

Warning: This class deals only with {@code char} values, that is, BMP characters. It does not understand + * supplementary Unicode code + * points in the range {@code 0x10000} to {@code 0x10FFFF} which includes the majority of + * assigned characters, including important CJK characters and emoji. + * + *

Supplementary characters are encoded + * into a {@code String} using surrogate pairs, and a {@code CharMatcher} treats these just as + * two separate characters. {@link #countIn} counts each supplementary character as 2 {@code char}s. + * + *

For up-to-date Unicode character properties (digit, letter, etc.) and support for + * supplementary code points, use ICU4J UCharacter and UnicodeSet (freeze() after building). For + * basic text processing based on UnicodeSet use the ICU4J UnicodeSetSpanner. + * + *

Example usages: + * + *

+ *   String trimmed = {@link #whitespace() whitespace()}.{@link #trimFrom trimFrom}(userInput);
+ *   if ({@link #ascii() ascii()}.{@link #matchesAllOf matchesAllOf}(s)) { ... }
+ * + *

See the Guava User Guide article on {@code CharMatcher} + * . + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class CharMatcher implements Predicate { + /* + * N777777777NO + * N7777777777777N + * M777777777777777N + * $N877777777D77777M + * N M77777777ONND777M + * MN777777777NN D777 + * N7ZN777777777NN ~M7778 + * N777777777777MMNN88777N + * N777777777777MNZZZ7777O + * DZN7777O77777777777777 + * N7OONND7777777D77777N + * 8$M++++?N???$77777$ + * M7++++N+M77777777N + * N77O777777777777$ M + * DNNM$$$$777777N D + * N$N:=N$777N7777M NZ + * 77Z::::N777777777 ODZZZ + * 77N::::::N77777777M NNZZZ$ + * $777:::::::77777777MN ZM8ZZZZZ + * 777M::::::Z7777777Z77 N++ZZZZNN + * 7777M:::::M7777777$777M $++IZZZZM + * M777$:::::N777777$M7777M +++++ZZZDN + * NN$::::::7777$$M777777N N+++ZZZZNZ + * N::::::N:7$O:77777777 N++++ZZZZN + * M::::::::::::N77777777+ +?+++++ZZZM + * 8::::::::::::D77777777M O+++++ZZ + * ::::::::::::M777777777N O+?D + * M:::::::::::M77777777778 77= + * D=::::::::::N7777777777N 777 + * INN===::::::=77777777777N I777N + * ?777N========N7777777777787M N7777 + * 77777$D======N77777777777N777N? N777777 + * I77777$$$N7===M$$77777777$77777777$MMZ77777777N + * $$$$$$$$$$$NIZN$$$$$$$$$M$$7777777777777777ON + * M$$$$$$$$M M$$$$$$$$N=N$$$$7777777$$$ND + * O77Z$$$$$$$ M$$$$$$$$MNI==$DNNNNM=~N + * 7 :N MNN$$$$M$ $$$777$8 8D8I + * NMM.:7O 777777778 + * 7777777MN + * M NO .7: + * M : M + * 8 + */ + + // Constant matcher factory methods + + /** + * Matches any character. + * + * @since 19.0 (since 1.0 as constant {@code ANY}) + */ + public static CharMatcher any() { + return Any.INSTANCE; + } + + /** + * Matches no characters. + * + * @since 19.0 (since 1.0 as constant {@code NONE}) + */ + public static CharMatcher none() { + return None.INSTANCE; + } + + /** + * Determines whether a character is whitespace according to the latest Unicode standard, as + * illustrated here. + * This is not the same definition used by other Java APIs. (See a comparison of several definitions of "whitespace".) + * + *

All Unicode White_Space characters are on the BMP and thus supported by this API. + * + *

Note: as the Unicode definition evolves, we will modify this matcher to keep it up to + * date. + * + * @since 19.0 (since 1.0 as constant {@code WHITESPACE}) + */ + public static CharMatcher whitespace() { + return Whitespace.INSTANCE; + } + + /** + * Determines whether a character is a breaking whitespace (that is, a whitespace which can be + * interpreted as a break between words for formatting purposes). See {@link #whitespace()} for a + * discussion of that term. + * + * @since 19.0 (since 2.0 as constant {@code BREAKING_WHITESPACE}) + */ + public static CharMatcher breakingWhitespace() { + return BreakingWhitespace.INSTANCE; + } + + /** + * Determines whether a character is ASCII, meaning that its code point is less than 128. + * + * @since 19.0 (since 1.0 as constant {@code ASCII}) + */ + public static CharMatcher ascii() { + return Ascii.INSTANCE; + } + + /** + * Determines whether a character is a BMP digit according to Unicode. If + * you only care to match ASCII digits, you can use {@code inRange('0', '9')}. + * + * @deprecated Many digits are supplementary characters; see the class documentation. + * @since 19.0 (since 1.0 as constant {@code DIGIT}) + */ + @Deprecated + public static CharMatcher digit() { + return Digit.INSTANCE; + } + + /** + * Determines whether a character is a BMP digit according to {@linkplain Character#isDigit(char) + * Java's definition}. If you only care to match ASCII digits, you can use {@code inRange('0', + * '9')}. + * + * @deprecated Many digits are supplementary characters; see the class documentation. + * @since 19.0 (since 1.0 as constant {@code JAVA_DIGIT}) + */ + @Deprecated + public static CharMatcher javaDigit() { + return JavaDigit.INSTANCE; + } + + /** + * Determines whether a character is a BMP letter according to {@linkplain + * Character#isLetter(char) Java's definition}. If you only care to match letters of the Latin + * alphabet, you can use {@code inRange('a', 'z').or(inRange('A', 'Z'))}. + * + * @deprecated Most letters are supplementary characters; see the class documentation. + * @since 19.0 (since 1.0 as constant {@code JAVA_LETTER}) + */ + @Deprecated + public static CharMatcher javaLetter() { + return JavaLetter.INSTANCE; + } + + /** + * Determines whether a character is a BMP letter or digit according to {@linkplain + * Character#isLetterOrDigit(char) Java's definition}. + * + * @deprecated Most letters and digits are supplementary characters; see the class documentation. + * @since 19.0 (since 1.0 as constant {@code JAVA_LETTER_OR_DIGIT}). + */ + @Deprecated + public static CharMatcher javaLetterOrDigit() { + return JavaLetterOrDigit.INSTANCE; + } + + /** + * Determines whether a BMP character is upper case according to {@linkplain + * Character#isUpperCase(char) Java's definition}. + * + * @deprecated Some uppercase characters are supplementary characters; see the class + * documentation. + * @since 19.0 (since 1.0 as constant {@code JAVA_UPPER_CASE}) + */ + @Deprecated + public static CharMatcher javaUpperCase() { + return JavaUpperCase.INSTANCE; + } + + /** + * Determines whether a BMP character is lower case according to {@linkplain + * Character#isLowerCase(char) Java's definition}. + * + * @deprecated Some lowercase characters are supplementary characters; see the class + * documentation. + * @since 19.0 (since 1.0 as constant {@code JAVA_LOWER_CASE}) + */ + @Deprecated + public static CharMatcher javaLowerCase() { + return JavaLowerCase.INSTANCE; + } + + /** + * Determines whether a character is an ISO control character as specified by {@link + * Character#isISOControl(char)}. + * + *

All ISO control codes are on the BMP and thus supported by this API. + * + * @since 19.0 (since 1.0 as constant {@code JAVA_ISO_CONTROL}) + */ + public static CharMatcher javaIsoControl() { + return JavaIsoControl.INSTANCE; + } + + /** + * Determines whether a character is invisible; that is, if its Unicode category is any of + * SPACE_SEPARATOR, LINE_SEPARATOR, PARAGRAPH_SEPARATOR, CONTROL, FORMAT, SURROGATE, and + * PRIVATE_USE according to ICU4J. + * + *

See also the Unicode Default_Ignorable_Code_Point property (available via ICU). + * + * @deprecated Most invisible characters are supplementary characters; see the class + * documentation. + * @since 19.0 (since 1.0 as constant {@code INVISIBLE}) + */ + @Deprecated + public static CharMatcher invisible() { + return Invisible.INSTANCE; + } + + /** + * Determines whether a character is single-width (not double-width). When in doubt, this matcher + * errs on the side of returning {@code false} (that is, it tends to assume a character is + * double-width). + * + *

Note: as the reference file evolves, we will modify this matcher to keep it up to + * date. + * + *

See also UAX #11 East Asian Width. + * + * @deprecated Many such characters are supplementary characters; see the class documentation. + * @since 19.0 (since 1.0 as constant {@code SINGLE_WIDTH}) + */ + @Deprecated + public static CharMatcher singleWidth() { + return SingleWidth.INSTANCE; + } + + // Static factories + + /** Returns a {@code char} matcher that matches only one specified BMP character. */ + public static CharMatcher is(final char match) { + return new Is(match); + } + + /** + * Returns a {@code char} matcher that matches any character except the BMP character specified. + * + *

To negate another {@code CharMatcher}, use {@link #negate()}. + */ + public static CharMatcher isNot(final char match) { + return new IsNot(match); + } + + /** + * Returns a {@code char} matcher that matches any BMP character present in the given character + * sequence. Returns a bogus matcher if the sequence contains supplementary characters. + */ + public static CharMatcher anyOf(final CharSequence sequence) { + switch (sequence.length()) { + case 0: + return none(); + case 1: + return is(sequence.charAt(0)); + case 2: + return isEither(sequence.charAt(0), sequence.charAt(1)); + default: + // TODO(lowasser): is it potentially worth just going ahead and building a precomputed + // matcher? + return new AnyOf(sequence); + } + } + + /** + * Returns a {@code char} matcher that matches any BMP character not present in the given + * character sequence. Returns a bogus matcher if the sequence contains supplementary characters. + */ + public static CharMatcher noneOf(CharSequence sequence) { + return anyOf(sequence).negate(); + } + + /** + * Returns a {@code char} matcher that matches any character in a given BMP range (both endpoints + * are inclusive). For example, to match any lowercase letter of the English alphabet, use {@code + * CharMatcher.inRange('a', 'z')}. + * + * @throws IllegalArgumentException if {@code endInclusive < startInclusive} + */ + public static CharMatcher inRange(final char startInclusive, final char endInclusive) { + return new InRange(startInclusive, endInclusive); + } + + /** + * Returns a matcher with identical behavior to the given {@link Character}-based predicate, but + * which operates on primitive {@code char} instances instead. + */ + public static CharMatcher forPredicate(final Predicate predicate) { + return predicate instanceof CharMatcher ? (CharMatcher) predicate : new ForPredicate(predicate); + } + + // Constructors + + /** + * Constructor for use by subclasses. When subclassing, you may want to override {@code + * toString()} to provide a useful description. + */ + protected CharMatcher() {} + + // Abstract methods + + /** Determines a true or false value for the given character. */ + public abstract boolean matches(char c); + + // Non-static factories + + /** Returns a matcher that matches any character not matched by this matcher. */ + // @Override under Java 8 but not under Java 7 + @Override + public CharMatcher negate() { + return new Negated(this); + } + + /** + * Returns a matcher that matches any character matched by both this matcher and {@code other}. + */ + public CharMatcher and(CharMatcher other) { + return new And(this, other); + } + + /** + * Returns a matcher that matches any character matched by either this matcher or {@code other}. + */ + public CharMatcher or(CharMatcher other) { + return new Or(this, other); + } + + /** + * Returns a {@code char} matcher functionally equivalent to this one, but which may be faster to + * query than the original; your mileage may vary. Precomputation takes time and is likely to be + * worthwhile only if the precomputed matcher is queried many thousands of times. + * + *

This method has no effect (returns {@code this}) when called in GWT: it's unclear whether a + * precomputed matcher is faster, but it certainly consumes more memory, which doesn't seem like a + * worthwhile tradeoff in a browser. + */ + public CharMatcher precomputed() { + return Platform.precomputeCharMatcher(this); + } + + private static final int DISTINCT_CHARS = Character.MAX_VALUE - Character.MIN_VALUE + 1; + + /** + * This is the actual implementation of {@link #precomputed}, but we bounce calls through a method + * on {@link Platform} so that we can have different behavior in GWT. + * + *

This implementation tries to be smart in a number of ways. It recognizes cases where the + * negation is cheaper to precompute than the matcher itself; it tries to build small hash tables + * for matchers that only match a few characters, and so on. In the worst-case scenario, it + * constructs an eight-kilobyte bit array and queries that. In many situations this produces a + * matcher which is faster to query than the original. + */ + @J2ktIncompatible + @GwtIncompatible // SmallCharMatcher + CharMatcher precomputedInternal() { + final BitSet table = new BitSet(); + setBits(table); + int totalCharacters = table.cardinality(); + if (totalCharacters * 2 <= DISTINCT_CHARS) { + return precomputedPositive(totalCharacters, table, toString()); + } else { + // TODO(lowasser): is it worth it to worry about the last character of large matchers? + table.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); + int negatedCharacters = DISTINCT_CHARS - totalCharacters; + String suffix = ".negate()"; + final String description = toString(); + String negatedDescription = + description.endsWith(suffix) + ? description.substring(0, description.length() - suffix.length()) + : description + suffix; + return new NegatedFastMatcher( + precomputedPositive(negatedCharacters, table, negatedDescription)) { + @Override + public String toString() { + return description; + } + }; + } + } + + /** + * Helper method for {@link #precomputedInternal} that doesn't test if the negation is cheaper. + */ + @J2ktIncompatible + @GwtIncompatible // SmallCharMatcher + private static CharMatcher precomputedPositive( + int totalCharacters, BitSet table, String description) { + switch (totalCharacters) { + case 0: + return none(); + case 1: + return is((char) table.nextSetBit(0)); + case 2: + char c1 = (char) table.nextSetBit(0); + char c2 = (char) table.nextSetBit(c1 + 1); + return isEither(c1, c2); + default: + return isSmall(totalCharacters, table.length()) + ? SmallCharMatcher.from(table, description) + : new BitSetMatcher(table, description); + } + } + + @J2ktIncompatible + @GwtIncompatible // SmallCharMatcher + private static boolean isSmall(int totalCharacters, int tableLength) { + return totalCharacters <= SmallCharMatcher.MAX_SIZE + && tableLength > (totalCharacters * 4 * Character.SIZE); + // err on the side of BitSetMatcher + } + + /** Sets bits in {@code table} matched by this matcher. */ + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + void setBits(BitSet table) { + for (int c = Character.MAX_VALUE; c >= Character.MIN_VALUE; c--) { + if (matches((char) c)) { + table.set(c); + } + } + } + + // Text processing routines + + /** + * Returns {@code true} if a character sequence contains at least one matching BMP character. + * Equivalent to {@code !matchesNoneOf(sequence)}. + * + *

The default implementation iterates over the sequence, invoking {@link #matches} for each + * character, until this returns {@code true} or the end is reached. + * + * @param sequence the character sequence to examine, possibly empty + * @return {@code true} if this matcher matches at least one character in the sequence + * @since 8.0 + */ + public boolean matchesAnyOf(CharSequence sequence) { + return !matchesNoneOf(sequence); + } + + /** + * Returns {@code true} if a character sequence contains only matching BMP characters. + * + *

The default implementation iterates over the sequence, invoking {@link #matches} for each + * character, until this returns {@code false} or the end is reached. + * + * @param sequence the character sequence to examine, possibly empty + * @return {@code true} if this matcher matches every character in the sequence, including when + * the sequence is empty + */ + public boolean matchesAllOf(CharSequence sequence) { + for (int i = sequence.length() - 1; i >= 0; i--) { + if (!matches(sequence.charAt(i))) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if a character sequence contains no matching BMP characters. Equivalent to + * {@code !matchesAnyOf(sequence)}. + * + *

The default implementation iterates over the sequence, invoking {@link #matches} for each + * character, until this returns {@code true} or the end is reached. + * + * @param sequence the character sequence to examine, possibly empty + * @return {@code true} if this matcher matches no characters in the sequence, including when the + * sequence is empty + */ + public boolean matchesNoneOf(CharSequence sequence) { + return indexIn(sequence) == -1; + } + + /** + * Returns the index of the first matching BMP character in a character sequence, or {@code -1} if + * no matching character is present. + * + *

The default implementation iterates over the sequence in forward order calling {@link + * #matches} for each character. + * + * @param sequence the character sequence to examine from the beginning + * @return an index, or {@code -1} if no character matches + */ + public int indexIn(CharSequence sequence) { + return indexIn(sequence, 0); + } + + /** + * Returns the index of the first matching BMP character in a character sequence, starting from a + * given position, or {@code -1} if no character matches after that position. + * + *

The default implementation iterates over the sequence in forward order, beginning at {@code + * start}, calling {@link #matches} for each character. + * + * @param sequence the character sequence to examine + * @param start the first index to examine; must be nonnegative and no greater than {@code + * sequence.length()} + * @return the index of the first matching character, guaranteed to be no less than {@code start}, + * or {@code -1} if no character matches + * @throws IndexOutOfBoundsException if start is negative or greater than {@code + * sequence.length()} + */ + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + checkPositionIndex(start, length); + for (int i = start; i < length; i++) { + if (matches(sequence.charAt(i))) { + return i; + } + } + return -1; + } + + /** + * Returns the index of the last matching BMP character in a character sequence, or {@code -1} if + * no matching character is present. + * + *

The default implementation iterates over the sequence in reverse order calling {@link + * #matches} for each character. + * + * @param sequence the character sequence to examine from the end + * @return an index, or {@code -1} if no character matches + */ + public int lastIndexIn(CharSequence sequence) { + for (int i = sequence.length() - 1; i >= 0; i--) { + if (matches(sequence.charAt(i))) { + return i; + } + } + return -1; + } + + /** + * Returns the number of matching {@code char}s found in a character sequence. + * + *

Counts 2 per supplementary character, such as for {@link #whitespace}().{@link #negate}(). + */ + public int countIn(CharSequence sequence) { + int count = 0; + for (int i = 0; i < sequence.length(); i++) { + if (matches(sequence.charAt(i))) { + count++; + } + } + return count; + } + + /** + * Returns a string containing all non-matching characters of a character sequence, in order. For + * example: + * + *

{@code
+   * CharMatcher.is('a').removeFrom("bazaar")
+   * }
+ * + * ... returns {@code "bzr"}. + */ + public String removeFrom(CharSequence sequence) { + String string = sequence.toString(); + int pos = indexIn(string); + if (pos == -1) { + return string; + } + + char[] chars = string.toCharArray(); + int spread = 1; + + // This unusual loop comes from extensive benchmarking + OUT: + while (true) { + pos++; + while (true) { + if (pos == chars.length) { + break OUT; + } + if (matches(chars[pos])) { + break; + } + chars[pos - spread] = chars[pos]; + pos++; + } + spread++; + } + return new String(chars, 0, pos - spread); + } + + /** + * Returns a string containing all matching BMP characters of a character sequence, in order. For + * example: + * + *
{@code
+   * CharMatcher.is('a').retainFrom("bazaar")
+   * }
+ * + * ... returns {@code "aaa"}. + */ + public String retainFrom(CharSequence sequence) { + return negate().removeFrom(sequence); + } + + /** + * Returns a string copy of the input character sequence, with each matching BMP character + * replaced by a given replacement character. For example: + * + *
{@code
+   * CharMatcher.is('a').replaceFrom("radar", 'o')
+   * }
+ * + * ... returns {@code "rodor"}. + * + *

The default implementation uses {@link #indexIn(CharSequence)} to find the first matching + * character, then iterates the remainder of the sequence calling {@link #matches(char)} for each + * character. + * + * @param sequence the character sequence to replace matching characters in + * @param replacement the character to append to the result string in place of each matching + * character in {@code sequence} + * @return the new string + */ + public String replaceFrom(CharSequence sequence, char replacement) { + String string = sequence.toString(); + int pos = indexIn(string); + if (pos == -1) { + return string; + } + char[] chars = string.toCharArray(); + chars[pos] = replacement; + for (int i = pos + 1; i < chars.length; i++) { + if (matches(chars[i])) { + chars[i] = replacement; + } + } + return new String(chars); + } + + /** + * Returns a string copy of the input character sequence, with each matching BMP character + * replaced by a given replacement sequence. For example: + * + *

{@code
+   * CharMatcher.is('a').replaceFrom("yaha", "oo")
+   * }
+ * + * ... returns {@code "yoohoo"}. + * + *

Note: If the replacement is a fixed string with only one character, you are better + * off calling {@link #replaceFrom(CharSequence, char)} directly. + * + * @param sequence the character sequence to replace matching characters in + * @param replacement the characters to append to the result string in place of each matching + * character in {@code sequence} + * @return the new string + */ + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + int replacementLen = replacement.length(); + if (replacementLen == 0) { + return removeFrom(sequence); + } + if (replacementLen == 1) { + return replaceFrom(sequence, replacement.charAt(0)); + } + + String string = sequence.toString(); + int pos = indexIn(string); + if (pos == -1) { + return string; + } + + int len = string.length(); + StringBuilder buf = new StringBuilder((len * 3 / 2) + 16); + + int oldpos = 0; + do { + buf.append(string, oldpos, pos); + buf.append(replacement); + oldpos = pos + 1; + pos = indexIn(string, oldpos); + } while (pos != -1); + + buf.append(string, oldpos, len); + return buf.toString(); + } + + /** + * Returns a substring of the input character sequence that omits all matching BMP characters from + * the beginning and from the end of the string. For example: + * + *

{@code
+   * CharMatcher.anyOf("ab").trimFrom("abacatbab")
+   * }
+ * + * ... returns {@code "cat"}. + * + *

Note that: + * + *

{@code
+   * CharMatcher.inRange('\0', ' ').trimFrom(str)
+   * }
+ * + * ... is equivalent to {@link String#trim()}. + */ + public String trimFrom(CharSequence sequence) { + int len = sequence.length(); + int first; + int last; + + for (first = 0; first < len; first++) { + if (!matches(sequence.charAt(first))) { + break; + } + } + for (last = len - 1; last > first; last--) { + if (!matches(sequence.charAt(last))) { + break; + } + } + + return sequence.subSequence(first, last + 1).toString(); + } + + /** + * Returns a substring of the input character sequence that omits all matching BMP characters from + * the beginning of the string. For example: + * + *
{@code
+   * CharMatcher.anyOf("ab").trimLeadingFrom("abacatbab")
+   * }
+ * + * ... returns {@code "catbab"}. + */ + public String trimLeadingFrom(CharSequence sequence) { + int len = sequence.length(); + for (int first = 0; first < len; first++) { + if (!matches(sequence.charAt(first))) { + return sequence.subSequence(first, len).toString(); + } + } + return ""; + } + + /** + * Returns a substring of the input character sequence that omits all matching BMP characters from + * the end of the string. For example: + * + *
{@code
+   * CharMatcher.anyOf("ab").trimTrailingFrom("abacatbab")
+   * }
+ * + * ... returns {@code "abacat"}. + */ + public String trimTrailingFrom(CharSequence sequence) { + int len = sequence.length(); + for (int last = len - 1; last >= 0; last--) { + if (!matches(sequence.charAt(last))) { + return sequence.subSequence(0, last + 1).toString(); + } + } + return ""; + } + + /** + * Returns a string copy of the input character sequence, with each group of consecutive matching + * BMP characters replaced by a single replacement character. For example: + * + *
{@code
+   * CharMatcher.anyOf("eko").collapseFrom("bookkeeper", '-')
+   * }
+ * + * ... returns {@code "b-p-r"}. + * + *

The default implementation uses {@link #indexIn(CharSequence)} to find the first matching + * character, then iterates the remainder of the sequence calling {@link #matches(char)} for each + * character. + * + * @param sequence the character sequence to replace matching groups of characters in + * @param replacement the character to append to the result string in place of each group of + * matching characters in {@code sequence} + * @return the new string + */ + public String collapseFrom(CharSequence sequence, char replacement) { + // This implementation avoids unnecessary allocation. + int len = sequence.length(); + for (int i = 0; i < len; i++) { + char c = sequence.charAt(i); + if (matches(c)) { + if (c == replacement && (i == len - 1 || !matches(sequence.charAt(i + 1)))) { + // a no-op replacement + i++; + } else { + StringBuilder builder = new StringBuilder(len).append(sequence, 0, i).append(replacement); + return finishCollapseFrom(sequence, i + 1, len, replacement, builder, true); + } + } + } + // no replacement needed + return sequence.toString(); + } + + /** + * Collapses groups of matching characters exactly as {@link #collapseFrom} does, except that + * groups of matching BMP characters at the start or end of the sequence are removed without + * replacement. + */ + public String trimAndCollapseFrom(CharSequence sequence, char replacement) { + // This implementation avoids unnecessary allocation. + int len = sequence.length(); + int first = 0; + int last = len - 1; + + while (first < len && matches(sequence.charAt(first))) { + first++; + } + + while (last > first && matches(sequence.charAt(last))) { + last--; + } + + return (first == 0 && last == len - 1) + ? collapseFrom(sequence, replacement) + : finishCollapseFrom( + sequence, first, last + 1, replacement, new StringBuilder(last + 1 - first), false); + } + + private String finishCollapseFrom( + CharSequence sequence, + int start, + int end, + char replacement, + StringBuilder builder, + boolean inMatchingGroup) { + for (int i = start; i < end; i++) { + char c = sequence.charAt(i); + if (matches(c)) { + if (!inMatchingGroup) { + builder.append(replacement); + inMatchingGroup = true; + } + } else { + builder.append(c); + inMatchingGroup = false; + } + } + return builder.toString(); + } + + /** + * @deprecated Provided only to satisfy the {@link Predicate} interface; use {@link #matches} + * instead. + */ + @Deprecated + @Override + public boolean apply(Character character) { + return matches(character); + } + + /** + * Returns a string representation of this {@code CharMatcher}, such as {@code + * CharMatcher.or(WHITESPACE, JAVA_DIGIT)}. + */ + @Override + public String toString() { + return super.toString(); + } + + /** + * Returns the Java Unicode escape sequence for the given {@code char}, in the form "\u12AB" where + * "12AB" is the four hexadecimal digits representing the 16-bit code unit. + */ + private static String showCharacter(char c) { + String hex = "0123456789ABCDEF"; + char[] tmp = {'\\', 'u', '\0', '\0', '\0', '\0'}; + for (int i = 0; i < 4; i++) { + tmp[5 - i] = hex.charAt(c & 0xF); + c = (char) (c >> 4); + } + return String.copyValueOf(tmp); + } + + // Fast matchers + + /** A matcher for which precomputation will not yield any significant benefit. */ + abstract static class FastMatcher extends CharMatcher { + + @Override + public final CharMatcher precomputed() { + return this; + } + + @Override + public CharMatcher negate() { + return new NegatedFastMatcher(this); + } + } + + /** {@link FastMatcher} which overrides {@code toString()} with a custom name. */ + abstract static class NamedFastMatcher extends FastMatcher { + + private final String description; + + NamedFastMatcher(String description) { + this.description = checkNotNull(description); + } + + @Override + public final String toString() { + return description; + } + } + + /** Negation of a {@link FastMatcher}. */ + static class NegatedFastMatcher extends Negated { + + NegatedFastMatcher(CharMatcher original) { + super(original); + } + + @Override + public final CharMatcher precomputed() { + return this; + } + } + + /** Fast matcher using a {@link BitSet} table of matching characters. */ + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + private static final class BitSetMatcher extends NamedFastMatcher { + + private final BitSet table; + + private BitSetMatcher(BitSet table, String description) { + super(description); + if (table.length() + Long.SIZE < table.size()) { + table = (BitSet) table.clone(); + // If only we could actually call BitSet.trimToSize() ourselves... + } + this.table = table; + } + + @Override + public boolean matches(char c) { + return table.get(c); + } + + @Override + void setBits(BitSet bitSet) { + bitSet.or(table); + } + } + + // Static constant implementation classes + + /** Implementation of {@link #any()}. */ + private static final class Any extends NamedFastMatcher { + + static final Any INSTANCE = new Any(); + + private Any() { + super("CharMatcher.any()"); + } + + @Override + public boolean matches(char c) { + return true; + } + + @Override + public int indexIn(CharSequence sequence) { + return (sequence.length() == 0) ? -1 : 0; + } + + @Override + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + checkPositionIndex(start, length); + return (start == length) ? -1 : start; + } + + @Override + public int lastIndexIn(CharSequence sequence) { + return sequence.length() - 1; + } + + @Override + public boolean matchesAllOf(CharSequence sequence) { + checkNotNull(sequence); + return true; + } + + @Override + public boolean matchesNoneOf(CharSequence sequence) { + return sequence.length() == 0; + } + + @Override + public String removeFrom(CharSequence sequence) { + checkNotNull(sequence); + return ""; + } + + @Override + public String replaceFrom(CharSequence sequence, char replacement) { + char[] array = new char[sequence.length()]; + Arrays.fill(array, replacement); + return new String(array); + } + + @Override + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + StringBuilder result = new StringBuilder(sequence.length() * replacement.length()); + for (int i = 0; i < sequence.length(); i++) { + result.append(replacement); + } + return result.toString(); + } + + @Override + public String collapseFrom(CharSequence sequence, char replacement) { + return (sequence.length() == 0) ? "" : String.valueOf(replacement); + } + + @Override + public String trimFrom(CharSequence sequence) { + checkNotNull(sequence); + return ""; + } + + @Override + public int countIn(CharSequence sequence) { + return sequence.length(); + } + + @Override + public CharMatcher and(CharMatcher other) { + return checkNotNull(other); + } + + @Override + public CharMatcher or(CharMatcher other) { + checkNotNull(other); + return this; + } + + @Override + public CharMatcher negate() { + return none(); + } + } + + /** Implementation of {@link #none()}. */ + private static final class None extends NamedFastMatcher { + + static final None INSTANCE = new None(); + + private None() { + super("CharMatcher.none()"); + } + + @Override + public boolean matches(char c) { + return false; + } + + @Override + public int indexIn(CharSequence sequence) { + checkNotNull(sequence); + return -1; + } + + @Override + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + checkPositionIndex(start, length); + return -1; + } + + @Override + public int lastIndexIn(CharSequence sequence) { + checkNotNull(sequence); + return -1; + } + + @Override + public boolean matchesAllOf(CharSequence sequence) { + return sequence.length() == 0; + } + + @Override + public boolean matchesNoneOf(CharSequence sequence) { + checkNotNull(sequence); + return true; + } + + @Override + public String removeFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public String replaceFrom(CharSequence sequence, char replacement) { + return sequence.toString(); + } + + @Override + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + checkNotNull(replacement); + return sequence.toString(); + } + + @Override + public String collapseFrom(CharSequence sequence, char replacement) { + return sequence.toString(); + } + + @Override + public String trimFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public String trimLeadingFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public String trimTrailingFrom(CharSequence sequence) { + return sequence.toString(); + } + + @Override + public int countIn(CharSequence sequence) { + checkNotNull(sequence); + return 0; + } + + @Override + public CharMatcher and(CharMatcher other) { + checkNotNull(other); + return this; + } + + @Override + public CharMatcher or(CharMatcher other) { + return checkNotNull(other); + } + + @Override + public CharMatcher negate() { + return any(); + } + } + + /** Implementation of {@link #whitespace()}. */ + @VisibleForTesting + static final class Whitespace extends NamedFastMatcher { + + // TABLE is a precomputed hashset of whitespace characters. MULTIPLIER serves as a hash function + // whose key property is that it maps 25 characters into the 32-slot table without collision. + // Basically this is an opportunistic fast implementation as opposed to "good code". For most + // other use-cases, the reduction in readability isn't worth it. + static final String TABLE = + "\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000" + + "\u2029\u000B\u3000\u2008\u2003\u205F\u3000\u1680" + + "\u0009\u0020\u2006\u2001\u202F\u00A0\u000C\u2009" + + "\u3000\u2004\u3000\u3000\u2028\n\u2007\u3000"; + static final int MULTIPLIER = 1682554634; + static final int SHIFT = Integer.numberOfLeadingZeros(TABLE.length() - 1); + + static final Whitespace INSTANCE = new Whitespace(); + + Whitespace() { + super("CharMatcher.whitespace()"); + } + + @Override + public boolean matches(char c) { + return TABLE.charAt((MULTIPLIER * c) >>> SHIFT) == c; + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + for (int i = 0; i < TABLE.length(); i++) { + table.set(TABLE.charAt(i)); + } + } + } + + /** Implementation of {@link #breakingWhitespace()}. */ + private static final class BreakingWhitespace extends CharMatcher { + + static final CharMatcher INSTANCE = new BreakingWhitespace(); + + @Override + public boolean matches(char c) { + switch (c) { + case '\t': + case '\n': + case '\013': + case '\f': + case '\r': + case ' ': + case '\u0085': + case '\u1680': + case '\u2028': + case '\u2029': + case '\u205f': + case '\u3000': + return true; + case '\u2007': + return false; + default: + return c >= '\u2000' && c <= '\u200a'; + } + } + + @Override + public String toString() { + return "CharMatcher.breakingWhitespace()"; + } + } + + /** Implementation of {@link #ascii()}. */ + private static final class Ascii extends NamedFastMatcher { + + static final Ascii INSTANCE = new Ascii(); + + Ascii() { + super("CharMatcher.ascii()"); + } + + @Override + public boolean matches(char c) { + return c <= '\u007f'; + } + } + + /** Implementation that matches characters that fall within multiple ranges. */ + private static class RangesMatcher extends CharMatcher { + + private final String description; + private final char[] rangeStarts; + private final char[] rangeEnds; + + RangesMatcher(String description, char[] rangeStarts, char[] rangeEnds) { + this.description = description; + this.rangeStarts = rangeStarts; + this.rangeEnds = rangeEnds; + checkArgument(rangeStarts.length == rangeEnds.length); + for (int i = 0; i < rangeStarts.length; i++) { + checkArgument(rangeStarts[i] <= rangeEnds[i]); + if (i + 1 < rangeStarts.length) { + checkArgument(rangeEnds[i] < rangeStarts[i + 1]); + } + } + } + + @Override + public boolean matches(char c) { + int index = Arrays.binarySearch(rangeStarts, c); + if (index >= 0) { + return true; + } else { + index = ~index - 1; + return index >= 0 && c <= rangeEnds[index]; + } + } + + @Override + public String toString() { + return description; + } + } + + /** Implementation of {@link #digit()}. */ + private static final class Digit extends RangesMatcher { + // Plug the following UnicodeSet pattern into + // https://unicode.org/cldr/utility/list-unicodeset.jsp + // [[:Nd:]&[:nv=0:]&[\u0000-\uFFFF]] + // and get the zeroes from there. + + // Must be in ascending order. + private static final String ZEROES = + "0\u0660\u06f0\u07c0\u0966\u09e6\u0a66\u0ae6\u0b66\u0be6\u0c66\u0ce6\u0d66\u0de6" + + "\u0e50\u0ed0\u0f20\u1040\u1090\u17e0\u1810\u1946\u19d0\u1a80\u1a90\u1b50\u1bb0" + + "\u1c40\u1c50\ua620\ua8d0\ua900\ua9d0\ua9f0\uaa50\uabf0\uff10"; + + private static char[] zeroes() { + return ZEROES.toCharArray(); + } + + private static char[] nines() { + char[] nines = new char[ZEROES.length()]; + for (int i = 0; i < ZEROES.length(); i++) { + nines[i] = (char) (ZEROES.charAt(i) + 9); + } + return nines; + } + + static final Digit INSTANCE = new Digit(); + + private Digit() { + super("CharMatcher.digit()", zeroes(), nines()); + } + } + + /** Implementation of {@link #javaDigit()}. */ + private static final class JavaDigit extends CharMatcher { + + static final JavaDigit INSTANCE = new JavaDigit(); + + @Override + public boolean matches(char c) { + return Character.isDigit(c); + } + + @Override + public String toString() { + return "CharMatcher.javaDigit()"; + } + } + + /** Implementation of {@link #javaLetter()}. */ + private static final class JavaLetter extends CharMatcher { + + static final JavaLetter INSTANCE = new JavaLetter(); + + @Override + public boolean matches(char c) { + return Character.isLetter(c); + } + + @Override + public String toString() { + return "CharMatcher.javaLetter()"; + } + } + + /** Implementation of {@link #javaLetterOrDigit()}. */ + private static final class JavaLetterOrDigit extends CharMatcher { + + static final JavaLetterOrDigit INSTANCE = new JavaLetterOrDigit(); + + @Override + public boolean matches(char c) { + return Character.isLetterOrDigit(c); + } + + @Override + public String toString() { + return "CharMatcher.javaLetterOrDigit()"; + } + } + + /** Implementation of {@link #javaUpperCase()}. */ + private static final class JavaUpperCase extends CharMatcher { + + static final JavaUpperCase INSTANCE = new JavaUpperCase(); + + @Override + public boolean matches(char c) { + return Character.isUpperCase(c); + } + + @Override + public String toString() { + return "CharMatcher.javaUpperCase()"; + } + } + + /** Implementation of {@link #javaLowerCase()}. */ + private static final class JavaLowerCase extends CharMatcher { + + static final JavaLowerCase INSTANCE = new JavaLowerCase(); + + @Override + public boolean matches(char c) { + return Character.isLowerCase(c); + } + + @Override + public String toString() { + return "CharMatcher.javaLowerCase()"; + } + } + + /** Implementation of {@link #javaIsoControl()}. */ + private static final class JavaIsoControl extends NamedFastMatcher { + + static final JavaIsoControl INSTANCE = new JavaIsoControl(); + + private JavaIsoControl() { + super("CharMatcher.javaIsoControl()"); + } + + @Override + public boolean matches(char c) { + return c <= '\u001f' || (c >= '\u007f' && c <= '\u009f'); + } + } + + /** Implementation of {@link #invisible()}. */ + private static final class Invisible extends RangesMatcher { + // Plug the following UnicodeSet pattern into + // https://unicode.org/cldr/utility/list-unicodeset.jsp + // [[[:Zs:][:Zl:][:Zp:][:Cc:][:Cf:][:Cs:][:Co:]]&[\u0000-\uFFFF]] + // with the "Abbreviate" option, and get the ranges from there. + private static final String RANGE_STARTS = + "\u0000\u007f\u00ad\u0600\u061c\u06dd\u070f\u0890\u08e2\u1680\u180e\u2000\u2028\u205f\u2066" + + "\u3000\ud800\ufeff\ufff9"; + private static final String RANGE_ENDS = // inclusive ends + "\u0020\u00a0\u00ad\u0605\u061c\u06dd\u070f\u0891\u08e2\u1680\u180e\u200f\u202f\u2064\u206f" + + "\u3000\uf8ff\ufeff\ufffb"; + + static final Invisible INSTANCE = new Invisible(); + + private Invisible() { + super("CharMatcher.invisible()", RANGE_STARTS.toCharArray(), RANGE_ENDS.toCharArray()); + } + } + + /** Implementation of {@link #singleWidth()}. */ + private static final class SingleWidth extends RangesMatcher { + + static final SingleWidth INSTANCE = new SingleWidth(); + + private SingleWidth() { + super( + "CharMatcher.singleWidth()", + "\u0000\u05be\u05d0\u05f3\u0600\u0750\u0e00\u1e00\u2100\ufb50\ufe70\uff61".toCharArray(), + "\u04f9\u05be\u05ea\u05f4\u06ff\u077f\u0e7f\u20af\u213a\ufdff\ufeff\uffdc".toCharArray()); + } + } + + // Non-static factory implementation classes + + /** Implementation of {@link #negate()}. */ + private static class Negated extends CharMatcher { + + final CharMatcher original; + + Negated(CharMatcher original) { + this.original = checkNotNull(original); + } + + @Override + public boolean matches(char c) { + return !original.matches(c); + } + + @Override + public boolean matchesAllOf(CharSequence sequence) { + return original.matchesNoneOf(sequence); + } + + @Override + public boolean matchesNoneOf(CharSequence sequence) { + return original.matchesAllOf(sequence); + } + + @Override + public int countIn(CharSequence sequence) { + return sequence.length() - original.countIn(sequence); + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + BitSet tmp = new BitSet(); + original.setBits(tmp); + tmp.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); + table.or(tmp); + } + + @Override + public CharMatcher negate() { + return original; + } + + @Override + public String toString() { + return original + ".negate()"; + } + } + + /** Implementation of {@link #and(CharMatcher)}. */ + private static final class And extends CharMatcher { + + final CharMatcher first; + final CharMatcher second; + + And(CharMatcher a, CharMatcher b) { + first = checkNotNull(a); + second = checkNotNull(b); + } + + @Override + public boolean matches(char c) { + return first.matches(c) && second.matches(c); + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + BitSet tmp1 = new BitSet(); + first.setBits(tmp1); + BitSet tmp2 = new BitSet(); + second.setBits(tmp2); + tmp1.and(tmp2); + table.or(tmp1); + } + + @Override + public String toString() { + return "CharMatcher.and(" + first + ", " + second + ")"; + } + } + + /** Implementation of {@link #or(CharMatcher)}. */ + private static final class Or extends CharMatcher { + + final CharMatcher first; + final CharMatcher second; + + Or(CharMatcher a, CharMatcher b) { + first = checkNotNull(a); + second = checkNotNull(b); + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + first.setBits(table); + second.setBits(table); + } + + @Override + public boolean matches(char c) { + return first.matches(c) || second.matches(c); + } + + @Override + public String toString() { + return "CharMatcher.or(" + first + ", " + second + ")"; + } + } + + // Static factory implementations + + /** Implementation of {@link #is(char)}. */ + private static final class Is extends FastMatcher { + + private final char match; + + Is(char match) { + this.match = match; + } + + @Override + public boolean matches(char c) { + return c == match; + } + + @Override + public String replaceFrom(CharSequence sequence, char replacement) { + return sequence.toString().replace(match, replacement); + } + + @Override + public CharMatcher and(CharMatcher other) { + return other.matches(match) ? this : none(); + } + + @Override + public CharMatcher or(CharMatcher other) { + return other.matches(match) ? other : super.or(other); + } + + @Override + public CharMatcher negate() { + return isNot(match); + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + table.set(match); + } + + @Override + public String toString() { + return "CharMatcher.is('" + showCharacter(match) + "')"; + } + } + + /** Implementation of {@link #isNot(char)}. */ + private static final class IsNot extends FastMatcher { + + private final char match; + + IsNot(char match) { + this.match = match; + } + + @Override + public boolean matches(char c) { + return c != match; + } + + @Override + public CharMatcher and(CharMatcher other) { + return other.matches(match) ? super.and(other) : other; + } + + @Override + public CharMatcher or(CharMatcher other) { + return other.matches(match) ? any() : this; + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + table.set(0, match); + table.set(match + 1, Character.MAX_VALUE + 1); + } + + @Override + public CharMatcher negate() { + return is(match); + } + + @Override + public String toString() { + return "CharMatcher.isNot('" + showCharacter(match) + "')"; + } + } + + private static CharMatcher.IsEither isEither(char c1, char c2) { + return new CharMatcher.IsEither(c1, c2); + } + + /** Implementation of {@link #anyOf(CharSequence)} for exactly two characters. */ + private static final class IsEither extends FastMatcher { + + private final char match1; + private final char match2; + + IsEither(char match1, char match2) { + this.match1 = match1; + this.match2 = match2; + } + + @Override + public boolean matches(char c) { + return c == match1 || c == match2; + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + table.set(match1); + table.set(match2); + } + + @Override + public String toString() { + return "CharMatcher.anyOf(\"" + showCharacter(match1) + showCharacter(match2) + "\")"; + } + } + + /** Implementation of {@link #anyOf(CharSequence)} for three or more characters. */ + private static final class AnyOf extends CharMatcher { + + private final char[] chars; + + public AnyOf(CharSequence chars) { + this.chars = chars.toString().toCharArray(); + Arrays.sort(this.chars); + } + + @Override + public boolean matches(char c) { + return Arrays.binarySearch(chars, c) >= 0; + } + + @Override + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + void setBits(BitSet table) { + for (char c : chars) { + table.set(c); + } + } + + @Override + public String toString() { + StringBuilder description = new StringBuilder("CharMatcher.anyOf(\""); + for (char c : chars) { + description.append(showCharacter(c)); + } + description.append("\")"); + return description.toString(); + } + } + + /** Implementation of {@link #inRange(char, char)}. */ + private static final class InRange extends FastMatcher { + + private final char startInclusive; + private final char endInclusive; + + InRange(char startInclusive, char endInclusive) { + checkArgument(endInclusive >= startInclusive); + this.startInclusive = startInclusive; + this.endInclusive = endInclusive; + } + + @Override + public boolean matches(char c) { + return startInclusive <= c && c <= endInclusive; + } + + @J2ktIncompatible + @GwtIncompatible // used only from other GwtIncompatible code + @Override + void setBits(BitSet table) { + table.set(startInclusive, endInclusive + 1); + } + + @Override + public String toString() { + return "CharMatcher.inRange('" + + showCharacter(startInclusive) + + "', '" + + showCharacter(endInclusive) + + "')"; + } + } + + /** Implementation of {@link #forPredicate(Predicate)}. */ + private static final class ForPredicate extends CharMatcher { + + private final Predicate predicate; + + ForPredicate(Predicate predicate) { + this.predicate = checkNotNull(predicate); + } + + @Override + public boolean matches(char c) { + return predicate.apply(c); + } + + @SuppressWarnings("deprecation") // intentional; deprecation is for callers primarily + @Override + public boolean apply(Character character) { + return predicate.apply(checkNotNull(character)); + } + + @Override + public String toString() { + return "CharMatcher.forPredicate(" + predicate + ")"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Charsets.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Charsets.java new file mode 100644 index 0000000000000..011009fa5e197 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Charsets.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.nio.charset.Charset; + +/** + * Contains constant definitions for the six standard {@link Charset} instances, which are + * guaranteed to be supported by all Java platform implementations. + * + *

Assuming you're free to choose, note that {@link #UTF_8} is widely preferred. + * + *

See the Guava User Guide article on {@code Charsets}. + * + * @author Mike Bostock + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Charsets { + private Charsets() {} + + /** + * US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US). + * + *

Note for Java 7 and later: this constant should be treated as deprecated; use {@link + * java.nio.charset.StandardCharsets#US_ASCII} instead. + */ + @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset US_ASCII = Charset.forName("US-ASCII"); + + /** + * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). + * + *

Note for Java 7 and later: this constant should be treated as deprecated; use {@link + * java.nio.charset.StandardCharsets#ISO_8859_1} instead. + */ + public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + + /** + * UTF-8: eight-bit UCS Transformation Format. + * + *

Note for Java 7 and later: this constant should be treated as deprecated; use {@link + * java.nio.charset.StandardCharsets#UTF_8} instead. + */ + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + /** + * UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order. + * + *

Note for Java 7 and later: this constant should be treated as deprecated; use {@link + * java.nio.charset.StandardCharsets#UTF_16BE} instead. + */ + @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); + + /** + * UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order. + * + *

Note for Java 7 and later: this constant should be treated as deprecated; use {@link + * java.nio.charset.StandardCharsets#UTF_16LE} instead. + */ + @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); + + /** + * UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order + * mark. + * + *

Note for Java 7 and later: this constant should be treated as deprecated; use {@link + * java.nio.charset.StandardCharsets#UTF_16} instead. + */ + @J2ktIncompatible @GwtIncompatible // Charset not supported by GWT + public static final Charset UTF_16 = Charset.forName("UTF-16"); + + /* + * Please do not add new Charset references to this class, unless those character encodings are + * part of the set required to be supported by all Java platform implementations! Any Charsets + * initialized here may cause unexpected delays when this class is loaded. See the Charset + * Javadocs for the list of built-in character encodings. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CommonMatcher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CommonMatcher.java new file mode 100644 index 0000000000000..185127f1c6df7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CommonMatcher.java @@ -0,0 +1,43 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * The subset of the {@link java.util.regex.Matcher} API which is used by this package, and also + * shared with the {@code re2j} library. For internal use only. Please refer to the {@code Matcher} + * javadoc for details. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class CommonMatcher { + public abstract boolean matches(); + + public abstract boolean find(); + + public abstract boolean find(int index); + + public abstract String replaceAll(String replacement); + + public abstract int end(); + + public abstract int start(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CommonPattern.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CommonPattern.java new file mode 100644 index 0000000000000..be25d1839c428 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/CommonPattern.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * The subset of the {@link java.util.regex.Pattern} API which is used by this package, and also + * shared with the {@code re2j} library. For internal use only. Please refer to the {@code Pattern} + * javadoc for details. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class CommonPattern { + public abstract CommonMatcher matcher(CharSequence t); + + public abstract String pattern(); + + public abstract int flags(); + + // Re-declare this as abstract to force subclasses to override. + @Override + public abstract String toString(); + + public static CommonPattern compile(String pattern) { + return Platform.compilePattern(pattern); + } + + public static boolean isPcreLike() { + return Platform.patternCompilerIsPcreLike(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Converter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Converter.java new file mode 100644 index 0000000000000..abd4cace03b2a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Converter.java @@ -0,0 +1,600 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A function from {@code A} to {@code B} with an associated reverse function from {@code B} + * to {@code A}; used for converting back and forth between different representations of the same + * information. + * + *

Invertibility

+ * + *

The reverse operation may be a strict inverse (meaning that {@code + * converter.reverse().convert(converter.convert(a)).equals(a)} is always true). However, it is very + * common (perhaps more common) for round-trip conversion to be lossy. Consider an + * example round-trip using {@link + * org.apache.iotdb.google.common.primitives.Doubles#stringConverter}: + * + *

    + *
  1. {@code stringConverter().convert("1.00")} returns the {@code Double} value {@code 1.0} + *
  2. {@code stringConverter().reverse().convert(1.0)} returns the string {@code "1.0"} -- + * not the same string ({@code "1.00"}) we started with + *
+ * + *

Note that it should still be the case that the round-tripped and original objects are + * similar. + * + *

Nullability

+ * + *

A converter always converts {@code null} to {@code null} and non-null references to non-null + * references. It would not make sense to consider {@code null} and a non-null reference to be + * "different representations of the same information", since one is distinguishable from + * missing information and the other is not. The {@link #convert} method handles this null + * behavior for all converters; implementations of {@link #doForward} and {@link #doBackward} are + * guaranteed to never be passed {@code null}, and must never return {@code null}. + * + *

Common ways to use

+ * + *

Getting a converter: + * + *

    + *
  • Use a provided converter implementation, such as {@link Enums#stringConverter}, {@link + * org.apache.iotdb.google.common.primitives.Ints#stringConverter Ints.stringConverter} or the + * {@linkplain #reverse reverse} views of these. + *
  • Convert between specific preset values using {@link + * org.apache.iotdb.google.common.collect.Maps#asConverter Maps.asConverter}. For example, use + * this to create a "fake" converter for a unit test. It is unnecessary (and confusing) to + * mock the {@code Converter} type using a mocking framework. + *
  • Extend this class and implement its {@link #doForward} and {@link #doBackward} methods. + *
  • Java 8 users: you may prefer to pass two lambda expressions or method references to + * the {@link #from from} factory method. + *
+ * + *

Using a converter: + * + *

    + *
  • Convert one instance in the "forward" direction using {@code converter.convert(a)}. + *
  • Convert multiple instances "forward" using {@code converter.convertAll(as)}. + *
  • Convert in the "backward" direction using {@code converter.reverse().convert(b)} or {@code + * converter.reverse().convertAll(bs)}. + *
  • Use {@code converter} or {@code converter.reverse()} anywhere a {@link + * java.util.function.Function} is accepted (for example {@link java.util.stream.Stream#map + * Stream.map}). + *
  • Do not call {@link #doForward} or {@link #doBackward} directly; these exist only to + * be overridden. + *
+ * + *

Example

+ * + *
+ *   return new Converter<Integer, String>() {
+ *     protected String doForward(Integer i) {
+ *       return Integer.toHexString(i);
+ *     }
+ *
+ *     protected Integer doBackward(String s) {
+ *       return parseUnsignedInt(s, 16);
+ *     }
+ *   };
+ * + *

An alternative using Java 8: + * + *

{@code
+ * return Converter.from(
+ *     Integer::toHexString,
+ *     s -> parseUnsignedInt(s, 16));
+ * }
+ * + * @author Mike Ward + * @author Kurt Alfred Kluever + * @author Gregory Kick + * @since 16.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +/* + * 1. The type parameter is rather than so that we can use T in the + * doForward and doBackward methods to indicate that the parameter cannot be null. (We also take + * advantage of that for convertAll, as discussed on that method.) + * + * 2. The supertype of this class could be `Function<@Nullable A, @Nullable B>`, since + * Converter.apply (like Converter.convert) is capable of accepting null inputs. However, a + * supertype of `Function` turns out to be massively more useful to callers in practice: They + * want their output to be non-null in operations like `stream.map(myConverter)`, and we can + * guarantee that as long as we also require the input type to be non-null[*] (which is a + * requirement that existing callers already fulfill). + * + * Disclaimer: Part of the reason that callers are so well adapted to `Function` may be that + * that is how the signature looked even prior to this comment! So naturally any change can break + * existing users, but it can't *fix* existing users because any users who needed + * `Function<@Nullable A, @Nullable B>` already had to find a workaround. Still, there is a *ton* of + * fallout from trying to switch. I would be shocked if the switch would offer benefits to anywhere + * near enough users to justify the costs. + * + * Fortunately, if anyone does want to use a Converter as a `Function<@Nullable A, @Nullable B>`, + * it's easy to get one: `converter::convert`. + * + * [*] In annotating this class, we're ignoring LegacyConverter. + */ +public abstract class Converter implements Function { + private final boolean handleNullAutomatically; + + // We lazily cache the reverse view to avoid allocating on every call to reverse(). + private transient Converter reverse; + + /** Constructor for use by subclasses. */ + protected Converter() { + this(true); + } + + /** Constructor used only by {@code LegacyConverter} to suspend automatic null-handling. */ + Converter(boolean handleNullAutomatically) { + this.handleNullAutomatically = handleNullAutomatically; + } + + // SPI methods (what subclasses must implement) + + /** + * Returns a representation of {@code a} as an instance of type {@code B}. If {@code a} cannot be + * converted, an unchecked exception (such as {@link IllegalArgumentException}) should be thrown. + * + * @param a the instance to convert; will never be null + * @return the converted instance; must not be null + */ + protected abstract B doForward(A a); + + /** + * Returns a representation of {@code b} as an instance of type {@code A}. If {@code b} cannot be + * converted, an unchecked exception (such as {@link IllegalArgumentException}) should be thrown. + * + * @param b the instance to convert; will never be null + * @return the converted instance; must not be null + * @throws UnsupportedOperationException if backward conversion is not implemented; this should be + * very rare. Note that if backward conversion is not only unimplemented but + * unimplementable (for example, consider a {@code Converter}), + * then this is not logically a {@code Converter} at all, and should just implement {@link + * Function}. + */ + protected abstract A doBackward(B b); + + // API (consumer-side) methods + + /** + * Returns a representation of {@code a} as an instance of type {@code B}. + * + * @return the converted value; is null if and only if {@code a} is null + */ + public final B convert(A a) { + return correctedDoForward(a); + } + + B correctedDoForward(A a) { + if (handleNullAutomatically) { + // TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert? + return a == null ? null : checkNotNull(doForward(a)); + } else { + return unsafeDoForward(a); + } + } + + A correctedDoBackward(B b) { + if (handleNullAutomatically) { + // TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert? + return b == null ? null : checkNotNull(doBackward(b)); + } else { + return unsafeDoBackward(b); + } + } + + /* + * LegacyConverter violates the contract of Converter by allowing its doForward and doBackward + * methods to accept null. We could avoid having unchecked casts in Converter.java itself if we + * could perform a cast to LegacyConverter, but we can't because it's an internal-only class. + * + * TODO(cpovirk): So make it part of the open-source build, albeit package-private there? + * + * So we use uncheckedCastNullableTToT here. This is a weird usage of that method: The method is + * documented as being for use with type parameters that have parametric nullness. But Converter's + * type parameters do not. Still, we use it here so that we can suppress a warning at a smaller + * level than the whole method but without performing a runtime null check. That way, we can still + * pass null inputs to LegacyConverter, and it can violate the contract of Converter. + * + * TODO(cpovirk): Could this be simplified if we modified implementations of LegacyConverter to + * override methods (probably called "unsafeDoForward" and "unsafeDoBackward") with the same + * signatures as the methods below, rather than overriding the same doForward and doBackward + * methods as implementations of normal converters do? + * + * But no matter what we do, it's worth remembering that the resulting code is going to be unsound + * in the presence of LegacyConverter, at least in the case of users who view the converter as a + * Function or who call convertAll (and for any checkers that apply @PolyNull-like semantics + * to Converter.convert). So maybe we don't want to think too hard about how to prevent our + * checkers from issuing errors related to LegacyConverter, since it turns out that + * LegacyConverter does violate the assumptions we make elsewhere. + */ + + private B unsafeDoForward(A a) { + return doForward(uncheckedCastNullableTToT(a)); + } + + private A unsafeDoBackward(B b) { + return doBackward(uncheckedCastNullableTToT(b)); + } + + /** + * Returns an iterable that applies {@code convert} to each element of {@code fromIterable}. The + * conversion is done lazily. + * + *

The returned iterable's iterator supports {@code remove()} if the input iterator does. After + * a successful {@code remove()} call, {@code fromIterable} no longer contains the corresponding + * element. + */ + /* + * Just as Converter could implement `Function<@Nullable A, @Nullable B>` instead of `Function`, convertAll could accept and return iterables with nullable element types. In both cases, + * we've chosen to instead use a signature that benefits existing users -- and is still safe. + * + * For convertAll, I haven't looked as closely at *how* much existing users benefit, so we should + * keep an eye out for problems that new users encounter. Note also that convertAll could support + * both use cases by using @PolyNull. (By contrast, we can't use @PolyNull for our superinterface + * (`implements Function<@PolyNull A, @PolyNull B>`), at least as far as I know.) + */ + public Iterable convertAll(Iterable fromIterable) { + checkNotNull(fromIterable, "fromIterable"); + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + private final Iterator fromIterator = fromIterable.iterator(); + + @Override + public boolean hasNext() { + return fromIterator.hasNext(); + } + + @Override + public B next() { + return convert(fromIterator.next()); + } + + @Override + public void remove() { + fromIterator.remove(); + } + }; + } + }; + } + + /** + * Returns the reversed view of this converter, which converts {@code this.convert(a)} back to a + * value roughly equivalent to {@code a}. + * + *

The returned converter is serializable if {@code this} converter is. + * + *

Note: you should not override this method. It is non-final for legacy reasons. + */ + public Converter reverse() { + Converter result = reverse; + return (result == null) ? reverse = new ReverseConverter<>(this) : result; + } + + private static final class ReverseConverter extends Converter + implements Serializable { + final Converter original; + + ReverseConverter(Converter original) { + this.original = original; + } + + /* + * These gymnastics are a little confusing. Basically this class has neither legacy nor + * non-legacy behavior; it just needs to let the behavior of the backing converter shine + * through. So, we override the correctedDo* methods, after which the do* methods should never + * be reached. + */ + + @Override + protected A doForward(B b) { + throw new AssertionError(); + } + + @Override + protected B doBackward(A a) { + throw new AssertionError(); + } + + @Override + A correctedDoForward(B b) { + return original.correctedDoBackward(b); + } + + @Override + B correctedDoBackward(A a) { + return original.correctedDoForward(a); + } + + @Override + public Converter reverse() { + return original; + } + + @Override + public boolean equals(Object object) { + if (object instanceof ReverseConverter) { + ReverseConverter that = (ReverseConverter) object; + return this.original.equals(that.original); + } + return false; + } + + @Override + public int hashCode() { + return ~original.hashCode(); + } + + @Override + public String toString() { + return original + ".reverse()"; + } + + private static final long serialVersionUID = 0L; + } + + /** + * Returns a converter whose {@code convert} method applies {@code secondConverter} to the result + * of this converter. Its {@code reverse} method applies the converters in reverse order. + * + *

The returned converter is serializable if {@code this} converter and {@code secondConverter} + * are. + */ + public final Converter andThen(Converter secondConverter) { + return doAndThen(secondConverter); + } + + /** Package-private non-final implementation of andThen() so only we can override it. */ + Converter doAndThen(Converter secondConverter) { + return new ConverterComposition<>(this, checkNotNull(secondConverter)); + } + + private static final class ConverterComposition extends Converter + implements Serializable { + final Converter first; + final Converter second; + + ConverterComposition(Converter first, Converter second) { + this.first = first; + this.second = second; + } + + /* + * These gymnastics are a little confusing. Basically this class has neither legacy nor + * non-legacy behavior; it just needs to let the behaviors of the backing converters shine + * through (which might even differ from each other!). So, we override the correctedDo* methods, + * after which the do* methods should never be reached. + */ + + @Override + protected C doForward(A a) { + throw new AssertionError(); + } + + @Override + protected A doBackward(C c) { + throw new AssertionError(); + } + + @Override + C correctedDoForward(A a) { + return second.correctedDoForward(first.correctedDoForward(a)); + } + + @Override + A correctedDoBackward(C c) { + return first.correctedDoBackward(second.correctedDoBackward(c)); + } + + @Override + public boolean equals(Object object) { + if (object instanceof ConverterComposition) { + ConverterComposition that = (ConverterComposition) object; + return this.first.equals(that.first) && this.second.equals(that.second); + } + return false; + } + + @Override + public int hashCode() { + return 31 * first.hashCode() + second.hashCode(); + } + + @Override + public String toString() { + return first + ".andThen(" + second + ")"; + } + + private static final long serialVersionUID = 0L; + } + + /** + * @deprecated Provided to satisfy the {@code Function} interface; use {@link #convert} instead. + */ + @Deprecated + @Override + public final B apply(A a) { + /* + * Given that we declare this method as accepting and returning non-nullable values (because we + * implement Function, as discussed in a class-level comment), it would make some sense to + * perform runtime null checks on the input and output. (That would also make NullPointerTester + * happy!) However, since we didn't do that for many years, we're not about to start now. + * (Runtime checks could be particularly bad for users of LegacyConverter.) + * + * Luckily, our nullness checker is smart enough to realize that `convert` has @PolyNull-like + * behavior, so it knows that `convert(a)` returns a non-nullable value, and we don't need to + * perform even a cast, much less a runtime check. + * + * All that said, don't forget that everyone should call converter.convert() instead of + * converter.apply(), anyway. If clients use only converter.convert(), then their nullness + * checkers are unlikely to ever look at the annotations on this declaration. + * + * Historical note: At one point, we'd declared this method as accepting and returning nullable + * values. For details on that, see earlier revisions of this file. + */ + return convert(a); + } + + /** + * Indicates whether another object is equal to this converter. + * + *

Most implementations will have no reason to override the behavior of {@link Object#equals}. + * However, an implementation may also choose to return {@code true} whenever {@code object} is a + * {@link Converter} that it considers interchangeable with this one. "Interchangeable" + * typically means that {@code Objects.equal(this.convert(a), that.convert(a))} is true for + * all {@code a} of type {@code A} (and similarly for {@code reverse}). Note that a {@code false} + * result from this method does not imply that the converters are known not to be + * interchangeable. + */ + @Override + public boolean equals(Object object) { + return super.equals(object); + } + + // Static converters + + /** + * Returns a converter based on separate forward and backward functions. This is useful if the + * function instances already exist, or so that you can supply lambda expressions. If those + * circumstances don't apply, you probably don't need to use this; subclass {@code Converter} and + * implement its {@link #doForward} and {@link #doBackward} methods directly. + * + *

These functions will never be passed {@code null} and must not under any circumstances + * return {@code null}. If a value cannot be converted, the function should throw an unchecked + * exception (typically, but not necessarily, {@link IllegalArgumentException}). + * + *

The returned converter is serializable if both provided functions are. + * + * @since 17.0 + */ + public static Converter from( + Function forwardFunction, + Function backwardFunction) { + return new FunctionBasedConverter<>(forwardFunction, backwardFunction); + } + + private static final class FunctionBasedConverter extends Converter + implements Serializable { + private final Function forwardFunction; + private final Function backwardFunction; + + private FunctionBasedConverter( + Function forwardFunction, + Function backwardFunction) { + this.forwardFunction = checkNotNull(forwardFunction); + this.backwardFunction = checkNotNull(backwardFunction); + } + + @Override + protected B doForward(A a) { + return forwardFunction.apply(a); + } + + @Override + protected A doBackward(B b) { + return backwardFunction.apply(b); + } + + @Override + public boolean equals(Object object) { + if (object instanceof FunctionBasedConverter) { + FunctionBasedConverter that = (FunctionBasedConverter) object; + return this.forwardFunction.equals(that.forwardFunction) + && this.backwardFunction.equals(that.backwardFunction); + } + return false; + } + + @Override + public int hashCode() { + return forwardFunction.hashCode() * 31 + backwardFunction.hashCode(); + } + + @Override + public String toString() { + return "Converter.from(" + forwardFunction + ", " + backwardFunction + ")"; + } + } + + /** Returns a serializable converter that always converts or reverses an object to itself. */ + @SuppressWarnings("unchecked") // implementation is "fully variant" + public static Converter identity() { + return (IdentityConverter) IdentityConverter.INSTANCE; + } + + /** + * A converter that always converts or reverses an object to itself. Note that T is now a + * "pass-through type". + */ + private static final class IdentityConverter extends Converter implements Serializable { + static final IdentityConverter INSTANCE = new IdentityConverter<>(); + + @Override + protected T doForward(T t) { + return t; + } + + @Override + protected T doBackward(T t) { + return t; + } + + @Override + public IdentityConverter reverse() { + return this; + } + + @Override + Converter doAndThen(Converter otherConverter) { + return checkNotNull(otherConverter, "otherConverter"); + } + + /* + * We *could* override convertAll() to return its input, but it's a rather pointless + * optimization and opened up a weird type-safety problem. + */ + + @Override + public String toString() { + return "Converter.identity()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0L; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Defaults.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Defaults.java new file mode 100644 index 0000000000000..2a31a46d0af1a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Defaults.java @@ -0,0 +1,71 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * This class provides default values for all Java types, as defined by the JLS. + * + * @author Ben Yu + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Defaults { + private Defaults() {} + + private static final Double DOUBLE_DEFAULT = 0d; + private static final Float FLOAT_DEFAULT = 0f; + + /** + * Returns the default value of {@code type} as defined by JLS --- {@code 0} for numbers, {@code + * false} for {@code boolean} and {@code '\0'} for {@code char}. For non-primitive types and + * {@code void}, {@code null} is returned. + */ + @SuppressWarnings("unchecked") + public static T defaultValue(Class type) { + checkNotNull(type); + if (type.isPrimitive()) { + if (type == boolean.class) { + return (T) Boolean.FALSE; + } else if (type == char.class) { + return (T) Character.valueOf('\0'); + } else if (type == byte.class) { + return (T) Byte.valueOf((byte) 0); + } else if (type == short.class) { + return (T) Short.valueOf((short) 0); + } else if (type == int.class) { + return (T) Integer.valueOf(0); + } else if (type == long.class) { + return (T) Long.valueOf(0L); + } else if (type == float.class) { + return (T) FLOAT_DEFAULT; + } else if (type == double.class) { + return (T) DOUBLE_DEFAULT; + } + } + return null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..19366103a7574 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Enums.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Enums.java new file mode 100644 index 0000000000000..cea9318492f75 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Enums.java @@ -0,0 +1,160 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods for working with {@link Enum} instances. + * + * @author Steve McKay + * @since 9.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public final class Enums { + + private Enums() {} + + /** + * Returns the {@link Field} in which {@code enumValue} is defined. For example, to get the {@code + * Description} annotation on the {@code GOLF} constant of enum {@code Sport}, use {@code + * Enums.getField(Sport.GOLF).getAnnotation(Description.class)}. + * + * @since 12.0 + */ + @GwtIncompatible // reflection + public static Field getField(Enum enumValue) { + Class clazz = enumValue.getDeclaringClass(); + try { + return clazz.getDeclaredField(enumValue.name()); + } catch (NoSuchFieldException impossible) { + throw new AssertionError(impossible); + } + } + + /** + * Returns an optional enum constant for the given type, using {@link Enum#valueOf}. If the + * constant does not exist, {@link Optional#absent} is returned. A common use case is for parsing + * user input or falling back to a default enum constant. For example, {@code + * Enums.getIfPresent(Country.class, countryInput).or(Country.DEFAULT);} + * + * @since 12.0 + */ + public static > Optional getIfPresent(Class enumClass, String value) { + checkNotNull(enumClass); + checkNotNull(value); + return Platform.getEnumIfPresent(enumClass, value); + } + + @GwtIncompatible // java.lang.ref.WeakReference + private static final Map>, Map>>> + enumConstantCache = new WeakHashMap<>(); + + @GwtIncompatible // java.lang.ref.WeakReference + private static > Map>> populateCache( + Class enumClass) { + Map>> result = new HashMap<>(); + for (T enumInstance : EnumSet.allOf(enumClass)) { + result.put(enumInstance.name(), new WeakReference>(enumInstance)); + } + enumConstantCache.put(enumClass, result); + return result; + } + + @GwtIncompatible // java.lang.ref.WeakReference + static > Map>> getEnumConstants( + Class enumClass) { + synchronized (enumConstantCache) { + Map>> constants = enumConstantCache.get(enumClass); + if (constants == null) { + constants = populateCache(enumClass); + } + return constants; + } + } + + /** + * Returns a serializable converter that converts between strings and {@code enum} values of type + * {@code enumClass} using {@link Enum#valueOf(Class, String)} and {@link Enum#name()}. The + * converter will throw an {@code IllegalArgumentException} if the argument is not the name of any + * enum constant in the specified enum. + * + * @since 16.0 + */ + @GwtIncompatible + public static > Converter stringConverter(Class enumClass) { + return new StringConverter<>(enumClass); + } + + @GwtIncompatible + private static final class StringConverter> extends Converter + implements Serializable { + + private final Class enumClass; + + StringConverter(Class enumClass) { + this.enumClass = checkNotNull(enumClass); + } + + @Override + protected T doForward(String value) { + return Enum.valueOf(enumClass, value); + } + + @Override + protected String doBackward(T enumValue) { + return enumValue.name(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof StringConverter) { + StringConverter that = (StringConverter) object; + return this.enumClass.equals(that.enumClass); + } + return false; + } + + @Override + public int hashCode() { + return enumClass.hashCode(); + } + + @Override + public String toString() { + return "Enums.stringConverter(" + enumClass.getName() + ".class)"; + } + + private static final long serialVersionUID = 0L; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Equivalence.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Equivalence.java new file mode 100644 index 0000000000000..39418130c933c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Equivalence.java @@ -0,0 +1,400 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.function.BiPredicate; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A strategy for determining whether two instances are considered equivalent, and for computing + * hash codes in a manner consistent with that equivalence. Two examples of equivalences are the + * {@linkplain #identity() identity equivalence} and the {@linkplain #equals "equals" equivalence}. + * + * @author Bob Lee + * @author Ben Yu + * @author Gregory Kick + * @since 10.0 (mostly + * source-compatible since 4.0) + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +/* + * The type parameter is rather than so that we can use T in the + * doEquivalent and doHash methods to indicate that the parameter cannot be null. + */ +public abstract class Equivalence implements BiPredicate { + /** Constructor for use by subclasses. */ + protected Equivalence() {} + + /** + * Returns {@code true} if the given objects are considered equivalent. + * + *

This method describes an equivalence relation on object references, meaning that for + * all references {@code x}, {@code y}, and {@code z} (any of which may be null): + * + *

    + *
  • {@code equivalent(x, x)} is true (reflexive property) + *
  • {@code equivalent(x, y)} and {@code equivalent(y, x)} each return the same result + * (symmetric property) + *
  • If {@code equivalent(x, y)} and {@code equivalent(y, z)} are both true, then {@code + * equivalent(x, z)} is also true (transitive property) + *
+ * + *

Note that all calls to {@code equivalent(x, y)} are expected to return the same result as + * long as neither {@code x} nor {@code y} is modified. + */ + public final boolean equivalent(T a, T b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + return doEquivalent(a, b); + } + + /** + * @deprecated Provided only to satisfy the {@link BiPredicate} interface; use {@link #equivalent} + * instead. + * @since 21.0 + */ + @Deprecated + @Override + public final boolean test(T t, T u) { + return equivalent(t, u); + } + + /** + * Implemented by the user to determine whether {@code a} and {@code b} are considered equivalent, + * subject to the requirements specified in {@link #equivalent}. + * + *

This method should not be called except by {@link #equivalent}. When {@link #equivalent} + * calls this method, {@code a} and {@code b} are guaranteed to be distinct, non-null instances. + * + * @since 10.0 (previously, subclasses would override equivalent()) + */ + protected abstract boolean doEquivalent(T a, T b); + + /** + * Returns a hash code for {@code t}. + * + *

The {@code hash} has the following properties: + * + *

    + *
  • It is consistent: for any reference {@code x}, multiple invocations of {@code + * hash(x}} consistently return the same value provided {@code x} remains unchanged + * according to the definition of the equivalence. The hash need not remain consistent from + * one execution of an application to another execution of the same application. + *
  • It is distributable across equivalence: for any references {@code x} and {@code + * y}, if {@code equivalent(x, y)}, then {@code hash(x) == hash(y)}. It is not + * necessary that the hash be distributable across inequivalence. If {@code + * equivalence(x, y)} is false, {@code hash(x) == hash(y)} may still be true. + *
  • {@code hash(null)} is {@code 0}. + *
+ */ + public final int hash(T t) { + if (t == null) { + return 0; + } + return doHash(t); + } + + /** + * Implemented by the user to return a hash code for {@code t}, subject to the requirements + * specified in {@link #hash}. + * + *

This method should not be called except by {@link #hash}. When {@link #hash} calls this + * method, {@code t} is guaranteed to be non-null. + * + * @since 10.0 (previously, subclasses would override hash()) + */ + protected abstract int doHash(T t); + + /** + * Returns a new equivalence relation for {@code F} which evaluates equivalence by first applying + * {@code function} to the argument, then evaluating using {@code this}. That is, for any pair of + * non-null objects {@code x} and {@code y}, {@code equivalence.onResultOf(function).equivalent(a, + * b)} is true if and only if {@code equivalence.equivalent(function.apply(a), function.apply(b))} + * is true. + * + *

For example: + * + *

{@code
+   * Equivalence SAME_AGE = Equivalence.equals().onResultOf(GET_PERSON_AGE);
+   * }
+ * + *

{@code function} will never be invoked with a null value. + * + *

Note that {@code function} must be consistent according to {@code this} equivalence + * relation. That is, invoking {@link Function#apply} multiple times for a given value must return + * equivalent results. For example, {@code + * Equivalence.identity().onResultOf(Functions.toStringFunction())} is broken because it's not + * guaranteed that {@link Object#toString}) always returns the same string instance. + * + * @since 10.0 + */ + public final Equivalence onResultOf(Function function) { + return new FunctionalEquivalence<>(function, this); + } + + /** + * Returns a wrapper of {@code reference} that implements {@link Wrapper#equals(Object) + * Object.equals()} such that {@code wrap(a).equals(wrap(b))} if and only if {@code equivalent(a, + * b)}. + * + *

The returned object is serializable if both this {@code Equivalence} and {@code reference} + * are serializable (including when {@code reference} is null). + * + * @since 10.0 + */ + public final Wrapper wrap(@ParametricNullness S reference) { + return new Wrapper<>(this, reference); + } + + /** + * Wraps an object so that {@link #equals(Object)} and {@link #hashCode()} delegate to an {@link + * Equivalence}. + * + *

For example, given an {@link Equivalence} for {@link String strings} named {@code equiv} + * that tests equivalence using their lengths: + * + *

{@code
+   * equiv.wrap("a").equals(equiv.wrap("b")) // true
+   * equiv.wrap("a").equals(equiv.wrap("hello")) // false
+   * }
+ * + *

Note in particular that an equivalence wrapper is never equal to the object it wraps. + * + *

{@code
+   * equiv.wrap(obj).equals(obj) // always false
+   * }
+ * + * @since 10.0 + */ + public static final class Wrapper implements Serializable { + /* + * Equivalence's type argument is always non-nullable: Equivalence, never + * Equivalence<@Nullable Number>. That can still produce wrappers of various types -- + * Wrapper, Wrapper, Wrapper<@Nullable Integer>, etc. If we used just + * Equivalence below, no type could satisfy both that bound and T's own + * bound. With this type, they have some overlap: in our example, Equivalence + * and Equivalence. + */ + private final Equivalence equivalence; + + @ParametricNullness private final T reference; + + private Wrapper(Equivalence equivalence, @ParametricNullness T reference) { + this.equivalence = checkNotNull(equivalence); + this.reference = reference; + } + + /** Returns the (possibly null) reference wrapped by this instance. */ + @ParametricNullness + public T get() { + return reference; + } + + /** + * Returns {@code true} if {@link Equivalence#equivalent(Object, Object)} applied to the wrapped + * references is {@code true} and both wrappers use the {@link Object#equals(Object) same} + * equivalence. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Wrapper) { + Wrapper that = (Wrapper) obj; // note: not necessarily a Wrapper + + if (this.equivalence.equals(that.equivalence)) { + /* + * We'll accept that as sufficient "proof" that either equivalence should be able to + * handle either reference, so it's safe to circumvent compile-time type checking. + */ + @SuppressWarnings("unchecked") + Equivalence equivalence = (Equivalence) this.equivalence; + return equivalence.equivalent(this.reference, that.reference); + } + } + return false; + } + + /** Returns the result of {@link Equivalence#hash(Object)} applied to the wrapped reference. */ + @Override + public int hashCode() { + return equivalence.hash(reference); + } + + /** + * Returns a string representation for this equivalence wrapper. The form of this string + * representation is not specified. + */ + @Override + public String toString() { + return equivalence + ".wrap(" + reference + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an equivalence over iterables based on the equivalence of their elements. More + * specifically, two iterables are considered equivalent if they both contain the same number of + * elements, and each pair of corresponding elements is equivalent according to {@code this}. Null + * iterables are equivalent to one another. + * + *

Note that this method performs a similar function for equivalences as {@link + * org.apache.iotdb.google.common.collect.Ordering#lexicographical} does for orderings. + * + *

The returned object is serializable if this object is serializable. + * + * @since 10.0 + */ + @GwtCompatible(serializable = true) + public final Equivalence> pairwise() { + // Ideally, the returned equivalence would support Iterable. However, + // the need for this is so rare that it's not worth making callers deal with the ugly wildcard. + return new PairwiseEquivalence<>(this); + } + + /** + * Returns a predicate that evaluates to true if and only if the input is equivalent to {@code + * target} according to this equivalence relation. + * + * @since 10.0 + */ + public final Predicate equivalentTo(T target) { + return new EquivalentToPredicate(this, target); + } + + private static final class EquivalentToPredicate implements Predicate, Serializable { + + private final Equivalence equivalence; + private final T target; + + EquivalentToPredicate(Equivalence equivalence, T target) { + this.equivalence = checkNotNull(equivalence); + this.target = target; + } + + @Override + public boolean apply(T input) { + return equivalence.equivalent(input, target); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof EquivalentToPredicate) { + EquivalentToPredicate that = (EquivalentToPredicate) obj; + return equivalence.equals(that.equivalence) && Objects.equal(target, that.target); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(equivalence, target); + } + + @Override + public String toString() { + return equivalence + ".equivalentTo(" + target + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an equivalence that delegates to {@link Object#equals} and {@link Object#hashCode}. + * {@link Equivalence#equivalent} returns {@code true} if both values are null, or if neither + * value is null and {@link Object#equals} returns {@code true}. {@link Equivalence#hash} returns + * {@code 0} if passed a null value. + * + * @since 13.0 + * @since 8.0 (in Equivalences with null-friendly behavior) + * @since 4.0 (in Equivalences) + */ + public static Equivalence equals() { + return Equals.INSTANCE; + } + + /** + * Returns an equivalence that uses {@code ==} to compare values and {@link + * System#identityHashCode(Object)} to compute the hash code. {@link Equivalence#equivalent} + * returns {@code true} if {@code a == b}, including in the case that a and b are both null. + * + * @since 13.0 + * @since 4.0 (in Equivalences) + */ + public static Equivalence identity() { + return Identity.INSTANCE; + } + + static final class Equals extends Equivalence implements Serializable { + + static final Equals INSTANCE = new Equals(); + + @Override + protected boolean doEquivalent(Object a, Object b) { + return a.equals(b); + } + + @Override + protected int doHash(Object o) { + return o.hashCode(); + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + static final class Identity extends Equivalence implements Serializable { + + static final Identity INSTANCE = new Identity(); + + @Override + protected boolean doEquivalent(Object a, Object b) { + return false; + } + + @Override + protected int doHash(Object o) { + return System.identityHashCode(o); + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ExtraObjectsMethodsForWeb.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ExtraObjectsMethodsForWeb.java new file mode 100644 index 0000000000000..e212e32380645 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ExtraObjectsMethodsForWeb.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular + * version. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ExtraObjectsMethodsForWeb {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizablePhantomReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizablePhantomReference.java new file mode 100644 index 0000000000000..6e42886b09de1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizablePhantomReference.java @@ -0,0 +1,53 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +/** + * Phantom reference with a {@code finalizeReferent()} method which a background thread invokes + * after the garbage collector reclaims the referent. This is a simpler alternative to using a + * {@link ReferenceQueue}. + * + *

Unlike a normal phantom reference, this reference will be cleared automatically. + * + * @author Bob Lee + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class FinalizablePhantomReference extends PhantomReference + implements FinalizableReference { + /** + * Constructs a new finalizable phantom reference. + * + * @param referent to phantom reference + * @param queue that should finalize the referent + */ + protected FinalizablePhantomReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableReference.java new file mode 100644 index 0000000000000..0f13988759f7c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableReference.java @@ -0,0 +1,42 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Implemented by references that have code to run after garbage collection of their referents. + * + * @see FinalizableReferenceQueue + * @author Bob Lee + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface FinalizableReference { + /** + * Invoked on a background thread after the referent has been garbage collected unless security + * restrictions prevented starting a background thread, in which case this method is invoked when + * new references are created. + */ + void finalizeReferent(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableReferenceQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableReferenceQueue.java new file mode 100644 index 0000000000000..91051bf1257e8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableReferenceQueue.java @@ -0,0 +1,360 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A reference queue with an associated background thread that dequeues references and invokes + * {@link FinalizableReference#finalizeReferent()} on them. + * + *

Keep a strong reference to this object until all of the associated referents have been + * finalized. If this object is garbage collected earlier, the backing thread will not invoke {@code + * finalizeReferent()} on the remaining references. + * + *

As an example of how this is used, imagine you have a class {@code MyServer} that creates a + * {@link java.net.ServerSocket ServerSocket}, and you would like to ensure that the {@code + * ServerSocket} is closed even if the {@code MyServer} object is garbage-collected without calling + * its {@code close} method. You could use a finalizer to accomplish this, but that has a + * number of well-known problems. Here is how you might use this class instead: + * + *

{@code
+ * public class MyServer implements Closeable {
+ *   private static final FinalizableReferenceQueue frq = new FinalizableReferenceQueue();
+ *   // You might also share this between several objects.
+ *
+ *   private static final Set> references = Sets.newConcurrentHashSet();
+ *   // This ensures that the FinalizablePhantomReference itself is not garbage-collected.
+ *
+ *   private final ServerSocket serverSocket;
+ *
+ *   private MyServer(...) {
+ *     ...
+ *     this.serverSocket = new ServerSocket(...);
+ *     ...
+ *   }
+ *
+ *   public static MyServer create(...) {
+ *     MyServer myServer = new MyServer(...);
+ *     final ServerSocket serverSocket = myServer.serverSocket;
+ *     Reference reference = new FinalizablePhantomReference(myServer, frq) {
+ *       public void finalizeReferent() {
+ *         references.remove(this):
+ *         if (!serverSocket.isClosed()) {
+ *           ...log a message about how nobody called close()...
+ *           try {
+ *             serverSocket.close();
+ *           } catch (IOException e) {
+ *             ...
+ *           }
+ *         }
+ *       }
+ *     };
+ *     references.add(reference);
+ *     return myServer;
+ *   }
+ *
+ *   public void close() {
+ *     serverSocket.close();
+ *   }
+ * }
+ * }
+ * + * @author Bob Lee + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public class FinalizableReferenceQueue implements Closeable { + /* + * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a + * map built by MapMaker) no longer has a strong reference to this object, the garbage collector + * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the + * Finalizer to stop. + * + * If this library is loaded in the system class loader, FinalizableReferenceQueue can load + * Finalizer directly with no problems. + * + * If this library is loaded in an application class loader, it's important that Finalizer not + * have a strong reference back to the class loader. Otherwise, you could have a graph like this: + * + * Finalizer Thread runs instance of -> Finalizer.class loaded by -> Application class loader + * which loaded -> ReferenceMap.class which has a static -> FinalizableReferenceQueue instance + * + * Even if no other references to classes from the application class loader remain, the Finalizer + * thread keeps an indirect strong reference to the queue in ReferenceMap, which keeps the + * Finalizer running, and as a result, the application class loader can never be reclaimed. + * + * This means that dynamically loaded web applications and OSGi bundles can't be unloaded. + * + * If the library is loaded in an application class loader, we try to break the cycle by loading + * Finalizer in its own independent class loader: + * + * System class loader -> Application class loader -> ReferenceMap -> FinalizableReferenceQueue -> + * etc. -> Decoupled class loader -> Finalizer + * + * Now, Finalizer no longer keeps an indirect strong reference to the static + * FinalizableReferenceQueue field in ReferenceMap. The application class loader can be reclaimed + * at which point the Finalizer thread will stop and its decoupled class loader can also be + * reclaimed. + * + * If any of this fails along the way, we fall back to loading Finalizer directly in the + * application class loader. + * + * NOTE: The tests for this behavior (FinalizableReferenceQueueClassLoaderUnloadingTest) fail + * strangely when run in JDK 9. We are considering this a known issue. Please see + * https://github.com/google/guava/issues/3086 for more information. + */ + + private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); + + private static final String FINALIZER_CLASS_NAME = + "org.apache.iotdb.google.common.base.internal.Finalizer"; + + /** Reference to Finalizer.startFinalizer(). */ + private static final Method startFinalizer; + + static { + Class finalizer = + loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader()); + startFinalizer = getStartFinalizer(finalizer); + } + + /** The actual reference queue that our background thread will poll. */ + final ReferenceQueue queue; + + final PhantomReference frqRef; + + /** Whether or not the background thread started successfully. */ + final boolean threadStarted; + + /** Constructs a new queue. */ + public FinalizableReferenceQueue() { + // We could start the finalizer lazily, but I'd rather it blow up early. + queue = new ReferenceQueue<>(); + frqRef = new PhantomReference<>(this, queue); + boolean threadStarted = false; + try { + startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); + threadStarted = true; + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); // startFinalizer() is public + } catch (Throwable t) { + logger.log( + Level.INFO, + "Failed to start reference finalizer thread." + + " Reference cleanup will only occur when new references are created.", + t); + } + + this.threadStarted = threadStarted; + } + + @Override + public void close() { + frqRef.enqueue(); + cleanUp(); + } + + /** + * Repeatedly dequeues references from the queue and invokes {@link + * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a + * no-op if the background thread was created successfully. + */ + void cleanUp() { + if (threadStarted) { + return; + } + + Reference reference; + while ((reference = queue.poll()) != null) { + /* + * This is for the benefit of phantom references. Weak and soft references will have already + * been cleared by this point. + */ + reference.clear(); + try { + ((FinalizableReference) reference).finalizeReferent(); + } catch (Throwable t) { + logger.log(Level.SEVERE, "Error cleaning up after reference.", t); + } + } + } + + /** + * Iterates through the given loaders until it finds one that can load Finalizer. + * + * @return Finalizer.class + */ + private static Class loadFinalizer(FinalizerLoader... loaders) { + for (FinalizerLoader loader : loaders) { + Class finalizer = loader.loadFinalizer(); + if (finalizer != null) { + return finalizer; + } + } + + throw new AssertionError(); + } + + /** Loads Finalizer.class. */ + interface FinalizerLoader { + + /** + * Returns Finalizer.class or null if this loader shouldn't or can't load it. + * + * @throws SecurityException if we don't have the appropriate privileges + */ + Class loadFinalizer(); + } + + /** + * Tries to load Finalizer from the system class loader. If Finalizer is in the system class path, + * we needn't create a separate loader. + */ + static class SystemLoader implements FinalizerLoader { + // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable + // finding Finalizer on the system class path even if it is there. + @VisibleForTesting static boolean disabled; + + @Override + public Class loadFinalizer() { + if (disabled) { + return null; + } + ClassLoader systemLoader; + try { + systemLoader = ClassLoader.getSystemClassLoader(); + } catch (SecurityException e) { + logger.info("Not allowed to access system class loader."); + return null; + } + if (systemLoader != null) { + try { + return systemLoader.loadClass(FINALIZER_CLASS_NAME); + } catch (ClassNotFoundException e) { + // Ignore. Finalizer is simply in a child class loader. + return null; + } + } else { + return null; + } + } + } + + /** + * Try to load Finalizer in its own class loader. If Finalizer's thread had a direct reference to + * our class loader (which could be that of a dynamically loaded web application or OSGi bundle), + * it would prevent our class loader from getting garbage collected. + */ + static class DecoupledLoader implements FinalizerLoader { + private static final String LOADING_ERROR = + "Could not load Finalizer in its own class loader. Loading Finalizer in the current class " + + "loader instead. As a result, you will not be able to garbage collect this class " + + "loader. To support reclaiming this class loader, either resolve the underlying " + + "issue, or move Guava to your system class path."; + + @Override + public Class loadFinalizer() { + try { + /* + * We use URLClassLoader because it's the only concrete class loader implementation in the + * JDK. If we used our own ClassLoader subclass, Finalizer would indirectly reference this + * class loader: + * + * Finalizer.class -> CustomClassLoader -> CustomClassLoader.class -> This class loader + * + * System class loader will (and must) be the parent. + */ + ClassLoader finalizerLoader = newLoader(getBaseUrl()); + return finalizerLoader.loadClass(FINALIZER_CLASS_NAME); + } catch (Exception e) { + logger.log(Level.WARNING, LOADING_ERROR, e); + return null; + } + } + + /** Gets URL for base of path containing Finalizer.class. */ + URL getBaseUrl() throws IOException { + // Find URL pointing to Finalizer.class file. + String finalizerPath = FINALIZER_CLASS_NAME.replace('.', '/') + ".class"; + URL finalizerUrl = getClass().getClassLoader().getResource(finalizerPath); + if (finalizerUrl == null) { + throw new FileNotFoundException(finalizerPath); + } + + // Find URL pointing to base of class path. + String urlString = finalizerUrl.toString(); + if (!urlString.endsWith(finalizerPath)) { + throw new IOException("Unsupported path style: " + urlString); + } + urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); + return new URL(finalizerUrl, urlString); + } + + /** Creates a class loader with the given base URL as its classpath. */ + URLClassLoader newLoader(URL base) { + // We use the bootstrap class loader as the parent because Finalizer by design uses + // only standard Java classes. That also means that FinalizableReferenceQueueTest + // doesn't pick up the wrong version of the Finalizer class. + return new URLClassLoader(new URL[] {base}, null); + } + } + + /** + * Loads Finalizer directly using the current class loader. We won't be able to garbage collect + * this class loader, but at least the world doesn't end. + */ + static class DirectLoader implements FinalizerLoader { + @Override + public Class loadFinalizer() { + try { + return Class.forName(FINALIZER_CLASS_NAME); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + } + + /** Looks up Finalizer.startFinalizer(). */ + static Method getStartFinalizer(Class finalizer) { + try { + return finalizer.getMethod( + "startFinalizer", Class.class, ReferenceQueue.class, PhantomReference.class); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableSoftReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableSoftReference.java new file mode 100644 index 0000000000000..e6fd6c4bad99b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableSoftReference.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; + +/** + * Soft reference with a {@code finalizeReferent()} method which a background thread invokes after + * the garbage collector reclaims the referent. This is a simpler alternative to using a {@link + * ReferenceQueue}. + * + * @author Bob Lee + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class FinalizableSoftReference extends SoftReference + implements FinalizableReference { + /** + * Constructs a new finalizable soft reference. + * + * @param referent to softly reference + * @param queue that should finalize the referent + */ + protected FinalizableSoftReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableWeakReference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableWeakReference.java new file mode 100644 index 0000000000000..9e649f9b5d6ae --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FinalizableWeakReference.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +/** + * Weak reference with a {@code finalizeReferent()} method which a background thread invokes after + * the garbage collector reclaims the referent. This is a simpler alternative to using a {@link + * ReferenceQueue}. + * + * @author Bob Lee + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class FinalizableWeakReference extends WeakReference + implements FinalizableReference { + /** + * Constructs a new finalizable weak reference. + * + * @param referent to weakly reference + * @param queue that should finalize the referent + */ + protected FinalizableWeakReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Function.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Function.java new file mode 100644 index 0000000000000..606a0fde99b80 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Function.java @@ -0,0 +1,67 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Legacy version of {@link java.util.function.Function java.util.function.Function}. + * + *

The {@link Functions} class provides common functions and related utilities. + * + *

As this interface extends {@code java.util.function.Function}, an instance of this type can be + * used as a {@code java.util.function.Function} directly. To use a {@code + * java.util.function.Function} in a context where a {@code + * org.apache.iotdb.google.common.base.Function} is needed, use {@code function::apply}. + * + *

This interface is now a legacy type. Use {@code java.util.function.Function} (or the + * appropriate primitive specialization such as {@code ToIntFunction}) instead whenever possible. + * Otherwise, at least reduce explicit dependencies on this type by using lambda expressions + * or method references instead of classes, leaving your code easier to migrate in the future. + * + *

See the Guava User Guide article on the use of {@code Function}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@FunctionalInterface +@ElementTypesAreNonnullByDefault +public interface Function + extends java.util.function.Function { + @Override + @ParametricNullness + T apply(@ParametricNullness F input); + + /** + * May return {@code true} if {@code object} is a {@code Function} that behaves identically + * to this function. + * + *

Warning: do not depend on the behavior of this method. + * + *

Historically, {@code Function} instances in this library have implemented this method to + * recognize certain cases where distinct {@code Function} instances would in fact behave + * identically. However, as code migrates to {@code java.util.function}, that behavior will + * disappear. It is best not to depend on it. + */ + @Override + boolean equals(Object object); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FunctionalEquivalence.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FunctionalEquivalence.java new file mode 100644 index 0000000000000..7b60c2b74fc3c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/FunctionalEquivalence.java @@ -0,0 +1,80 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Equivalence applied on functional result. + * + * @author Bob Lee + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class FunctionalEquivalence extends Equivalence implements Serializable { + + private static final long serialVersionUID = 0; + + private final Function function; + private final Equivalence resultEquivalence; + + FunctionalEquivalence( + Function function, Equivalence resultEquivalence) { + this.function = checkNotNull(function); + this.resultEquivalence = checkNotNull(resultEquivalence); + } + + @Override + protected boolean doEquivalent(F a, F b) { + return resultEquivalence.equivalent(function.apply(a), function.apply(b)); + } + + @Override + protected int doHash(F a) { + return resultEquivalence.hash(function.apply(a)); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof FunctionalEquivalence) { + FunctionalEquivalence that = (FunctionalEquivalence) obj; + return function.equals(that.function) && resultEquivalence.equals(that.resultEquivalence); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(function, resultEquivalence); + } + + @Override + public String toString() { + return resultEquivalence + ".onResultOf(" + function + ")"; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Functions.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Functions.java new file mode 100644 index 0000000000000..97a8fb0fd923e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Functions.java @@ -0,0 +1,435 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@code org.apache.iotdb.google.common.base.Function} + * instances; see that class for information about migrating to {@code java.util.function}. + * + *

All methods return serializable functions as long as they're given serializable parameters. + * + *

See the Guava User Guide article on the use of {@code Function}. + * + * @author Mike Bostock + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Functions { + private Functions() {} + + /** + * A function equivalent to the method reference {@code Object::toString}, for users not yet using + * Java 8. The function simply invokes {@code toString} on its argument and returns the result. It + * throws a {@link NullPointerException} on null input. + * + *

Warning: The returned function may not be consistent with equals (as + * documented at {@link Function#apply}). For example, this function yields different results for + * the two equal instances {@code ImmutableSet.of(1, 2)} and {@code ImmutableSet.of(2, 1)}. + * + *

Warning: as with all function types in this package, avoid depending on the specific + * {@code equals}, {@code hashCode} or {@code toString} behavior of the returned function. A + * future migration to {@code java.util.function} will not preserve this behavior. + * + *

For Java 8 users: use the method reference {@code Object::toString} instead. In the + * future, when this class requires Java 8, this method will be deprecated. See {@link Function} + * for more important information about the Java 8 transition. + */ + public static Function toStringFunction() { + return ToStringFunction.INSTANCE; + } + + // enum singleton pattern + private enum ToStringFunction implements Function { + INSTANCE; + + @Override + public String apply(Object o) { + checkNotNull(o); // eager for GWT. + return o.toString(); + } + + @Override + public String toString() { + return "Functions.toStringFunction()"; + } + } + + /** + * Returns the identity function. + * + *

Discouraged: Prefer using a lambda like {@code v -> v}, which is shorter and often + * more readable. + */ + // implementation is "fully variant"; E has become a "pass-through" type + @SuppressWarnings("unchecked") + public static Function identity() { + return (Function) IdentityFunction.INSTANCE; + } + + // enum singleton pattern + private enum IdentityFunction implements Function { + INSTANCE; + + @Override + public Object apply(Object o) { + return o; + } + + @Override + public String toString() { + return "Functions.identity()"; + } + } + + /** + * Returns a function which performs a map lookup. The returned function throws an {@link + * IllegalArgumentException} if given a key that does not exist in the map. See also {@link + * #forMap(Map, Object)}, which returns a default value in this case. + * + *

Note: if {@code map} is a {@link org.apache.iotdb.google.common.collect.BiMap BiMap} (or can + * be one), you can use {@link org.apache.iotdb.google.common.collect.Maps#asConverter + * Maps.asConverter} instead to get a function that also supports reverse conversion. + * + *

Java 8 users: if you are okay with {@code null} being returned for an unrecognized + * key (instead of an exception being thrown), you can use the method reference {@code map::get} + * instead. + */ + public static Function forMap(Map map) { + return new FunctionForMapNoDefault<>(map); + } + + /** + * Returns a function which performs a map lookup with a default value. The function created by + * this method returns {@code defaultValue} for all inputs that do not belong to the map's key + * set. See also {@link #forMap(Map)}, which throws an exception in this case. + * + *

Java 8 users: you can just write the lambda expression {@code k -> + * map.getOrDefault(k, defaultValue)} instead. + * + * @param map source map that determines the function behavior + * @param defaultValue the value to return for inputs that aren't map keys + * @return function that returns {@code map.get(a)} when {@code a} is a key, or {@code + * defaultValue} otherwise + */ + public static Function forMap( + Map map, @ParametricNullness V defaultValue) { + return new ForMapWithDefault<>(map, defaultValue); + } + + private static class FunctionForMapNoDefault + implements Function, Serializable { + final Map map; + + FunctionForMapNoDefault(Map map) { + this.map = checkNotNull(map); + } + + @Override + @ParametricNullness + public V apply(@ParametricNullness K key) { + V result = map.get(key); + checkArgument(result != null || map.containsKey(key), "Key '%s' not present in map", key); + // The unchecked cast is safe because of the containsKey check. + return uncheckedCastNullableTToT(result); + } + + @Override + public boolean equals(Object o) { + if (o instanceof FunctionForMapNoDefault) { + FunctionForMapNoDefault that = (FunctionForMapNoDefault) o; + return map.equals(that.map); + } + return false; + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public String toString() { + return "Functions.forMap(" + map + ")"; + } + + private static final long serialVersionUID = 0; + } + + private static class ForMapWithDefault + implements Function, Serializable { + final Map map; + @ParametricNullness final V defaultValue; + + ForMapWithDefault(Map map, @ParametricNullness V defaultValue) { + this.map = checkNotNull(map); + this.defaultValue = defaultValue; + } + + @Override + @ParametricNullness + public V apply(@ParametricNullness K key) { + V result = map.get(key); + // The unchecked cast is safe because of the containsKey check. + return (result != null || map.containsKey(key)) + ? uncheckedCastNullableTToT(result) + : defaultValue; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ForMapWithDefault) { + ForMapWithDefault that = (ForMapWithDefault) o; + return map.equals(that.map) && Objects.equal(defaultValue, that.defaultValue); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(map, defaultValue); + } + + @Override + public String toString() { + // TODO(cpovirk): maybe remove "defaultValue=" to make this look like the method call does + return "Functions.forMap(" + map + ", defaultValue=" + defaultValue + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the composition of two functions. For {@code f: A->B} and {@code g: B->C}, composition + * is defined as the function h such that {@code h(a) == g(f(a))} for each {@code a}. + * + *

Java 8 users: use {@code g.compose(f)} or (probably clearer) {@code f.andThen(g)} + * instead. + * + * @param g the second function to apply + * @param f the first function to apply + * @return the composition of {@code f} and {@code g} + * @see function composition + */ + public static Function compose( + Function g, Function f) { + return new FunctionComposition<>(g, f); + } + + private static class FunctionComposition + implements Function, Serializable { + private final Function g; + private final Function f; + + public FunctionComposition(Function g, Function f) { + this.g = checkNotNull(g); + this.f = checkNotNull(f); + } + + @Override + @ParametricNullness + public C apply(@ParametricNullness A a) { + return g.apply(f.apply(a)); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof FunctionComposition) { + FunctionComposition that = (FunctionComposition) obj; + return f.equals(that.f) && g.equals(that.g); + } + return false; + } + + @Override + public int hashCode() { + return f.hashCode() ^ g.hashCode(); + } + + @Override + public String toString() { + // TODO(cpovirk): maybe make this look like the method call does ("Functions.compose(...)") + return g + "(" + f + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates a function that returns the same boolean output as the given predicate for all inputs. + * + *

The returned function is consistent with equals (as documented at {@link + * Function#apply}) if and only if {@code predicate} is itself consistent with equals. + * + *

Java 8 users: use the method reference {@code predicate::test} instead. + */ + public static Function forPredicate(Predicate predicate) { + return new PredicateFunction<>(predicate); + } + + /** + * @see Functions#forPredicate + */ + private static class PredicateFunction + implements Function, Serializable { + private final Predicate predicate; + + private PredicateFunction(Predicate predicate) { + this.predicate = checkNotNull(predicate); + } + + @Override + public Boolean apply(@ParametricNullness T t) { + return predicate.apply(t); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PredicateFunction) { + PredicateFunction that = (PredicateFunction) obj; + return predicate.equals(that.predicate); + } + return false; + } + + @Override + public int hashCode() { + return predicate.hashCode(); + } + + @Override + public String toString() { + return "Functions.forPredicate(" + predicate + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a function that ignores its input and always returns {@code value}. + * + *

Java 8 users: use the lambda expression {@code o -> value} instead. + * + * @param value the constant value for the function to return + * @return a function that always returns {@code value} + */ + public static Function constant(@ParametricNullness E value) { + return new ConstantFunction<>(value); + } + + private static class ConstantFunction + implements Function, Serializable { + @ParametricNullness private final E value; + + public ConstantFunction(@ParametricNullness E value) { + this.value = value; + } + + @Override + @ParametricNullness + public E apply(Object from) { + return value; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ConstantFunction) { + ConstantFunction that = (ConstantFunction) obj; + return Objects.equal(value, that.value); + } + return false; + } + + @Override + public int hashCode() { + return (value == null) ? 0 : value.hashCode(); + } + + @Override + public String toString() { + return "Functions.constant(" + value + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a function that ignores its input and returns the result of {@code supplier.get()}. + * + *

Java 8 users: use the lambda expression {@code o -> supplier.get()} instead. + * + * @since 10.0 + */ + public static Function forSupplier( + Supplier supplier) { + return new SupplierFunction<>(supplier); + } + + /** + * @see Functions#forSupplier + */ + private static class SupplierFunction + implements Function, Serializable { + + private final Supplier supplier; + + private SupplierFunction(Supplier supplier) { + this.supplier = checkNotNull(supplier); + } + + @Override + @ParametricNullness + public T apply(@ParametricNullness F input) { + return supplier.get(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof SupplierFunction) { + SupplierFunction that = (SupplierFunction) obj; + return this.supplier.equals(that.supplier); + } + return false; + } + + @Override + public int hashCode() { + return supplier.hashCode(); + } + + @Override + public String toString() { + return "Functions.forSupplier(" + supplier + ")"; + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Java8Compatibility.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Java8Compatibility.java new file mode 100644 index 0000000000000..05685cfa2b7e7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Java8Compatibility.java @@ -0,0 +1,52 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.nio.Buffer; + +/** + * Wrappers around {@link Buffer} methods that are covariantly overridden in Java 9+. See + * https://github.com/google/guava/issues/3990 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class Java8Compatibility { + static void clear(Buffer b) { + b.clear(); + } + + static void flip(Buffer b) { + b.flip(); + } + + static void limit(Buffer b, int limit) { + b.limit(limit); + } + + static void position(Buffer b, int position) { + b.position(position); + } + + private Java8Compatibility() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/JdkPattern.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/JdkPattern.java new file mode 100644 index 0000000000000..49a53ff1cc8c4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/JdkPattern.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** A regex pattern implementation which is backed by the {@link Pattern}. */ +@ElementTypesAreNonnullByDefault +@J2ktIncompatible +@GwtIncompatible +final class JdkPattern extends CommonPattern implements Serializable { + private final Pattern pattern; + + JdkPattern(Pattern pattern) { + this.pattern = Preconditions.checkNotNull(pattern); + } + + @Override + public CommonMatcher matcher(CharSequence t) { + return new JdkMatcher(pattern.matcher(t)); + } + + @Override + public String pattern() { + return pattern.pattern(); + } + + @Override + public int flags() { + return pattern.flags(); + } + + @Override + public String toString() { + return pattern.toString(); + } + + private static final class JdkMatcher extends CommonMatcher { + final Matcher matcher; + + JdkMatcher(Matcher matcher) { + this.matcher = Preconditions.checkNotNull(matcher); + } + + @Override + public boolean matches() { + return matcher.matches(); + } + + @Override + public boolean find() { + return matcher.find(); + } + + @Override + public boolean find(int index) { + return matcher.find(index); + } + + @Override + public String replaceAll(String replacement) { + return matcher.replaceAll(replacement); + } + + @Override + public int end() { + return matcher.end(); + } + + @Override + public int start() { + return matcher.start(); + } + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Joiner.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Joiner.java new file mode 100644 index 0000000000000..042389bd94a05 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Joiner.java @@ -0,0 +1,487 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.IOException; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a + * {@link Map}) with a separator. It either appends the results to an {@link Appendable} or returns + * them as a {@link String}. Example: + * + *

{@code
+ * Joiner joiner = Joiner.on("; ").skipNulls();
+ *  . . .
+ * return joiner.join("Harry", null, "Ron", "Hermione");
+ * }
+ * + *

This returns the string {@code "Harry; Ron; Hermione"}. Note that all input elements are + * converted to strings using {@link Object#toString()} before being appended. + * + *

If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified, the joining + * methods will throw {@link NullPointerException} if any given element is null. + * + *

Warning: joiner instances are always immutable; a configuration method such as {@code + * useForNull} has no effect on the instance it is invoked on! You must store and use the new joiner + * instance returned by the method. This makes joiners thread-safe, and safe to store as {@code + * static final} constants. + * + *

{@code
+ * // Bad! Do not do this!
+ * Joiner joiner = Joiner.on(',');
+ * joiner.skipNulls(); // does nothing!
+ * return joiner.join("wrong", null, "wrong");
+ * }
+ * + *

See the Guava User Guide article on {@code Joiner}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public class Joiner { + /** Returns a joiner which automatically places {@code separator} between consecutive elements. */ + public static Joiner on(String separator) { + return new Joiner(separator); + } + + /** Returns a joiner which automatically places {@code separator} between consecutive elements. */ + public static Joiner on(char separator) { + return new Joiner(String.valueOf(separator)); + } + + private final String separator; + + private Joiner(String separator) { + this.separator = checkNotNull(separator); + } + + private Joiner(Joiner prototype) { + this.separator = prototype.separator; + } + + /* + * In this file, we use instead of to work around a Kotlin bug + * (see b/189937072 until we file a bug against Kotlin itself). (The two should be equivalent, so + * we normally prefer the shorter one.) + */ + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code appendable}. + */ + public A appendTo(A appendable, Iterable parts) + throws IOException { + return appendTo(appendable, parts.iterator()); + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code appendable}. + * + * @since 11.0 + */ + public A appendTo(A appendable, Iterator parts) + throws IOException { + checkNotNull(appendable); + if (parts.hasNext()) { + appendable.append(toString(parts.next())); + while (parts.hasNext()) { + appendable.append(separator); + appendable.append(toString(parts.next())); + } + } + return appendable; + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code appendable}. + */ + public final A appendTo(A appendable, Object[] parts) throws IOException { + return appendTo(appendable, Arrays.asList(parts)); + } + + /** Appends to {@code appendable} the string representation of each of the remaining arguments. */ + public final A appendTo( + A appendable, Object first, Object second, Object... rest) throws IOException { + return appendTo(appendable, iterable(first, second, rest)); + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable, + * Iterable)}, except that it does not throw {@link IOException}. + */ + public final StringBuilder appendTo(StringBuilder builder, Iterable parts) { + return appendTo(builder, parts.iterator()); + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable, + * Iterable)}, except that it does not throw {@link IOException}. + * + * @since 11.0 + */ + public final StringBuilder appendTo(StringBuilder builder, Iterator parts) { + try { + appendTo((Appendable) builder, parts); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return builder; + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable, + * Iterable)}, except that it does not throw {@link IOException}. + */ + public final StringBuilder appendTo(StringBuilder builder, Object[] parts) { + return appendTo(builder, Arrays.asList(parts)); + } + + /** + * Appends to {@code builder} the string representation of each of the remaining arguments. + * Identical to {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does not + * throw {@link IOException}. + */ + public final StringBuilder appendTo( + StringBuilder builder, Object first, Object second, Object... rest) { + return appendTo(builder, iterable(first, second, rest)); + } + + /** + * Returns a string containing the string representation of each of {@code parts}, using the + * previously configured separator between each. + */ + public final String join(Iterable parts) { + return join(parts.iterator()); + } + + /** + * Returns a string containing the string representation of each of {@code parts}, using the + * previously configured separator between each. + * + * @since 11.0 + */ + public final String join(Iterator parts) { + return appendTo(new StringBuilder(), parts).toString(); + } + + /** + * Returns a string containing the string representation of each of {@code parts}, using the + * previously configured separator between each. + */ + public final String join(Object[] parts) { + return join(Arrays.asList(parts)); + } + + /** + * Returns a string containing the string representation of each argument, using the previously + * configured separator between each. + */ + public final String join(Object first, Object second, Object... rest) { + return join(iterable(first, second, rest)); + } + + /** + * Returns a joiner with the same behavior as this one, except automatically substituting {@code + * nullText} for any provided null elements. + */ + public Joiner useForNull(String nullText) { + checkNotNull(nullText); + return new Joiner(this) { + @Override + CharSequence toString(Object part) { + return (part == null) ? nullText : Joiner.this.toString(part); + } + + @Override + public Joiner useForNull(String nullText) { + throw new UnsupportedOperationException("already specified useForNull"); + } + + @Override + public Joiner skipNulls() { + throw new UnsupportedOperationException("already specified useForNull"); + } + }; + } + + /** + * Returns a joiner with the same behavior as this joiner, except automatically skipping over any + * provided null elements. + */ + public Joiner skipNulls() { + return new Joiner(this) { + @Override + public A appendTo(A appendable, Iterator parts) + throws IOException { + checkNotNull(appendable, "appendable"); + checkNotNull(parts, "parts"); + while (parts.hasNext()) { + Object part = parts.next(); + if (part != null) { + appendable.append(Joiner.this.toString(part)); + break; + } + } + while (parts.hasNext()) { + Object part = parts.next(); + if (part != null) { + appendable.append(separator); + appendable.append(Joiner.this.toString(part)); + } + } + return appendable; + } + + @Override + public Joiner useForNull(String nullText) { + throw new UnsupportedOperationException("already specified skipNulls"); + } + + @Override + public MapJoiner withKeyValueSeparator(String kvs) { + throw new UnsupportedOperationException("can't use .skipNulls() with maps"); + } + }; + } + + /** + * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as + * this {@code Joiner} otherwise. + * + * @since 20.0 + */ + public MapJoiner withKeyValueSeparator(char keyValueSeparator) { + return withKeyValueSeparator(String.valueOf(keyValueSeparator)); + } + + /** + * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as + * this {@code Joiner} otherwise. + */ + public MapJoiner withKeyValueSeparator(String keyValueSeparator) { + return new MapJoiner(this, keyValueSeparator); + } + + /** + * An object that joins map entries in the same manner as {@code Joiner} joins iterables and + * arrays. Like {@code Joiner}, it is thread-safe and immutable. + * + *

In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code + * Multimap} entries in two distinct modes: + * + *

+ * + * @since 2.0 + */ + public static final class MapJoiner { + private final Joiner joiner; + private final String keyValueSeparator; + + private MapJoiner(Joiner joiner, String keyValueSeparator) { + this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull + this.keyValueSeparator = checkNotNull(keyValueSeparator); + } + + /** + * Appends the string representation of each entry of {@code map}, using the previously + * configured separator and key-value separator, to {@code appendable}. + */ + public A appendTo(A appendable, Map map) throws IOException { + return appendTo(appendable, map.entrySet()); + } + + /** + * Appends the string representation of each entry of {@code map}, using the previously + * configured separator and key-value separator, to {@code builder}. Identical to {@link + * #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}. + */ + public StringBuilder appendTo(StringBuilder builder, Map map) { + return appendTo(builder, map.entrySet()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code appendable}. + * + * @since 10.0 + */ + public A appendTo(A appendable, Iterable> entries) + throws IOException { + return appendTo(appendable, entries.iterator()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code appendable}. + * + * @since 11.0 + */ + public A appendTo(A appendable, Iterator> parts) + throws IOException { + checkNotNull(appendable); + if (parts.hasNext()) { + Entry entry = parts.next(); + appendable.append(joiner.toString(entry.getKey())); + appendable.append(keyValueSeparator); + appendable.append(joiner.toString(entry.getValue())); + while (parts.hasNext()) { + appendable.append(joiner.separator); + Entry e = parts.next(); + appendable.append(joiner.toString(e.getKey())); + appendable.append(keyValueSeparator); + appendable.append(joiner.toString(e.getValue())); + } + } + return appendable; + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code builder}. Identical to {@link + * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}. + * + * @since 10.0 + */ + public StringBuilder appendTo(StringBuilder builder, Iterable> entries) { + return appendTo(builder, entries.iterator()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code builder}. Identical to {@link + * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}. + * + * @since 11.0 + */ + public StringBuilder appendTo(StringBuilder builder, Iterator> entries) { + try { + appendTo((Appendable) builder, entries); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return builder; + } + + /** + * Returns a string containing the string representation of each entry of {@code map}, using the + * previously configured separator and key-value separator. + */ + public String join(Map map) { + return join(map.entrySet()); + } + + /** + * Returns a string containing the string representation of each entry in {@code entries}, using + * the previously configured separator and key-value separator. + * + * @since 10.0 + */ + public String join(Iterable> entries) { + return join(entries.iterator()); + } + + /** + * Returns a string containing the string representation of each entry in {@code entries}, using + * the previously configured separator and key-value separator. + * + * @since 11.0 + */ + public String join(Iterator> entries) { + return appendTo(new StringBuilder(), entries).toString(); + } + + /** + * Returns a map joiner with the same behavior as this one, except automatically substituting + * {@code nullText} for any provided null keys or values. + */ + public MapJoiner useForNull(String nullText) { + return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator); + } + } + + CharSequence toString(Object part) { + /* + * requireNonNull is not safe: Joiner.on(...).join(somethingThatContainsNull) will indeed throw. + * However, Joiner.on(...).useForNull(...).join(somethingThatContainsNull) *is* safe -- because + * it returns a subclass of Joiner that overrides this method to tolerate null inputs. + * + * Unfortunately, we don't distinguish between these two cases in our public API: Joiner.on(...) + * and Joiner.on(...).useForNull(...) both declare the same return type: plain Joiner. To ensure + * that users *can* pass null arguments to Joiner, we annotate it as if it always tolerates null + * inputs, rather than as if it never tolerates them. + * + * We rely on checkers to implement special cases to catch dangerous calls to join(), etc. based + * on what they know about the particular Joiner instances the calls are performed on. + * + * (In addition to useForNull, we also offer skipNulls. It, too, tolerates null inputs, but its + * tolerance is implemented differently: Its implementation avoids calling this toString(Object) + * method in the first place.) + */ + requireNonNull(part); + return (part instanceof CharSequence) ? (CharSequence) part : part.toString(); + } + + private static Iterable iterable(Object first, Object second, Object[] rest) { + checkNotNull(rest); + return new AbstractList() { + @Override + public int size() { + return rest.length + 2; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + default: + return rest[index - 2]; + } + } + }; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/MoreObjects.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/MoreObjects.java new file mode 100644 index 0000000000000..91fd8129bd8f7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/MoreObjects.java @@ -0,0 +1,442 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Helper functions that operate on any {@code Object}, and are not already provided in {@link + * java.util.Objects}. + * + *

See the Guava User Guide on writing {@code Object} + * methods with {@code MoreObjects}. + * + * @author Laurence Gonsalves + * @since 18.0 (since 2.0 as {@code Objects}) + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class MoreObjects { + /** + * Returns the first of two given parameters that is not {@code null}, if either is, or otherwise + * throws a {@link NullPointerException}. + * + *

To find the first non-null element in an iterable, use {@code Iterables.find(iterable, + * Predicates.notNull())}. For varargs, use {@code Iterables.find(Arrays.asList(a, b, c, ...), + * Predicates.notNull())}, static importing as necessary. + * + *

Note: if {@code first} is represented as an {@link Optional}, this can be + * accomplished with {@link Optional#or(Object) first.or(second)}. That approach also allows for + * lazy evaluation of the fallback instance, using {@link Optional#or(Supplier) + * first.or(supplier)}. + * + *

Java 9 users: use {@code java.util.Objects.requireNonNullElse(first, second)} + * instead. + * + * @return {@code first} if it is non-null; otherwise {@code second} if it is non-null + * @throws NullPointerException if both {@code first} and {@code second} are null + * @since 18.0 (since 3.0 as {@code Objects.firstNonNull()}). + */ + public static T firstNonNull(T first, T second) { + if (first != null) { + return first; + } + if (second != null) { + return second; + } + throw new NullPointerException("Both parameters are null"); + } + + /** + * Creates an instance of {@link ToStringHelper}. + * + *

This is helpful for implementing {@link Object#toString()}. Specification by example: + * + *

{@code
+   * // Returns "ClassName{}"
+   * MoreObjects.toStringHelper(this)
+   *     .toString();
+   *
+   * // Returns "ClassName{x=1}"
+   * MoreObjects.toStringHelper(this)
+   *     .add("x", 1)
+   *     .toString();
+   *
+   * // Returns "MyObject{x=1}"
+   * MoreObjects.toStringHelper("MyObject")
+   *     .add("x", 1)
+   *     .toString();
+   *
+   * // Returns "ClassName{x=1, y=foo}"
+   * MoreObjects.toStringHelper(this)
+   *     .add("x", 1)
+   *     .add("y", "foo")
+   *     .toString();
+   *
+   * // Returns "ClassName{x=1}"
+   * MoreObjects.toStringHelper(this)
+   *     .omitNullValues()
+   *     .add("x", 1)
+   *     .add("y", null)
+   *     .toString();
+   * }
+ * + *

Note that in GWT, class names are often obfuscated. + * + * @param self the object to generate the string for (typically {@code this}), used only for its + * class name + * @since 18.0 (since 2.0 as {@code Objects.toStringHelper()}). + */ + public static ToStringHelper toStringHelper(Object self) { + return new ToStringHelper(self.getClass().getSimpleName()); + } + + /** + * Creates an instance of {@link ToStringHelper} in the same manner as {@link + * #toStringHelper(Object)}, but using the simple name of {@code clazz} instead of using an + * instance's {@link Object#getClass()}. + * + *

Note that in GWT, class names are often obfuscated. + * + * @param clazz the {@link Class} of the instance + * @since 18.0 (since 7.0 as {@code Objects.toStringHelper()}). + */ + public static ToStringHelper toStringHelper(Class clazz) { + return new ToStringHelper(clazz.getSimpleName()); + } + + /** + * Creates an instance of {@link ToStringHelper} in the same manner as {@link + * #toStringHelper(Object)}, but using {@code className} instead of using an instance's {@link + * Object#getClass()}. + * + * @param className the name of the instance type + * @since 18.0 (since 7.0 as {@code Objects.toStringHelper()}). + */ + public static ToStringHelper toStringHelper(String className) { + return new ToStringHelper(className); + } + + /** + * Support class for {@link MoreObjects#toStringHelper}. + * + * @author Jason Lee + * @since 18.0 (since 2.0 as {@code Objects.ToStringHelper}). + */ + public static final class ToStringHelper { + private final String className; + private final ValueHolder holderHead = new ValueHolder(); + private ValueHolder holderTail = holderHead; + private boolean omitNullValues = false; + private boolean omitEmptyValues = false; + + /** Use {@link MoreObjects#toStringHelper(Object)} to create an instance. */ + private ToStringHelper(String className) { + this.className = checkNotNull(className); + } + + /** + * Configures the {@link ToStringHelper} so {@link #toString()} will ignore properties with null + * value. The order of calling this method, relative to the {@code add()}/{@code addValue()} + * methods, is not significant. + * + * @since 18.0 (since 12.0 as {@code Objects.ToStringHelper.omitNullValues()}). + */ + public ToStringHelper omitNullValues() { + omitNullValues = true; + return this; + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. If {@code value} + * is {@code null}, the string {@code "null"} is used, unless {@link #omitNullValues()} is + * called, in which case this name/value pair will not be added. + */ + public ToStringHelper add(String name, Object value) { + return addHolder(name, value); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}). + */ + public ToStringHelper add(String name, boolean value) { + return addUnconditionalHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}). + */ + public ToStringHelper add(String name, char value) { + return addUnconditionalHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}). + */ + public ToStringHelper add(String name, double value) { + return addUnconditionalHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}). + */ + public ToStringHelper add(String name, float value) { + return addUnconditionalHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}). + */ + public ToStringHelper add(String name, int value) { + return addUnconditionalHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.add()}). + */ + public ToStringHelper add(String name, long value) { + return addUnconditionalHolder(name, String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, Object)} instead and give value a + * readable name. + */ + public ToStringHelper addValue(Object value) { + return addHolder(value); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, boolean)} instead and give value a + * readable name. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}). + */ + public ToStringHelper addValue(boolean value) { + return addUnconditionalHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, char)} instead and give value a + * readable name. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}). + */ + public ToStringHelper addValue(char value) { + return addUnconditionalHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, double)} instead and give value a + * readable name. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}). + */ + public ToStringHelper addValue(double value) { + return addUnconditionalHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, float)} instead and give value a + * readable name. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}). + */ + public ToStringHelper addValue(float value) { + return addUnconditionalHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, int)} instead and give value a + * readable name. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}). + */ + public ToStringHelper addValue(int value) { + return addUnconditionalHolder(String.valueOf(value)); + } + + /** + * Adds an unnamed value to the formatted output. + * + *

It is strongly encouraged to use {@link #add(String, long)} instead and give value a + * readable name. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.addValue()}). + */ + public ToStringHelper addValue(long value) { + return addUnconditionalHolder(String.valueOf(value)); + } + + private static boolean isEmpty(Object value) { + // Put types estimated to be the most frequent first. + if (value instanceof CharSequence) { + return ((CharSequence) value).length() == 0; + } else if (value instanceof Collection) { + return ((Collection) value).isEmpty(); + } else if (value instanceof Map) { + return ((Map) value).isEmpty(); + } else if (value instanceof java.util.Optional) { + return !((java.util.Optional) value).isPresent(); + } else if (value instanceof OptionalInt) { + return !((OptionalInt) value).isPresent(); + } else if (value instanceof OptionalLong) { + return !((OptionalLong) value).isPresent(); + } else if (value instanceof OptionalDouble) { + return !((OptionalDouble) value).isPresent(); + } else if (value instanceof Optional) { + return !((Optional) value).isPresent(); + } else if (value.getClass().isArray()) { + return Array.getLength(value) == 0; + } + return false; + } + + /** + * Returns a string in the format specified by {@link MoreObjects#toStringHelper(Object)}. + * + *

After calling this method, you can keep adding more properties to later call toString() + * again and get a more complete representation of the same object; but properties cannot be + * removed, so this only allows limited reuse of the helper instance. The helper allows + * duplication of properties (multiple name/value pairs with the same name can be added). + */ + @Override + public String toString() { + // create a copy to keep it consistent in case value changes + boolean omitNullValuesSnapshot = omitNullValues; + boolean omitEmptyValuesSnapshot = omitEmptyValues; + String nextSeparator = ""; + StringBuilder builder = new StringBuilder(32).append(className).append('{'); + for (ValueHolder valueHolder = holderHead.next; + valueHolder != null; + valueHolder = valueHolder.next) { + Object value = valueHolder.value; + if (valueHolder instanceof UnconditionalValueHolder + || (value == null + ? !omitNullValuesSnapshot + : (!omitEmptyValuesSnapshot || !isEmpty(value)))) { + builder.append(nextSeparator); + nextSeparator = ", "; + + if (valueHolder.name != null) { + builder.append(valueHolder.name).append('='); + } + if (value != null && value.getClass().isArray()) { + Object[] objectArray = {value}; + String arrayString = Arrays.deepToString(objectArray); + builder.append(arrayString, 1, arrayString.length() - 1); + } else { + builder.append(value); + } + } + } + return builder.append('}').toString(); + } + + private ValueHolder addHolder() { + ValueHolder valueHolder = new ValueHolder(); + holderTail = holderTail.next = valueHolder; + return valueHolder; + } + + private ToStringHelper addHolder(Object value) { + ValueHolder valueHolder = addHolder(); + valueHolder.value = value; + return this; + } + + private ToStringHelper addHolder(String name, Object value) { + ValueHolder valueHolder = addHolder(); + valueHolder.value = value; + valueHolder.name = checkNotNull(name); + return this; + } + + private UnconditionalValueHolder addUnconditionalHolder() { + UnconditionalValueHolder valueHolder = new UnconditionalValueHolder(); + holderTail = holderTail.next = valueHolder; + return valueHolder; + } + + private ToStringHelper addUnconditionalHolder(Object value) { + UnconditionalValueHolder valueHolder = addUnconditionalHolder(); + valueHolder.value = value; + return this; + } + + private ToStringHelper addUnconditionalHolder(String name, Object value) { + UnconditionalValueHolder valueHolder = addUnconditionalHolder(); + valueHolder.value = value; + valueHolder.name = checkNotNull(name); + return this; + } + + // Holder object for values that might be null and/or empty. + private static class ValueHolder { + String name; + Object value; + ValueHolder next; + } + + /** + * Holder object for values that cannot be null or empty (will be printed unconditionally). This + * helps to shortcut most calls to isEmpty(), which is important because the check for emptiness + * is relatively expensive. Use a subtype so this also doesn't need any extra storage. + */ + private static final class UnconditionalValueHolder extends ValueHolder {} + } + + private MoreObjects() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/NullnessCasts.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/NullnessCasts.java new file mode 100644 index 0000000000000..0ef7970c3870b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/NullnessCasts.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class NullnessCasts { + /** + * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that + * that conversion is safe. + * + *

This method is intended to help with usages of type parameters that have {@linkplain + * ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null + * types (or if the type is a non-variable type, like {@code String}), then code should almost + * never use this method, preferring instead to call {@code requireNonNull} so as to benefit from + * its runtime check. + * + *

An example use case for this method is in implementing an {@code Iterator} whose {@code + * next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the + * code would be responsible for populating a "real" {@code T} (which might still be the value + * {@code null}!) before returning it to callers. Depending on how the code is structured, a + * nullness analysis might not understand that the field has been populated. To avoid that problem + * without having to add {@code @SuppressWarnings}, the code can call this method. + * + *

Why not just add {@code SuppressWarnings}? The problem is that this method is + * typically useful for {@code return} statements. That leaves the code with two options: Either + * add the suppression to the whole method (which turns off checking for a large section of code), + * or extract a variable, and put the suppression on that. However, a local variable typically + * doesn't work: Because nullness analyses typically infer the nullness of local variables, + * there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the + * analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}. + * (Even if supported added {@code @NonNull}, that would not help, since the problem case + * addressed by this method is the case in which {@code T} has parametric nullness -- and thus its + * value may be legitimately {@code null}.) + */ + @ParametricNullness + @SuppressWarnings("nullness") + static T uncheckedCastNullableTToT(T t) { + return t; + } + + private NullnessCasts() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Objects.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Objects.java new file mode 100644 index 0000000000000..4f562551ffd68 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Objects.java @@ -0,0 +1,85 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Arrays; + +/** + * Helper functions that can operate on any {@code Object}. + * + *

See the Guava User Guide on writing {@code Object} + * methods with {@code Objects}. + * + * @author Laurence Gonsalves + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Objects extends ExtraObjectsMethodsForWeb { + private Objects() {} + + /** + * Determines whether two possibly-null objects are equal. Returns: + * + *

    + *
  • {@code true} if {@code a} and {@code b} are both null. + *
  • {@code true} if {@code a} and {@code b} are both non-null and they are equal according to + * {@link Object#equals(Object)}. + *
  • {@code false} in all other situations. + *
+ * + *

This assumes that any non-null objects passed to this function conform to the {@code + * equals()} contract. + * + *

Note for Java 7 and later: This method should be treated as deprecated; use {@link + * java.util.Objects#equals} instead. + */ + public static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + /** + * Generates a hash code for multiple values. The hash code is generated by calling {@link + * Arrays#hashCode(Object[])}. Note that array arguments to this method, with the exception of a + * single Object array, do not get any special handling; their hash codes are based on identity + * and not contents. + * + *

This is useful for implementing {@link Object#hashCode()}. For example, in an object that + * has three properties, {@code x}, {@code y}, and {@code z}, one could write: + * + *

{@code
+   * public int hashCode() {
+   *   return Objects.hashCode(getX(), getY(), getZ());
+   * }
+   * }
+ * + *

Warning: When a single object is supplied, the returned hash code does not equal the + * hash code of that object. + * + *

Note for Java 7 and later: This method should be treated as deprecated; use {@link + * java.util.Objects#hash} instead. + */ + public static int hashCode(Object... objects) { + return Arrays.hashCode(objects); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Optional.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Optional.java new file mode 100644 index 0000000000000..13dbe949d4ccf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Optional.java @@ -0,0 +1,361 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An immutable object that may contain a non-null reference to another object. Each instance of + * this type either contains a non-null reference, or contains nothing (in which case we say that + * the reference is "absent"); it is never said to "contain {@code null}". + * + *

A non-null {@code Optional} reference can be used as a replacement for a nullable {@code T} + * reference. It allows you to represent "a {@code T} that must be present" and a "a {@code T} that + * might be absent" as two distinct types in your program, which can aid clarity. + * + *

Some uses of this class include + * + *

    + *
  • As a method return type, as an alternative to returning {@code null} to indicate that no + * value was available + *
  • To distinguish between "unknown" (for example, not present in a map) and "known to have no + * value" (present in the map, with value {@code Optional.absent()}) + *
  • To wrap nullable references for storage in a collection that does not support {@code null} + * (though there are several other + * approaches to this that should be considered first) + *
+ * + *

A common alternative to using this class is to find or create a suitable null object for the type in question. + * + *

This class is not intended as a direct analogue of any existing "option" or "maybe" construct + * from other programming environments, though it may bear some similarities. + * + *

An instance of this class is serializable if its reference is absent or is a serializable + * object. + * + *

Comparison to {@code java.util.Optional} (JDK 8 and higher): A new {@code Optional} + * class was added for Java 8. The two classes are extremely similar, but incompatible (they cannot + * share a common supertype). All known differences are listed either here or with the + * relevant methods below. + * + *

    + *
  • This class is serializable; {@code java.util.Optional} is not. + *
  • {@code java.util.Optional} has the additional methods {@code ifPresent}, {@code filter}, + * {@code flatMap}, and {@code orElseThrow}. + *
  • {@code java.util} offers the primitive-specialized versions {@code OptionalInt}, {@code + * OptionalLong} and {@code OptionalDouble}, the use of which is recommended; Guava does not + * have these. + *
+ * + *

There are no plans to deprecate this class in the foreseeable future. However, we do + * gently recommend that you prefer the new, standard Java class whenever possible. + * + *

See the Guava User Guide article on using {@code + * Optional}. + * + * @param the type of instance that can be contained. {@code Optional} is naturally covariant on + * this type, so it is safe to cast an {@code Optional} to {@code Optional} for any + * supertype {@code S} of {@code T}. + * @author Kurt Alfred Kluever + * @author Kevin Bourrillion + * @since 10.0 + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +public abstract class Optional implements Serializable { + /** + * Returns an {@code Optional} instance with no contained reference. + * + *

Comparison to {@code java.util.Optional}: this method is equivalent to Java 8's + * {@code Optional.empty}. + */ + public static Optional absent() { + return Absent.withType(); + } + + /** + * Returns an {@code Optional} instance containing the given non-null reference. To have {@code + * null} treated as {@link #absent}, use {@link #fromNullable} instead. + * + *

Comparison to {@code java.util.Optional}: no differences. + * + * @throws NullPointerException if {@code reference} is null + */ + public static Optional of(T reference) { + return new Present(checkNotNull(reference)); + } + + /** + * If {@code nullableReference} is non-null, returns an {@code Optional} instance containing that + * reference; otherwise returns {@link Optional#absent}. + * + *

Comparison to {@code java.util.Optional}: this method is equivalent to Java 8's + * {@code Optional.ofNullable}. + */ + public static Optional fromNullable(T nullableReference) { + return (nullableReference == null) ? Optional.absent() : new Present(nullableReference); + } + + /** + * Returns the equivalent {@code org.apache.iotdb.google.common.base.Optional} value to the given + * {@code java.util.Optional}, or {@code null} if the argument is null. + * + * @since 21.0 + */ + public static Optional fromJavaUtil(java.util.Optional javaUtilOptional) { + return (javaUtilOptional == null) ? null : fromNullable(javaUtilOptional.orElse(null)); + } + + /** + * Returns the equivalent {@code java.util.Optional} value to the given {@code + * org.apache.iotdb.google.common.base.Optional}, or {@code null} if the argument is null. + * + *

If {@code googleOptional} is known to be non-null, use {@code googleOptional.toJavaUtil()} + * instead. + * + *

Unfortunately, the method reference {@code Optional::toJavaUtil} will not work, because it + * could refer to either the static or instance version of this method. Write out the lambda + * expression {@code o -> Optional.toJavaUtil(o)} instead. + * + * @since 21.0 + */ + public static java.util.Optional toJavaUtil(Optional googleOptional) { + return googleOptional == null ? null : googleOptional.toJavaUtil(); + } + + /** + * Returns the equivalent {@code java.util.Optional} value to this optional. + * + *

Unfortunately, the method reference {@code Optional::toJavaUtil} will not work, because it + * could refer to either the static or instance version of this method. Write out the lambda + * expression {@code o -> o.toJavaUtil()} instead. + * + * @since 21.0 + */ + public java.util.Optional toJavaUtil() { + return java.util.Optional.ofNullable(orNull()); + } + + Optional() {} + + /** + * Returns {@code true} if this holder contains a (non-null) instance. + * + *

Comparison to {@code java.util.Optional}: no differences. + */ + public abstract boolean isPresent(); + + /** + * Returns the contained instance, which must be present. If the instance might be absent, use + * {@link #or(Object)} or {@link #orNull} instead. + * + *

Comparison to {@code java.util.Optional}: when the value is absent, this method + * throws {@link IllegalStateException}, whereas the Java 8 counterpart throws {@link + * java.util.NoSuchElementException NoSuchElementException}. + * + * @throws IllegalStateException if the instance is absent ({@link #isPresent} returns {@code + * false}); depending on this specific exception type (over the more general {@link + * RuntimeException}) is discouraged + */ + public abstract T get(); + + /** + * Returns the contained instance if it is present; {@code defaultValue} otherwise. If no default + * value should be required because the instance is known to be present, use {@link #get()} + * instead. For a default value of {@code null}, use {@link #orNull}. + * + *

Note about generics: The signature {@code public T or(T defaultValue)} is overly + * restrictive. However, the ideal signature, {@code public S or(S)}, is not legal + * Java. As a result, some sensible operations involving subtypes are compile errors: + * + *

{@code
+   * Optional optionalInt = getSomeOptionalInt();
+   * Number value = optionalInt.or(0.5); // error
+   *
+   * FluentIterable numbers = getSomeNumbers();
+   * Optional first = numbers.first();
+   * Number value = first.or(0.5); // error
+   * }
+ * + *

As a workaround, it is always safe to cast an {@code Optional} to {@code + * Optional}. Casting either of the above example {@code Optional} instances to {@code + * Optional} (where {@code Number} is the desired output type) solves the problem: + * + *

{@code
+   * Optional optionalInt = (Optional) getSomeOptionalInt();
+   * Number value = optionalInt.or(0.5); // fine
+   *
+   * FluentIterable numbers = getSomeNumbers();
+   * Optional first = (Optional) numbers.first();
+   * Number value = first.or(0.5); // fine
+   * }
+ * + *

Comparison to {@code java.util.Optional}: this method is similar to Java 8's {@code + * Optional.orElse}, but will not accept {@code null} as a {@code defaultValue} ({@link #orNull} + * must be used instead). As a result, the value returned by this method is guaranteed non-null, + * which is not the case for the {@code java.util} equivalent. + */ + public abstract T or(T defaultValue); + + /** + * Returns this {@code Optional} if it has a value present; {@code secondChoice} otherwise. + * + *

Comparison to {@code java.util.Optional}: this method has no equivalent in Java 8's + * {@code Optional} class; write {@code thisOptional.isPresent() ? thisOptional : secondChoice} + * instead. + */ + public abstract Optional or(Optional secondChoice); + + /** + * Returns the contained instance if it is present; {@code supplier.get()} otherwise. + * + *

Comparison to {@code java.util.Optional}: this method is similar to Java 8's {@code + * Optional.orElseGet}, except when {@code supplier} returns {@code null}. In this case this + * method throws an exception, whereas the Java 8 method returns the {@code null} to the caller. + * + * @throws NullPointerException if this optional's value is absent and the supplier returns {@code + * null} + */ + public abstract T or(Supplier supplier); + + /** + * Returns the contained instance if it is present; {@code null} otherwise. If the instance is + * known to be present, use {@link #get()} instead. + * + *

Comparison to {@code java.util.Optional}: this method is equivalent to Java 8's + * {@code Optional.orElse(null)}. + */ + public abstract T orNull(); + + /** + * Returns an immutable singleton {@link Set} whose only element is the contained instance if it + * is present; an empty immutable {@link Set} otherwise. + * + *

Comparison to {@code java.util.Optional}: this method has no equivalent in Java 8's + * {@code Optional} class. However, this common usage: + * + *

{@code
+   * for (Foo foo : possibleFoo.asSet()) {
+   *   doSomethingWith(foo);
+   * }
+   * }
+ * + * ... can be replaced with: + * + *
{@code
+   * possibleFoo.ifPresent(foo -> doSomethingWith(foo));
+   * }
+ * + *

Java 9 users: some use cases can be written with calls to {@code optional.stream()}. + * + * @since 11.0 + */ + public abstract Set asSet(); + + /** + * If the instance is present, it is transformed with the given {@link Function}; otherwise, + * {@link Optional#absent} is returned. + * + *

Comparison to {@code java.util.Optional}: this method is similar to Java 8's {@code + * Optional.map}, except when {@code function} returns {@code null}. In this case this method + * throws an exception, whereas the Java 8 method returns {@code Optional.absent()}. + * + * @throws NullPointerException if the function returns {@code null} + * @since 12.0 + */ + public abstract Optional transform(Function function); + + /** + * Returns {@code true} if {@code object} is an {@code Optional} instance, and either the + * contained references are {@linkplain Object#equals equal} to each other or both are absent. + * Note that {@code Optional} instances of differing parameterized types can be equal. + * + *

Comparison to {@code java.util.Optional}: no differences. + */ + @Override + public abstract boolean equals(Object object); + + /** + * Returns a hash code for this instance. + * + *

Comparison to {@code java.util.Optional}: this class leaves the specific choice of + * hash code unspecified, unlike the Java 8 equivalent. + */ + @Override + public abstract int hashCode(); + + /** + * Returns a string representation for this instance. + * + *

Comparison to {@code java.util.Optional}: this class leaves the specific string + * representation unspecified, unlike the Java 8 equivalent. + */ + @Override + public abstract String toString(); + + /** + * Returns the value of each present instance from the supplied {@code optionals}, in order, + * skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are + * evaluated lazily. + * + *

Comparison to {@code java.util.Optional}: this method has no equivalent in Java 8's + * {@code Optional} class; use {@code + * optionals.stream().filter(Optional::isPresent).map(Optional::get)} instead. + * + *

Java 9 users: use {@code optionals.stream().flatMap(Optional::stream)} instead. + * + * @since 11.0 (generics widened in 13.0) + */ + public static Iterable presentInstances( + final Iterable> optionals) { + checkNotNull(optionals); + return new Iterable() { + @Override + public Iterator iterator() { + return new AbstractIterator() { + private final Iterator> iterator = + checkNotNull(optionals.iterator()); + + @Override + protected T computeNext() { + while (iterator.hasNext()) { + Optional optional = iterator.next(); + if (optional.isPresent()) { + return optional.get(); + } + } + return endOfData(); + } + }; + } + }; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/PairwiseEquivalence.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/PairwiseEquivalence.java new file mode 100644 index 0000000000000..baf5b8ed1e298 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/PairwiseEquivalence.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Iterator; + +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class PairwiseEquivalence extends Equivalence> + implements Serializable { + final Equivalence elementEquivalence; + + PairwiseEquivalence(Equivalence elementEquivalence) { + this.elementEquivalence = Preconditions.checkNotNull(elementEquivalence); + } + + @Override + protected boolean doEquivalent(Iterable iterableA, Iterable iterableB) { + Iterator iteratorA = iterableA.iterator(); + Iterator iteratorB = iterableB.iterator(); + + while (iteratorA.hasNext() && iteratorB.hasNext()) { + if (!elementEquivalence.equivalent(iteratorA.next(), iteratorB.next())) { + return false; + } + } + + return !iteratorA.hasNext() && !iteratorB.hasNext(); + } + + @Override + protected int doHash(Iterable iterable) { + int hash = 78721; + for (T element : iterable) { + hash = hash * 24943 + elementEquivalence.hash(element); + } + return hash; + } + + @Override + public boolean equals(Object object) { + if (object instanceof PairwiseEquivalence) { + @SuppressWarnings("unchecked") + PairwiseEquivalence that = (PairwiseEquivalence) object; + return this.elementEquivalence.equals(that.elementEquivalence); + } + + return false; + } + + @Override + public int hashCode() { + return elementEquivalence.hashCode() ^ 0x46a3eb07; + } + + @Override + public String toString() { + return elementEquivalence + ".pairwise()"; + } + + private static final long serialVersionUID = 1; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ParametricNullness.java new file mode 100644 index 0000000000000..e46a5a3195c2c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/PatternCompiler.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/PatternCompiler.java new file mode 100644 index 0000000000000..2a9348c3b4ada --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/PatternCompiler.java @@ -0,0 +1,46 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Pluggable interface for compiling a regex pattern. By default this package uses the {@code + * java.util.regex} library, but an alternate implementation can be supplied using the {@link + * java.util.ServiceLoader} mechanism. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +interface PatternCompiler { + /** + * Compiles the given pattern. + * + * @throws IllegalArgumentException if the pattern is invalid + */ + CommonPattern compile(String pattern); + + /** + * Returns {@code true} if the regex implementation behaves like Perl -- notably, by supporting + * possessive quantifiers but also being susceptible to catastrophic backtracking. + */ + boolean isPcreLike(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Platform.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Platform.java new file mode 100644 index 0000000000000..5113fd68f6ea6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Platform.java @@ -0,0 +1,109 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.ref.WeakReference; +import java.util.Locale; +import java.util.ServiceConfigurationError; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Jesse Wilson + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class Platform { + private static final Logger logger = Logger.getLogger(Platform.class.getName()); + private static final PatternCompiler patternCompiler = loadPatternCompiler(); + + private Platform() {} + + static CharMatcher precomputeCharMatcher(CharMatcher matcher) { + return matcher.precomputedInternal(); + } + + static > Optional getEnumIfPresent(Class enumClass, String value) { + WeakReference> ref = Enums.getEnumConstants(enumClass).get(value); + return ref == null ? Optional.absent() : Optional.of(enumClass.cast(ref.get())); + } + + static String formatCompact4Digits(double value) { + return String.format(Locale.ROOT, "%.4g", value); + } + + static boolean stringIsNullOrEmpty(String string) { + return string == null || string.isEmpty(); + } + + /** + * Returns the string if it is not null, or an empty string otherwise. + * + * @param string the string to test and possibly return + * @return {@code string} if it is not null; {@code ""} otherwise + */ + static String nullToEmpty(String string) { + return (string == null) ? "" : string; + } + + /** + * Returns the string if it is not empty, or a null string otherwise. + * + * @param string the string to test and possibly return + * @return {@code string} if it is not empty; {@code null} otherwise + */ + static String emptyToNull(String string) { + return stringIsNullOrEmpty(string) ? null : string; + } + + static CommonPattern compilePattern(String pattern) { + Preconditions.checkNotNull(pattern); + return patternCompiler.compile(pattern); + } + + static boolean patternCompilerIsPcreLike() { + return patternCompiler.isPcreLike(); + } + + private static PatternCompiler loadPatternCompiler() { + return new JdkPatternCompiler(); + } + + private static void logPatternCompilerError(ServiceConfigurationError e) { + logger.log(Level.WARNING, "Error loading regex compiler, falling back to next option", e); + } + + private static final class JdkPatternCompiler implements PatternCompiler { + @Override + public CommonPattern compile(String pattern) { + return new JdkPattern(Pattern.compile(pattern)); + } + + @Override + public boolean isPcreLike() { + return true; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Preconditions.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Preconditions.java new file mode 100644 index 0000000000000..7c3adf2a8dba2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Preconditions.java @@ -0,0 +1,1372 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static org.apache.iotdb.google.common.base.Strings.lenientFormat; + +/** + * Static convenience methods that help a method or constructor check whether it was invoked + * correctly (that is, whether its preconditions were met). + * + *

If the precondition is not met, the {@code Preconditions} method throws an unchecked exception + * of a specified type, which helps the method in which the exception was thrown communicate that + * its caller has made a mistake. This allows constructs such as + * + *

{@code
+ * public static double sqrt(double value) {
+ *   if (value < 0) {
+ *     throw new IllegalArgumentException("input is negative: " + value);
+ *   }
+ *   // calculate square root
+ * }
+ * }
+ * + *

to be replaced with the more compact + * + *

{@code
+ * public static double sqrt(double value) {
+ *   checkArgument(value >= 0, "input is negative: %s", value);
+ *   // calculate square root
+ * }
+ * }
+ * + *

so that a hypothetical bad caller of this method, such as: + * + *

{@code
+ * void exampleBadCaller() {
+ *   double d = sqrt(-1.0);
+ * }
+ * }
+ * + *

would be flagged as having called {@code sqrt()} with an illegal argument. + * + *

Performance

+ * + *

Avoid passing message arguments that are expensive to compute; your code will always compute + * them, even though they usually won't be needed. If you have such arguments, use the conventional + * if/throw idiom instead. + * + *

Depending on your message arguments, memory may be allocated for boxing and varargs array + * creation. However, the methods of this class have a large number of overloads that prevent such + * allocations in many common cases. + * + *

The message string is not formatted unless the exception will be thrown, so the cost of the + * string formatting itself should not be a concern. + * + *

As with any performance concerns, you should consider profiling your code (in a production + * environment if possible) before spending a lot of effort on tweaking a particular element. + * + *

Other types of preconditions

+ * + *

Not every type of precondition failure is supported by these methods. Continue to throw + * standard JDK exceptions such as {@link java.util.NoSuchElementException} or {@link + * UnsupportedOperationException} in the situations they are intended for. + * + *

Non-preconditions

+ * + *

It is of course possible to use the methods of this class to check for invalid conditions + * which are not the caller's fault. Doing so is not recommended because it is + * misleading to future readers of the code and of stack traces. See Conditional failures + * explained in the Guava User Guide for more advice. Notably, {@link Verify} offers assertions + * similar to those in this class for non-precondition checks. + * + *

{@code java.util.Objects.requireNonNull()}

+ * + *

Projects which use {@code org.apache.iotdb.google.common} should generally avoid the use of + * {@link java.util.Objects#requireNonNull(Object)}. Instead, use whichever of {@link + * #checkNotNull(Object)} or {@link Verify#verifyNotNull(Object)} is appropriate to the situation. + * (The same goes for the message-accepting overloads.) + * + *

Only {@code %s} is supported

+ * + *

{@code Preconditions} uses {@link Strings#lenientFormat} to format error message template + * strings. This only supports the {@code "%s"} specifier, not the full range of {@link + * java.util.Formatter} specifiers. However, note that if the number of arguments does not match the + * number of occurrences of {@code "%s"} in the format string, {@code Preconditions} will still + * behave as expected, and will still include all argument values in the error message; the message + * will simply not be formatted exactly as intended. + * + *

More information

+ * + *

See the Guava User Guide on using {@code + * Preconditions}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Preconditions { + private Preconditions() {} + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument(boolean expression, String errorMessageTemplate, char p1) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument(boolean expression, String errorMessageTemplate, int p1) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument(boolean expression, String errorMessageTemplate, long p1) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument(boolean expression, String errorMessageTemplate, Object p1) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, char p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, int p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, long p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, char p1, Object p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, char p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, int p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, long p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, int p1, Object p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, char p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, int p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, long p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, long p1, Object p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object p1, char p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object p1, int p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object p1, long p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object p1, Object p2) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object p1, Object p2, Object p3) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + *

See {@link #checkArgument(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkArgument( + boolean expression, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) { + if (!expression) { + throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + * @see Verify#verify Verify.verify() + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + * @see Verify#verify Verify.verify() + */ + public static void checkState(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @see Verify#verify Verify.verify() + */ + public static void checkState( + boolean expression, + /* + * TODO(cpovirk): Consider removing @CheckForNull here, as we've done with the other methods' + * errorMessageTemplate parameters: It is unlikely that callers intend for their string + * template to be null (though we do handle that case gracefully at runtime). I've left this + * one as it is because one of our users has defined a wrapper API around Preconditions, + * declaring a checkState method that accepts a possibly null template. So we'd need to update + * that user first. + */ + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, char p1) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, int p1) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, long p1) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, Object p1) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, char p1, char p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, char p1, int p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, char p1, long p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, char p1, Object p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, int p1, char p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, int p1, int p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, int p1, long p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, int p1, Object p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, long p1, char p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, long p1, int p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState(boolean expression, String errorMessageTemplate, long p1, long p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, long p1, Object p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, Object p1, char p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, Object p1, int p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, Object p1, long p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, Object p1, Object p2) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, Object p1, Object p2, Object p3) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + *

See {@link #checkState(boolean, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static void checkState( + boolean expression, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) { + if (!expression) { + throw new IllegalStateException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + } + } + + /* + * Preconditions.checkNotNull is *intended* for performing eager null checks on parameters that a + * nullness checker can already "prove" are non-null. That means that the first parameter to + * checkNotNull *should* be annotated to require it to be non-null. + * + * However, for a variety of reasons, Google developers have written a ton of code over the past + * decade that assumes that they can use checkNotNull for non-precondition checks. I had hoped to + * take a principled stand on this, but the amount of such code is simply overwhelming. To avoid + * creating a lot of compile errors that users would not find to be informative, we're giving in + * and allowing callers to pass arguments that a nullness checker believes could be null. + * + * We still encourage people to use requireNonNull over checkNotNull for non-precondition checks. + */ + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + * @see Verify#verifyNotNull Verify.verifyNotNull() + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + * @see Verify#verifyNotNull Verify.verifyNotNull() + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + * @see Verify#verifyNotNull Verify.verifyNotNull() + */ + public static T checkNotNull( + T reference, String errorMessageTemplate, Object... errorMessageArgs) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, char p1) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, int p1) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, long p1) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, Object p1) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, char p1, char p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, char p1, int p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, char p1, long p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, char p1, Object p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, int p1, char p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, int p1, int p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, int p1, long p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, int p1, Object p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, long p1, char p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, long p1, int p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, long p1, long p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, long p1, Object p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, Object p1, char p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, Object p1, int p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, Object p1, long p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull(T reference, String errorMessageTemplate, Object p1, Object p2) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull( + T reference, String errorMessageTemplate, Object p1, Object p2, Object p3) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + *

See {@link #checkNotNull(Object, String, Object...)} for details. + * + * @since 20.0 (varargs overload since 2.0) + */ + public static T checkNotNull( + T reference, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) { + if (reference == null) { + throw new NullPointerException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + } + return reference; + } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning methods are much slower. + * This is a big deal - we're talking factors of 2-8 in microbenchmarks, not just 10-20%. (This is + * a hotspot optimizer bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. There is a + * RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, depending on the args, so it + * appears that this pattern is not directly applicable. But we can use the ridiculous, devious + * trick of throwing an exception in the middle of the construction of another exception. Hotspot + * is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid element in an array, list or string of size + * {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid element in an array, list or string of size + * {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return lenientFormat("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return lenientFormat("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return lenientFormat("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return lenientFormat("%s (%s) must not be greater than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify valid positions in an array, list or + * string of size {@code size}, and are in order. A position index may range from zero to {@code + * size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an array, list or string + * @param end a user-supplied index identifying an ending position in an array, list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size}, + * or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start < 0 || start > size) { + return badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return lenientFormat("end index (%s) must not be less than start index (%s)", end, start); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Predicate.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Predicate.java new file mode 100644 index 0000000000000..11f7a73360061 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Predicate.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Legacy version of {@link java.util.function.Predicate java.util.function.Predicate}. Determines a + * true or false value for a given input. + * + *

As this interface extends {@code java.util.function.Predicate}, an instance of this type may + * be used as a {@code Predicate} directly. To use a {@code java.util.function.Predicate} where a + * {@code org.apache.iotdb.google.common.base.Predicate} is expected, use the method reference + * {@code predicate::test}. + * + *

This interface is now a legacy type. Use {@code java.util.function.Predicate} (or the + * appropriate primitive specialization such as {@code IntPredicate}) instead whenever possible. + * Otherwise, at least reduce explicit dependencies on this type by using lambda expressions + * or method references instead of classes, leaving your code easier to migrate in the future. + * + *

The {@link Predicates} class provides common predicates and related utilities. + * + *

See the Guava User Guide article on the use of {@code Predicate}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@FunctionalInterface +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface Predicate extends java.util.function.Predicate { + /** + * Returns the result of applying this predicate to {@code input} (Java 8 users, see notes in the + * class documentation above). This method is generally expected, but not absolutely + * required, to have the following properties: + * + *

    + *
  • Its execution does not cause any observable side effects. + *
  • The computation is consistent with equals; that is, {@link Objects#equal + * Objects.equal}{@code (a, b)} implies that {@code predicate.apply(a) == + * predicate.apply(b))}. + *
+ * + * @throws NullPointerException if {@code input} is null and this predicate does not accept null + * arguments + */ + boolean apply(@ParametricNullness T input); + + /** + * Indicates whether another object is equal to this predicate. + * + *

Most implementations will have no reason to override the behavior of {@link Object#equals}. + * However, an implementation may also choose to return {@code true} whenever {@code object} is a + * {@link Predicate} that it considers interchangeable with this one. "Interchangeable" + * typically means that {@code this.apply(t) == that.apply(t)} for all {@code t} of type + * {@code T}). Note that a {@code false} result from this method does not imply that the + * predicates are known not to be interchangeable. + */ + @Override + boolean equals(Object object); + + @Override + default boolean test(@ParametricNullness T input) { + return apply(input); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Predicates.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Predicates.java new file mode 100644 index 0000000000000..f9864276a6274 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Predicates.java @@ -0,0 +1,754 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@code Predicate} instances. + * + *

All methods return serializable predicates as long as they're given serializable parameters. + * + *

See the Guava User Guide article on the use of {@code Predicate}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Predicates { + private Predicates() {} + + // TODO(kevinb): considering having these implement a VisitablePredicate + // interface which specifies an accept(PredicateVisitor) method. + + /** Returns a predicate that always evaluates to {@code true}. */ + @GwtCompatible(serializable = true) + public static Predicate alwaysTrue() { + return ObjectPredicate.ALWAYS_TRUE.withNarrowedType(); + } + + /** Returns a predicate that always evaluates to {@code false}. */ + @GwtCompatible(serializable = true) + public static Predicate alwaysFalse() { + return ObjectPredicate.ALWAYS_FALSE.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference being tested is + * null. + */ + @GwtCompatible(serializable = true) + public static Predicate isNull() { + return ObjectPredicate.IS_NULL.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference being tested is not + * null. + */ + @GwtCompatible(serializable = true) + public static Predicate notNull() { + return ObjectPredicate.NOT_NULL.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the given predicate evaluates to {@code + * false}. + */ + public static Predicate not(Predicate predicate) { + return new NotPredicate<>(predicate); + } + + /** + * Returns a predicate that evaluates to {@code true} if each of its components evaluates to + * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" + * as soon as a false predicate is found. It defensively copies the iterable passed in, so future + * changes to it won't alter the behavior of this predicate. If {@code components} is empty, the + * returned predicate will always evaluate to {@code true}. + */ + public static Predicate and( + Iterable> components) { + return new AndPredicate<>(defensiveCopy(components)); + } + + /** + * Returns a predicate that evaluates to {@code true} if each of its components evaluates to + * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" + * as soon as a false predicate is found. It defensively copies the array passed in, so future + * changes to it won't alter the behavior of this predicate. If {@code components} is empty, the + * returned predicate will always evaluate to {@code true}. + */ + @SafeVarargs + public static Predicate and(Predicate... components) { + return new AndPredicate(defensiveCopy(components)); + } + + /** + * Returns a predicate that evaluates to {@code true} if both of its components evaluate to {@code + * true}. The components are evaluated in order, and evaluation will be "short-circuited" as soon + * as a false predicate is found. + */ + public static Predicate and( + Predicate first, Predicate second) { + return new AndPredicate<>(Predicates.asList(checkNotNull(first), checkNotNull(second))); + } + + /** + * Returns a predicate that evaluates to {@code true} if any one of its components evaluates to + * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" + * as soon as a true predicate is found. It defensively copies the iterable passed in, so future + * changes to it won't alter the behavior of this predicate. If {@code components} is empty, the + * returned predicate will always evaluate to {@code false}. + */ + public static Predicate or( + Iterable> components) { + return new OrPredicate<>(defensiveCopy(components)); + } + + /** + * Returns a predicate that evaluates to {@code true} if any one of its components evaluates to + * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" + * as soon as a true predicate is found. It defensively copies the array passed in, so future + * changes to it won't alter the behavior of this predicate. If {@code components} is empty, the + * returned predicate will always evaluate to {@code false}. + */ + @SafeVarargs + public static Predicate or(Predicate... components) { + return new OrPredicate(defensiveCopy(components)); + } + + /** + * Returns a predicate that evaluates to {@code true} if either of its components evaluates to + * {@code true}. The components are evaluated in order, and evaluation will be "short-circuited" + * as soon as a true predicate is found. + */ + public static Predicate or( + Predicate first, Predicate second) { + return new OrPredicate<>(Predicates.asList(checkNotNull(first), checkNotNull(second))); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object being tested {@code equals()} + * the given target or both are null. + */ + public static Predicate equalTo(@ParametricNullness T target) { + return (target == null) + ? Predicates.isNull() + : new IsEqualToPredicate(target).withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object being tested is an instance of + * the given class. If the object being tested is {@code null} this predicate evaluates to {@code + * false}. + * + *

If you want to filter an {@code Iterable} to narrow its type, consider using {@link + * org.apache.iotdb.google.common.collect.Iterables#filter(Iterable, Class)} in preference. + * + *

Warning: contrary to the typical assumptions about predicates (as documented at + * {@link Predicate#apply}), the returned predicate may not be consistent with equals. For + * example, {@code instanceOf(ArrayList.class)} will yield different results for the two equal + * instances {@code Lists.newArrayList(1)} and {@code Arrays.asList(1)}. + */ + @GwtIncompatible // Class.isInstance + public static Predicate instanceOf(Class clazz) { + return new InstanceOfPredicate<>(clazz); + } + + /** + * Returns a predicate that evaluates to {@code true} if the class being tested is assignable to + * (is a subtype of) {@code clazz}. Example: + * + *

{@code
+   * List> classes = Arrays.asList(
+   *     Object.class, String.class, Number.class, Long.class);
+   * return Iterables.filter(classes, subtypeOf(Number.class));
+   * }
+ * + * The code above returns an iterable containing {@code Number.class} and {@code Long.class}. + * + * @since 20.0 (since 10.0 under the incorrect name {@code assignableFrom}) + */ + @J2ktIncompatible + @GwtIncompatible // Class.isAssignableFrom + public static Predicate> subtypeOf(Class clazz) { + return new SubtypeOfPredicate(clazz); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference being tested is a + * member of the given collection. It does not defensively copy the collection passed in, so + * future changes to it will alter the behavior of the predicate. + * + *

This method can technically accept any {@code Collection}, but using a typed collection + * helps prevent bugs. This approach doesn't block any potential users since it is always possible + * to use {@code Predicates.in()}. + * + * @param target the collection that may contain the function input + */ + public static Predicate in(Collection target) { + return new InPredicate<>(target); + } + + /** + * Returns the composition of a function and a predicate. For every {@code x}, the generated + * predicate returns {@code predicate(function(x))}. + * + * @return the composition of the provided function and predicate + */ + public static Predicate compose( + Predicate predicate, Function function) { + return new CompositionPredicate<>(predicate, function); + } + + /** + * Returns a predicate that evaluates to {@code true} if the {@code CharSequence} being tested + * contains any match for the given regular expression pattern. The test used is equivalent to + * {@code Pattern.compile(pattern).matcher(arg).find()} + * + * @throws IllegalArgumentException if the pattern is invalid + * @since 3.0 + */ + @J2ktIncompatible + @GwtIncompatible // Only used by other GWT-incompatible code. + public static Predicate containsPattern(String pattern) { + return new ContainsPatternFromStringPredicate(pattern); + } + + /** + * Returns a predicate that evaluates to {@code true} if the {@code CharSequence} being tested + * contains any match for the given regular expression pattern. The test used is equivalent to + * {@code pattern.matcher(arg).find()} + * + * @since 3.0 + */ + @J2ktIncompatible + @GwtIncompatible(value = "java.util.regex.Pattern") + public static Predicate contains(Pattern pattern) { + return new ContainsPatternPredicate(new JdkPattern(pattern)); + } + + // End public API, begin private implementation classes. + + // Package private for GWT serialization. + enum ObjectPredicate implements Predicate { + /** + * @see Predicates#alwaysTrue() + */ + ALWAYS_TRUE { + @Override + public boolean apply(Object o) { + return true; + } + + @Override + public String toString() { + return "Predicates.alwaysTrue()"; + } + }, + /** + * @see Predicates#alwaysFalse() + */ + ALWAYS_FALSE { + @Override + public boolean apply(Object o) { + return false; + } + + @Override + public String toString() { + return "Predicates.alwaysFalse()"; + } + }, + /** + * @see Predicates#isNull() + */ + IS_NULL { + @Override + public boolean apply(Object o) { + return o == null; + } + + @Override + public String toString() { + return "Predicates.isNull()"; + } + }, + /** + * @see Predicates#notNull() + */ + NOT_NULL { + @Override + public boolean apply(Object o) { + return o != null; + } + + @Override + public String toString() { + return "Predicates.notNull()"; + } + }; + + @SuppressWarnings("unchecked") // safe contravariant cast + Predicate withNarrowedType() { + return (Predicate) this; + } + } + + /** + * @see Predicates#not(Predicate) + */ + private static class NotPredicate implements Predicate, Serializable { + final Predicate predicate; + + NotPredicate(Predicate predicate) { + this.predicate = checkNotNull(predicate); + } + + @Override + public boolean apply(@ParametricNullness T t) { + return !predicate.apply(t); + } + + @Override + public int hashCode() { + return ~predicate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NotPredicate) { + NotPredicate that = (NotPredicate) obj; + return predicate.equals(that.predicate); + } + return false; + } + + @Override + public String toString() { + return "Predicates.not(" + predicate + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#and(Iterable) + */ + private static class AndPredicate implements Predicate, Serializable { + private final List> components; + + private AndPredicate(List> components) { + this.components = components; + } + + @Override + public boolean apply(@ParametricNullness T t) { + // Avoid using the Iterator to avoid generating garbage (issue 820). + for (int i = 0; i < components.size(); i++) { + if (!components.get(i).apply(t)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + // add a random number to avoid collisions with OrPredicate + return components.hashCode() + 0x12472c2c; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof AndPredicate) { + AndPredicate that = (AndPredicate) obj; + return components.equals(that.components); + } + return false; + } + + @Override + public String toString() { + return toStringHelper("and", components); + } + + private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#or(Iterable) + */ + private static class OrPredicate implements Predicate, Serializable { + private final List> components; + + private OrPredicate(List> components) { + this.components = components; + } + + @Override + public boolean apply(@ParametricNullness T t) { + // Avoid using the Iterator to avoid generating garbage (issue 820). + for (int i = 0; i < components.size(); i++) { + if (components.get(i).apply(t)) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + // add a random number to avoid collisions with AndPredicate + return components.hashCode() + 0x053c91cf; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof OrPredicate) { + OrPredicate that = (OrPredicate) obj; + return components.equals(that.components); + } + return false; + } + + @Override + public String toString() { + return toStringHelper("or", components); + } + + private static final long serialVersionUID = 0; + } + + private static String toStringHelper(String methodName, Iterable components) { + StringBuilder builder = new StringBuilder("Predicates.").append(methodName).append('('); + boolean first = true; + for (Object o : components) { + if (!first) { + builder.append(','); + } + builder.append(o); + first = false; + } + return builder.append(')').toString(); + } + + /** + * @see Predicates#equalTo(Object) + */ + private static class IsEqualToPredicate implements Predicate, Serializable { + private final Object target; + + private IsEqualToPredicate(Object target) { + this.target = target; + } + + @Override + public boolean apply(Object o) { + return target.equals(o); + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IsEqualToPredicate) { + IsEqualToPredicate that = (IsEqualToPredicate) obj; + return target.equals(that.target); + } + return false; + } + + @Override + public String toString() { + return "Predicates.equalTo(" + target + ")"; + } + + private static final long serialVersionUID = 0; + + @SuppressWarnings("unchecked") // safe contravariant cast + Predicate withNarrowedType() { + return (Predicate) this; + } + } + + /** + * @see Predicates#instanceOf(Class) + */ + @GwtIncompatible // Class.isInstance + private static class InstanceOfPredicate implements Predicate, Serializable { + private final Class clazz; + + private InstanceOfPredicate(Class clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public boolean apply(@ParametricNullness T o) { + return clazz.isInstance(o); + } + + @Override + public int hashCode() { + return clazz.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof InstanceOfPredicate) { + InstanceOfPredicate that = (InstanceOfPredicate) obj; + return clazz == that.clazz; + } + return false; + } + + @Override + public String toString() { + return "Predicates.instanceOf(" + clazz.getName() + ")"; + } + + @J2ktIncompatible private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#subtypeOf(Class) + */ + @J2ktIncompatible + @GwtIncompatible // Class.isAssignableFrom + private static class SubtypeOfPredicate implements Predicate>, Serializable { + private final Class clazz; + + private SubtypeOfPredicate(Class clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public boolean apply(Class input) { + return clazz.isAssignableFrom(input); + } + + @Override + public int hashCode() { + return clazz.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof SubtypeOfPredicate) { + SubtypeOfPredicate that = (SubtypeOfPredicate) obj; + return clazz == that.clazz; + } + return false; + } + + @Override + public String toString() { + return "Predicates.subtypeOf(" + clazz.getName() + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#in(Collection) + */ + private static class InPredicate implements Predicate, Serializable { + private final Collection target; + + private InPredicate(Collection target) { + this.target = checkNotNull(target); + } + + @Override + public boolean apply(@ParametricNullness T t) { + try { + return target.contains(t); + } catch (NullPointerException | ClassCastException e) { + return false; + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof InPredicate) { + InPredicate that = (InPredicate) obj; + return target.equals(that.target); + } + return false; + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public String toString() { + return "Predicates.in(" + target + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#compose(Predicate, Function) + */ + private static class CompositionPredicate + implements Predicate, Serializable { + final Predicate p; + final Function f; + + private CompositionPredicate(Predicate p, Function f) { + this.p = checkNotNull(p); + this.f = checkNotNull(f); + } + + @Override + public boolean apply(@ParametricNullness A a) { + return p.apply(f.apply(a)); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CompositionPredicate) { + CompositionPredicate that = (CompositionPredicate) obj; + return f.equals(that.f) && p.equals(that.p); + } + return false; + } + + @Override + public int hashCode() { + return f.hashCode() ^ p.hashCode(); + } + + @Override + public String toString() { + // TODO(cpovirk): maybe make this look like the method call does ("Predicates.compose(...)") + return p + "(" + f + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#contains(Pattern) + */ + @J2ktIncompatible + @GwtIncompatible // Only used by other GWT-incompatible code. + private static class ContainsPatternPredicate implements Predicate, Serializable { + final CommonPattern pattern; + + ContainsPatternPredicate(CommonPattern pattern) { + this.pattern = checkNotNull(pattern); + } + + @Override + public boolean apply(CharSequence t) { + return pattern.matcher(t).find(); + } + + @Override + public int hashCode() { + // Pattern uses Object.hashCode, so we have to reach + // inside to build a hashCode consistent with equals. + + return Objects.hashCode(pattern.pattern(), pattern.flags()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ContainsPatternPredicate) { + ContainsPatternPredicate that = (ContainsPatternPredicate) obj; + + // Pattern uses Object (identity) equality, so we have to reach + // inside to compare individual fields. + return Objects.equal(pattern.pattern(), that.pattern.pattern()) + && pattern.flags() == that.pattern.flags(); + } + return false; + } + + @Override + public String toString() { + String patternString = + MoreObjects.toStringHelper(pattern) + .add("pattern", pattern.pattern()) + .add("pattern.flags", pattern.flags()) + .toString(); + return "Predicates.contains(" + patternString + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * @see Predicates#containsPattern(String) + */ + @J2ktIncompatible + @GwtIncompatible // Only used by other GWT-incompatible code. + private static class ContainsPatternFromStringPredicate extends ContainsPatternPredicate { + + ContainsPatternFromStringPredicate(String string) { + super(Platform.compilePattern(string)); + } + + @Override + public String toString() { + return "Predicates.containsPattern(" + pattern.pattern() + ")"; + } + + private static final long serialVersionUID = 0; + } + + private static List> asList( + Predicate first, Predicate second) { + // TODO(kevinb): understand why we still get a warning despite @SafeVarargs! + return Arrays.>asList(first, second); + } + + private static List defensiveCopy(T... array) { + return defensiveCopy(Arrays.asList(array)); + } + + static List defensiveCopy(Iterable iterable) { + ArrayList list = new ArrayList<>(); + for (T element : iterable) { + list.add(checkNotNull(element)); + } + return list; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Present.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Present.java new file mode 100644 index 0000000000000..1d325d9abc777 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Present.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collections; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** Implementation of an {@link Optional} containing a reference. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class Present extends Optional { + private final T reference; + + Present(T reference) { + this.reference = reference; + } + + @Override + public boolean isPresent() { + return true; + } + + @Override + public T get() { + return reference; + } + + @Override + public T or(T defaultValue) { + checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); + return reference; + } + + @Override + public Optional or(Optional secondChoice) { + checkNotNull(secondChoice); + return this; + } + + @Override + public T or(Supplier supplier) { + checkNotNull(supplier); + return reference; + } + + @Override + public T orNull() { + return reference; + } + + @Override + public Set asSet() { + return Collections.singleton(reference); + } + + @Override + public Optional transform(Function function) { + return new Present<>( + checkNotNull( + function.apply(reference), + "the Function passed to Optional.transform() must not return null.")); + } + + @Override + public boolean equals(Object object) { + if (object instanceof Present) { + Present other = (Present) object; + return reference.equals(other.reference); + } + return false; + } + + @Override + public int hashCode() { + return 0x598df91c + reference.hashCode(); + } + + @Override + public String toString() { + return "Optional.of(" + reference + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/SmallCharMatcher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/SmallCharMatcher.java new file mode 100644 index 0000000000000..13ac97acdc77e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/SmallCharMatcher.java @@ -0,0 +1,155 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.CharMatcher.NamedFastMatcher; + +import java.util.BitSet; + +/** + * An immutable version of CharMatcher for smallish sets of characters that uses a hash table with + * linear probing to check for matches. + * + * @author Christopher Swenson + */ +@J2ktIncompatible +@GwtIncompatible // no precomputation is done in GWT +@ElementTypesAreNonnullByDefault +final class SmallCharMatcher extends NamedFastMatcher { + static final int MAX_SIZE = 1023; + private final char[] table; + private final boolean containsZero; + private final long filter; + + private SmallCharMatcher(char[] table, long filter, boolean containsZero, String description) { + super(description); + this.table = table; + this.filter = filter; + this.containsZero = containsZero; + } + + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + + /* + * This method was rewritten in Java from an intermediate step of the Murmur hash function in + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the + * following header: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author + * hereby disclaims copyright to this source code. + */ + static int smear(int hashCode) { + return C2 * Integer.rotateLeft(hashCode * C1, 15); + } + + private boolean checkFilter(int c) { + return 1 == (1 & (filter >> c)); + } + + // This is all essentially copied from ImmutableSet, but we have to duplicate because + // of dependencies. + + // Represents how tightly we can pack things, as a maximum. + private static final double DESIRED_LOAD_FACTOR = 0.5; + + /** + * Returns an array size suitable for the backing array of a hash table that uses open addressing + * with linear probing in its implementation. The returned size is the smallest power of two that + * can hold setSize elements with the desired load factor. + */ + @VisibleForTesting + static int chooseTableSize(int setSize) { + if (setSize == 1) { + return 2; + } + // Correct the size for open addressing to match desired load factor. + // Round up to the next highest power of 2. + int tableSize = Integer.highestOneBit(setSize - 1) << 1; + while (tableSize * DESIRED_LOAD_FACTOR < setSize) { + tableSize <<= 1; + } + return tableSize; + } + + static CharMatcher from(BitSet chars, String description) { + // Compute the filter. + long filter = 0; + int size = chars.cardinality(); + boolean containsZero = chars.get(0); + // Compute the hash table. + char[] table = new char[chooseTableSize(size)]; + int mask = table.length - 1; + for (int c = chars.nextSetBit(0); c != -1; c = chars.nextSetBit(c + 1)) { + // Compute the filter at the same time. + filter |= 1L << c; + int index = smear(c) & mask; + while (true) { + // Check for empty. + if (table[index] == 0) { + table[index] = (char) c; + break; + } + // Linear probing. + index = (index + 1) & mask; + } + } + return new SmallCharMatcher(table, filter, containsZero, description); + } + + @Override + public boolean matches(char c) { + if (c == 0) { + return containsZero; + } + if (!checkFilter(c)) { + return false; + } + int mask = table.length - 1; + int startingIndex = smear(c) & mask; + int index = startingIndex; + do { + if (table[index] == 0) { // Check for empty. + return false; + } else if (table[index] == c) { // Check for match. + return true; + } else { // Linear probing. + index = (index + 1) & mask; + } + // Check to see if we wrapped around the whole table. + } while (index != startingIndex); + return false; + } + + @Override + void setBits(BitSet table) { + if (containsZero) { + table.set(0); + } + for (char c : this.table) { + if (c != 0) { + table.set(c); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Splitter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Splitter.java new file mode 100644 index 0000000000000..0bf6423aee9b7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Splitter.java @@ -0,0 +1,637 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Extracts non-overlapping substrings from an input string, typically by recognizing appearances of + * a separator sequence. This separator can be specified as a single {@linkplain #on(char) + * character}, fixed {@linkplain #on(String) string}, {@linkplain #onPattern regular expression} or + * {@link #on(CharMatcher) CharMatcher} instance. Or, instead of using a separator at all, a + * splitter can extract adjacent substrings of a given {@linkplain #fixedLength fixed length}. + * + *

For example, this expression: + * + *

{@code
+ * Splitter.on(',').split("foo,bar,qux")
+ * }
+ * + * ... produces an {@code Iterable} containing {@code "foo"}, {@code "bar"} and {@code "qux"}, in + * that order. + * + *

By default, {@code Splitter}'s behavior is simplistic and unassuming. The following + * expression: + * + *

{@code
+ * Splitter.on(',').split(" foo,,,  bar ,")
+ * }
+ * + * ... yields the substrings {@code [" foo", "", "", " bar ", ""]}. If this is not the desired + * behavior, use configuration methods to obtain a new splitter instance with modified + * behavior: + * + *
{@code
+ * private static final Splitter MY_SPLITTER = Splitter.on(',')
+ *     .trimResults()
+ *     .omitEmptyStrings();
+ * }
+ * + *

Now {@code MY_SPLITTER.split("foo,,, bar ,")} returns just {@code ["foo", "bar"]}. Note that + * the order in which these configuration methods are called is never significant. + * + *

Warning: Splitter instances are immutable. Invoking a configuration method has no + * effect on the receiving instance; you must store and use the new splitter instance it returns + * instead. + * + *

{@code
+ * // Do NOT do this
+ * Splitter splitter = Splitter.on('/');
+ * splitter.trimResults(); // does nothing!
+ * return splitter.split("wrong / wrong / wrong");
+ * }
+ * + *

For separator-based splitters that do not use {@code omitEmptyStrings}, an input string + * containing {@code n} occurrences of the separator naturally yields an iterable of size {@code n + + * 1}. So if the separator does not occur anywhere in the input, a single substring is returned + * containing the entire input. Consequently, all splitters split the empty string to {@code [""]} + * (note: even fixed-length splitters). + * + *

Splitter instances are thread-safe immutable, and are therefore safe to store as {@code static + * final} constants. + * + *

The {@link Joiner} class provides the inverse operation to splitting, but note that a + * round-trip between the two should be assumed to be lossy. + * + *

Note: if {@link #fixedLength} is used in conjunction with {@link #limit}, the final + * split piece may be longer than the specified fixed length. This is because the splitter + * will stop splitting when the limit is reached, and just return the final piece as-is. + * + *

Exception: for consistency with separator-based splitters, {@code split("")} does not + * yield an empty iterable, but an iterable containing {@code ""}. This is the only case in which + * {@code Iterables.size(split(input))} does not equal {@code IntMath.divide(input.length(), + * length, CEILING)}. To avoid this behavior, use {@code omitEmptyStrings}. + * + * @param length the desired length of pieces after splitting, a positive integer + * @return a splitter, with default settings, that can split into fixed sized pieces + * @throws IllegalArgumentException if {@code length} is zero or negative + */ + public static Splitter fixedLength(final int length) { + checkArgument(length > 0, "The length may not be less than 1"); + + return new Splitter( + new Strategy() { + @Override + public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) { + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + int nextChunkStart = start + length; + return (nextChunkStart < toSplit.length() ? nextChunkStart : -1); + } + + @Override + public int separatorEnd(int separatorPosition) { + return separatorPosition; + } + }; + } + }); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter, but automatically omits + * empty strings from the results. For example, {@code + * Splitter.on(',').omitEmptyStrings().split(",a,,,b,c,,")} returns an iterable containing only + * {@code ["a", "b", "c"]}. + * + *

If either {@code trimResults} option is also specified when creating a splitter, that + * splitter always trims results first before checking for emptiness. So, for example, {@code + * Splitter.on(':').omitEmptyStrings().trimResults().split(": : : ")} returns an empty iterable. + * + *

Note that it is ordinarily not possible for {@link #split(CharSequence)} to return an empty + * iterable, but when using this option, it can (if the input sequence consists of nothing but + * separators). + * + * @return a splitter with the desired configuration + */ + public Splitter omitEmptyStrings() { + return new Splitter(strategy, true, trimmer, limit); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter but stops splitting after + * it reaches the limit. The limit defines the maximum number of items returned by the iterator, + * or the maximum size of the list returned by {@link #splitToList}. + * + *

For example, {@code Splitter.on(',').limit(3).split("a,b,c,d")} returns an iterable + * containing {@code ["a", "b", "c,d"]}. When omitting empty strings, the omitted strings do not + * count. Hence, {@code Splitter.on(',').limit(3).omitEmptyStrings().split("a,,,b,,,c,d")} returns + * an iterable containing {@code ["a", "b", "c,d"}. When trim is requested, all entries are + * trimmed, including the last. Hence {@code Splitter.on(',').limit(3).trimResults().split(" a , b + * , c , d ")} results in {@code ["a", "b", "c , d"]}. + * + * @param maxItems the maximum number of items returned + * @return a splitter with the desired configuration + * @since 9.0 + */ + public Splitter limit(int maxItems) { + checkArgument(maxItems > 0, "must be greater than zero: %s", maxItems); + return new Splitter(strategy, omitEmptyStrings, trimmer, maxItems); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter, but automatically + * removes leading and trailing {@linkplain CharMatcher#whitespace whitespace} from each returned + * substring; equivalent to {@code trimResults(CharMatcher.whitespace())}. For example, {@code + * Splitter.on(',').trimResults().split(" a, b ,c ")} returns an iterable containing {@code ["a", + * "b", "c"]}. + * + * @return a splitter with the desired configuration + */ + public Splitter trimResults() { + return trimResults(CharMatcher.whitespace()); + } + + /** + * Returns a splitter that behaves equivalently to {@code this} splitter, but removes all leading + * or trailing characters matching the given {@code CharMatcher} from each returned substring. For + * example, {@code Splitter.on(',').trimResults(CharMatcher.is('_')).split("_a ,_b_ ,c__")} + * returns an iterable containing {@code ["a ", "b_ ", "c"]}. + * + * @param trimmer a {@link CharMatcher} that determines whether a character should be removed from + * the beginning/end of a subsequence + * @return a splitter with the desired configuration + */ + // TODO(kevinb): throw if a trimmer was already specified! + public Splitter trimResults(CharMatcher trimmer) { + checkNotNull(trimmer); + return new Splitter(strategy, omitEmptyStrings, trimmer, limit); + } + + /** + * Splits {@code sequence} into string components and makes them available through an {@link + * Iterator}, which may be lazily evaluated. If you want an eagerly computed {@link List}, use + * {@link #splitToList(CharSequence)}. Java 8 users may prefer {@link #splitToStream} instead. + * + * @param sequence the sequence of characters to split + * @return an iteration over the segments split from the parameter + */ + public Iterable split(final CharSequence sequence) { + checkNotNull(sequence); + + return new Iterable() { + @Override + public Iterator iterator() { + return splittingIterator(sequence); + } + + @Override + public String toString() { + return Joiner.on(", ") + .appendTo(new StringBuilder().append('['), this) + .append(']') + .toString(); + } + }; + } + + private Iterator splittingIterator(CharSequence sequence) { + return strategy.iterator(this, sequence); + } + + /** + * Splits {@code sequence} into string components and returns them as an immutable list. If you + * want an {@link Iterable} which may be lazily evaluated, use {@link #split(CharSequence)}. + * + * @param sequence the sequence of characters to split + * @return an immutable list of the segments split from the parameter + * @since 15.0 + */ + public List splitToList(CharSequence sequence) { + checkNotNull(sequence); + + Iterator iterator = splittingIterator(sequence); + List result = new ArrayList<>(); + + while (iterator.hasNext()) { + result.add(iterator.next()); + } + + return Collections.unmodifiableList(result); + } + + /** + * Splits {@code sequence} into string components and makes them available through an {@link + * Stream}, which may be lazily evaluated. If you want an eagerly computed {@link List}, use + * {@link #splitToList(CharSequence)}. + * + * @param sequence the sequence of characters to split + * @return a stream over the segments split from the parameter + * @since 28.2 + */ + public Stream splitToStream(CharSequence sequence) { + // Can't use Streams.stream() from base + return StreamSupport.stream(split(sequence).spliterator(), false); + } + + /** + * Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries + * into keys and values using the specified separator. + * + * @since 10.0 + */ + public MapSplitter withKeyValueSeparator(String separator) { + return withKeyValueSeparator(on(separator)); + } + + /** + * Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries + * into keys and values using the specified separator. + * + * @since 14.0 + */ + public MapSplitter withKeyValueSeparator(char separator) { + return withKeyValueSeparator(on(separator)); + } + + /** + * Returns a {@code MapSplitter} which splits entries based on this splitter, and splits entries + * into keys and values using the specified key-value splitter. + * + *

Note: Any configuration option configured on this splitter, such as {@link #trimResults}, + * does not change the behavior of the {@code keyValueSplitter}. + * + *

Example: + * + *

{@code
+   * String toSplit = " x -> y, z-> a ";
+   * Splitter outerSplitter = Splitter.on(',').trimResults();
+   * MapSplitter mapSplitter = outerSplitter.withKeyValueSeparator(Splitter.on("->"));
+   * Map result = mapSplitter.split(toSplit);
+   * assertThat(result).isEqualTo(ImmutableMap.of("x ", " y", "z", " a"));
+   * }
+ * + * @since 10.0 + */ + public MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) { + return new MapSplitter(this, keyValueSplitter); + } + + /** + * An object that splits strings into maps as {@code Splitter} splits iterables and lists. Like + * {@code Splitter}, it is thread-safe and immutable. The common way to build instances is by + * providing an additional {@linkplain Splitter#withKeyValueSeparator key-value separator} to + * {@link Splitter}. + * + * @since 10.0 + */ + public static final class MapSplitter { + private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry"; + private final Splitter outerSplitter; + private final Splitter entrySplitter; + + private MapSplitter(Splitter outerSplitter, Splitter entrySplitter) { + this.outerSplitter = outerSplitter; // only "this" is passed + this.entrySplitter = checkNotNull(entrySplitter); + } + + /** + * Splits {@code sequence} into substrings, splits each substring into an entry, and returns an + * unmodifiable map with each of the entries. For example, {@code + * Splitter.on(';').trimResults().withKeyValueSeparator("=>").split("a=>b ; c=>b")} will return + * a mapping from {@code "a"} to {@code "b"} and {@code "c"} to {@code "b"}. + * + *

The returned map preserves the order of the entries from {@code sequence}. + * + * @throws IllegalArgumentException if the specified sequence does not split into valid map + * entries, or if there are duplicate keys + */ + public Map split(CharSequence sequence) { + Map map = new LinkedHashMap<>(); + for (String entry : outerSplitter.split(sequence)) { + Iterator entryFields = entrySplitter.splittingIterator(entry); + + checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry); + String key = entryFields.next(); + checkArgument(!map.containsKey(key), "Duplicate key [%s] found.", key); + + checkArgument(entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry); + String value = entryFields.next(); + map.put(key, value); + + checkArgument(!entryFields.hasNext(), INVALID_ENTRY_MESSAGE, entry); + } + return Collections.unmodifiableMap(map); + } + } + + private interface Strategy { + Iterator iterator(Splitter splitter, CharSequence toSplit); + } + + private abstract static class SplittingIterator extends AbstractIterator { + final CharSequence toSplit; + final CharMatcher trimmer; + final boolean omitEmptyStrings; + + /** + * Returns the first index in {@code toSplit} at or after {@code start} that contains the + * separator. + */ + abstract int separatorStart(int start); + + /** + * Returns the first index in {@code toSplit} after {@code separatorPosition} that does not + * contain a separator. This method is only invoked after a call to {@code separatorStart}. + */ + abstract int separatorEnd(int separatorPosition); + + int offset = 0; + int limit; + + protected SplittingIterator(Splitter splitter, CharSequence toSplit) { + this.trimmer = splitter.trimmer; + this.omitEmptyStrings = splitter.omitEmptyStrings; + this.limit = splitter.limit; + this.toSplit = toSplit; + } + + @Override + protected String computeNext() { + /* + * The returned string will be from the end of the last match to the beginning of the next + * one. nextStart is the start position of the returned substring, while offset is the place + * to start looking for a separator. + */ + int nextStart = offset; + while (offset != -1) { + int start = nextStart; + int end; + + int separatorPosition = separatorStart(offset); + if (separatorPosition == -1) { + end = toSplit.length(); + offset = -1; + } else { + end = separatorPosition; + offset = separatorEnd(separatorPosition); + } + if (offset == nextStart) { + /* + * This occurs when some pattern has an empty match, even if it doesn't match the empty + * string -- for example, if it requires lookahead or the like. The offset must be + * increased to look for separators beyond this point, without changing the start position + * of the next returned substring -- so nextStart stays the same. + */ + offset++; + if (offset > toSplit.length()) { + offset = -1; + } + continue; + } + + while (start < end && trimmer.matches(toSplit.charAt(start))) { + start++; + } + while (end > start && trimmer.matches(toSplit.charAt(end - 1))) { + end--; + } + + if (omitEmptyStrings && start == end) { + // Don't include the (unused) separator in next split string. + nextStart = offset; + continue; + } + + if (limit == 1) { + // The limit has been reached, return the rest of the string as the + // final item. This is tested after empty string removal so that + // empty strings do not count towards the limit. + end = toSplit.length(); + offset = -1; + // Since we may have changed the end, we need to trim it again. + while (end > start && trimmer.matches(toSplit.charAt(end - 1))) { + end--; + } + } else { + limit--; + } + + return toSplit.subSequence(start, end).toString(); + } + return endOfData(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/StandardSystemProperty.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/StandardSystemProperty.java new file mode 100644 index 0000000000000..e06f89cd73940 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/StandardSystemProperty.java @@ -0,0 +1,171 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Represents a {@linkplain System#getProperties() standard system property}. + * + * @author Kurt Alfred Kluever + * @since 15.0 + */ +@J2ktIncompatible +@GwtIncompatible // java.lang.System#getProperty +@ElementTypesAreNonnullByDefault +public enum StandardSystemProperty { + + /** Java Runtime Environment version. */ + JAVA_VERSION("java.version"), + + /** Java Runtime Environment vendor. */ + JAVA_VENDOR("java.vendor"), + + /** Java vendor URL. */ + JAVA_VENDOR_URL("java.vendor.url"), + + /** Java installation directory. */ + JAVA_HOME("java.home"), + + /** Java Virtual Machine specification version. */ + JAVA_VM_SPECIFICATION_VERSION("java.vm.specification.version"), + + /** Java Virtual Machine specification vendor. */ + JAVA_VM_SPECIFICATION_VENDOR("java.vm.specification.vendor"), + + /** Java Virtual Machine specification name. */ + JAVA_VM_SPECIFICATION_NAME("java.vm.specification.name"), + + /** Java Virtual Machine implementation version. */ + JAVA_VM_VERSION("java.vm.version"), + + /** Java Virtual Machine implementation vendor. */ + JAVA_VM_VENDOR("java.vm.vendor"), + + /** Java Virtual Machine implementation name. */ + JAVA_VM_NAME("java.vm.name"), + + /** Java Runtime Environment specification version. */ + JAVA_SPECIFICATION_VERSION("java.specification.version"), + + /** Java Runtime Environment specification vendor. */ + JAVA_SPECIFICATION_VENDOR("java.specification.vendor"), + + /** Java Runtime Environment specification name. */ + JAVA_SPECIFICATION_NAME("java.specification.name"), + + /** Java class format version number. */ + JAVA_CLASS_VERSION("java.class.version"), + + /** Java class path. */ + JAVA_CLASS_PATH("java.class.path"), + + /** List of paths to search when loading libraries. */ + JAVA_LIBRARY_PATH("java.library.path"), + + /** Default temp file path. */ + JAVA_IO_TMPDIR("java.io.tmpdir"), + + /** Name of JIT compiler to use. */ + JAVA_COMPILER("java.compiler"), + + /** + * Path of extension directory or directories. + * + * @deprecated This property was deprecated in + * Java 8 and removed in Java 9. We do not plan to remove this API from Guava, but if you are + * using it, it is probably not doing what you want. + */ + @Deprecated + JAVA_EXT_DIRS("java.ext.dirs"), + + /** Operating system name. */ + OS_NAME("os.name"), + + /** Operating system architecture. */ + OS_ARCH("os.arch"), + + /** Operating system version. */ + OS_VERSION("os.version"), + + /** File separator ("/" on UNIX). */ + FILE_SEPARATOR("file.separator"), + + /** Path separator (":" on UNIX). */ + PATH_SEPARATOR("path.separator"), + + /** Line separator ("\n" on UNIX). */ + LINE_SEPARATOR("line.separator"), + + /** User's account name. */ + USER_NAME("user.name"), + + /** User's home directory. */ + USER_HOME("user.home"), + + /** User's current working directory. */ + USER_DIR("user.dir"); + + private final String key; + + StandardSystemProperty(String key) { + this.key = key; + } + + /** Returns the key used to look up this system property. */ + public String key() { + return key; + } + + /** + * Returns the current value for this system property by delegating to {@link + * System#getProperty(String)}. + * + *

The value returned by this method is non-null except in rare circumstances: + * + *

    + *
  • {@link #JAVA_EXT_DIRS} was deprecated in Java 8 and removed in Java 9. We have not + * confirmed whether it is available under older versions. + *
  • {@link #JAVA_COMPILER}, while still listed as required as of Java 15, is typically not + * available even under older version. + *
  • Any property may be cleared through APIs like {@link System#clearProperty}. + *
  • Unusual environments like GWT may have their own special handling of system properties. + *
+ * + *

Note that {@code StandardSystemProperty} does not provide constants for more recently added + * properties, including: + * + *

    + *
  • {@code java.vendor.version} (added in Java 11, listed as optional as of Java 13) + *
  • {@code jdk.module.*} (added in Java 9, optional) + *
+ */ + public String value() { + return System.getProperty(key); + } + + /** Returns a string representation of this system property. */ + @Override + public String toString() { + return key() + "=" + value(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Stopwatch.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Stopwatch.java new file mode 100644 index 0000000000000..0a11aa1a5f109 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Stopwatch.java @@ -0,0 +1,288 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An object that accurately measures elapsed time: the measured duration between two + * successive readings of "now" in the same process. + * + *

In contrast, wall time is a reading of "now" as given by a method like {@link + * System#currentTimeMillis()}, best represented as an {@link java.time.Instant}. Such values + * can be subtracted to obtain a {@code Duration} (such as by {@code Duration.between}), but + * doing so does not give a reliable measurement of elapsed time, because wall time readings + * are inherently approximate, routinely affected by periodic clock corrections. Because this class + * (by default) uses {@link System#nanoTime}, it is unaffected by these changes. + * + *

Use this class instead of direct calls to {@link System#nanoTime} for two reasons: + * + *

    + *
  • The raw {@code long} values returned by {@code nanoTime} are meaningless and unsafe to use + * in any other way than how {@code Stopwatch} uses them. + *
  • An alternative source of nanosecond ticks can be substituted, for example for testing or + * performance reasons, without affecting most of your code. + *
+ * + *

The one downside of {@code Stopwatch} relative to {@link System#nanoTime()} is that {@code + * Stopwatch} requires object allocation and additional method calls, which can reduce the accuracy + * of the elapsed times reported. {@code Stopwatch} is still suitable for logging and metrics where + * reasonably accurate values are sufficient. If the uncommon case that you need to maximize + * accuracy, use {@code System.nanoTime()} directly instead. + * + *

Basic usage: + * + *

{@code
+ * Stopwatch stopwatch = Stopwatch.createStarted();
+ * doSomething();
+ * stopwatch.stop(); // optional
+ *
+ * Duration duration = stopwatch.elapsed();
+ *
+ * log.info("time: " + stopwatch); // formatted string like "12.3 ms"
+ * }
+ * + *

The state-changing methods are not idempotent; it is an error to start or stop a stopwatch + * that is already in the desired state. + * + *

When testing code that uses this class, use {@link #createUnstarted(Ticker)} or {@link + * #createStarted(Ticker)} to supply a fake or mock ticker. This allows you to simulate any valid + * behavior of the stopwatch. + * + *

Note: This class is not thread-safe. + * + *

Warning for Android users: a stopwatch with default behavior may not continue to keep + * time while the device is asleep. Instead, create one like this: + * + *

{@code
+ * Stopwatch.createStarted(
+ *      new Ticker() {
+ *        public long read() {
+ *          return android.os.SystemClock.elapsedRealtimeNanos(); // requires API Level 17
+ *        }
+ *      });
+ * }
+ * + * @author Kevin Bourrillion + * @since 10.0 + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("GoodTime") // lots of violations +@ElementTypesAreNonnullByDefault +public final class Stopwatch { + private final Ticker ticker; + private boolean isRunning; + private long elapsedNanos; + private long startTick; + + /** + * Creates (but does not start) a new stopwatch using {@link System#nanoTime} as its time source. + * + * @since 15.0 + */ + public static Stopwatch createUnstarted() { + return new Stopwatch(); + } + + /** + * Creates (but does not start) a new stopwatch, using the specified time source. + * + * @since 15.0 + */ + public static Stopwatch createUnstarted(Ticker ticker) { + return new Stopwatch(ticker); + } + + /** + * Creates (and starts) a new stopwatch using {@link System#nanoTime} as its time source. + * + * @since 15.0 + */ + public static Stopwatch createStarted() { + return new Stopwatch().start(); + } + + /** + * Creates (and starts) a new stopwatch, using the specified time source. + * + * @since 15.0 + */ + public static Stopwatch createStarted(Ticker ticker) { + return new Stopwatch(ticker).start(); + } + + Stopwatch() { + this.ticker = Ticker.systemTicker(); + } + + Stopwatch(Ticker ticker) { + this.ticker = checkNotNull(ticker, "ticker"); + } + + /** + * Returns {@code true} if {@link #start()} has been called on this stopwatch, and {@link #stop()} + * has not been called since the last call to {@code start()}. + */ + public boolean isRunning() { + return isRunning; + } + + /** + * Starts the stopwatch. + * + * @return this {@code Stopwatch} instance + * @throws IllegalStateException if the stopwatch is already running. + */ + public Stopwatch start() { + checkState(!isRunning, "This stopwatch is already running."); + isRunning = true; + startTick = ticker.read(); + return this; + } + + /** + * Stops the stopwatch. Future reads will return the fixed duration that had elapsed up to this + * point. + * + * @return this {@code Stopwatch} instance + * @throws IllegalStateException if the stopwatch is already stopped. + */ + public Stopwatch stop() { + long tick = ticker.read(); + checkState(isRunning, "This stopwatch is already stopped."); + isRunning = false; + elapsedNanos += tick - startTick; + return this; + } + + /** + * Sets the elapsed time for this stopwatch to zero, and places it in a stopped state. + * + * @return this {@code Stopwatch} instance + */ + public Stopwatch reset() { + elapsedNanos = 0; + isRunning = false; + return this; + } + + private long elapsedNanos() { + return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos; + } + + /** + * Returns the current elapsed time shown on this stopwatch, expressed in the desired time unit, + * with any fraction rounded down. + * + *

Note: the overhead of measurement can be more than a microsecond, so it is generally + * not useful to specify {@link TimeUnit#NANOSECONDS} precision here. + * + *

It is generally not a good idea to use an ambiguous, unitless {@code long} to represent + * elapsed time. Therefore, we recommend using {@link #elapsed()} instead, which returns a + * strongly-typed {@code Duration} instance. + * + * @since 14.0 (since 10.0 as {@code elapsedTime()}) + */ + public long elapsed(TimeUnit desiredUnit) { + return desiredUnit.convert(elapsedNanos(), NANOSECONDS); + } + + /** + * Returns the current elapsed time shown on this stopwatch as a {@link Duration}. Unlike {@link + * #elapsed(TimeUnit)}, this method does not lose any precision due to rounding. + * + * @since 22.0 + */ + @J2ktIncompatible + @GwtIncompatible + public Duration elapsed() { + return Duration.ofNanos(elapsedNanos()); + } + + /** Returns a string representation of the current elapsed time. */ + @Override + public String toString() { + long nanos = elapsedNanos(); + + TimeUnit unit = chooseUnit(nanos); + double value = (double) nanos / NANOSECONDS.convert(1, unit); + + // Too bad this functionality is not exposed as a regular method call + return Platform.formatCompact4Digits(value) + " " + abbreviate(unit); + } + + private static TimeUnit chooseUnit(long nanos) { + if (DAYS.convert(nanos, NANOSECONDS) > 0) { + return DAYS; + } + if (HOURS.convert(nanos, NANOSECONDS) > 0) { + return HOURS; + } + if (MINUTES.convert(nanos, NANOSECONDS) > 0) { + return MINUTES; + } + if (SECONDS.convert(nanos, NANOSECONDS) > 0) { + return SECONDS; + } + if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) { + return MILLISECONDS; + } + if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) { + return MICROSECONDS; + } + return NANOSECONDS; + } + + private static String abbreviate(TimeUnit unit) { + switch (unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "\u03bcs"; // μs + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + case MINUTES: + return "min"; + case HOURS: + return "h"; + case DAYS: + return "d"; + default: + throw new AssertionError(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Strings.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Strings.java new file mode 100644 index 0000000000000..5b42256d27165 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Strings.java @@ -0,0 +1,323 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@code String} or {@code CharSequence} instances. + * + * @author Kevin Bourrillion + * @since 3.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Strings { + private Strings() {} + + /** + * Returns the given string if it is non-null; the empty string otherwise. + * + * @param string the string to test and possibly return + * @return {@code string} itself if it is non-null; {@code ""} if it is null + */ + public static String nullToEmpty(String string) { + return Platform.nullToEmpty(string); + } + + /** + * Returns the given string if it is nonempty; {@code null} otherwise. + * + * @param string the string to test and possibly return + * @return {@code string} itself if it is nonempty; {@code null} if it is empty or null + */ + public static String emptyToNull(String string) { + return Platform.emptyToNull(string); + } + + /** + * Returns {@code true} if the given string is null or is the empty string. + * + *

Consider normalizing your string references with {@link #nullToEmpty}. If you do, you can + * use {@link String#isEmpty()} instead of this method, and you won't need special null-safe forms + * of methods like {@link String#toUpperCase} either. Or, if you'd like to normalize "in the other + * direction," converting empty strings to {@code null}, you can use {@link #emptyToNull}. + * + * @param string a string reference to check + * @return {@code true} if the string is null or is the empty string + */ + public static boolean isNullOrEmpty(String string) { + return Platform.stringIsNullOrEmpty(string); + } + + /** + * Returns a string, of length at least {@code minLength}, consisting of {@code string} prepended + * with as many copies of {@code padChar} as are necessary to reach that length. For example, + * + *

    + *
  • {@code padStart("7", 3, '0')} returns {@code "007"} + *
  • {@code padStart("2010", 3, '0')} returns {@code "2010"} + *
+ * + *

See {@link java.util.Formatter} for a richer set of formatting capabilities. + * + * @param string the string which should appear at the end of the result + * @param minLength the minimum length the resulting string must have. Can be zero or negative, in + * which case the input string is always returned. + * @param padChar the character to insert at the beginning of the result until the minimum length + * is reached + * @return the padded string + */ + public static String padStart(String string, int minLength, char padChar) { + checkNotNull(string); // eager for GWT. + if (string.length() >= minLength) { + return string; + } + StringBuilder sb = new StringBuilder(minLength); + for (int i = string.length(); i < minLength; i++) { + sb.append(padChar); + } + sb.append(string); + return sb.toString(); + } + + /** + * Returns a string, of length at least {@code minLength}, consisting of {@code string} appended + * with as many copies of {@code padChar} as are necessary to reach that length. For example, + * + *

    + *
  • {@code padEnd("4.", 5, '0')} returns {@code "4.000"} + *
  • {@code padEnd("2010", 3, '!')} returns {@code "2010"} + *
+ * + *

See {@link java.util.Formatter} for a richer set of formatting capabilities. + * + * @param string the string which should appear at the beginning of the result + * @param minLength the minimum length the resulting string must have. Can be zero or negative, in + * which case the input string is always returned. + * @param padChar the character to append to the end of the result until the minimum length is + * reached + * @return the padded string + */ + public static String padEnd(String string, int minLength, char padChar) { + checkNotNull(string); // eager for GWT. + if (string.length() >= minLength) { + return string; + } + StringBuilder sb = new StringBuilder(minLength); + sb.append(string); + for (int i = string.length(); i < minLength; i++) { + sb.append(padChar); + } + return sb.toString(); + } + + /** + * Returns a string consisting of a specific number of concatenated copies of an input string. For + * example, {@code repeat("hey", 3)} returns the string {@code "heyheyhey"}. + * + *

Java 11+ users: use {@code string.repeat(count)} instead. + * + * @param string any non-null string + * @param count the number of times to repeat it; a nonnegative integer + * @return a string containing {@code string} repeated {@code count} times (the empty string if + * {@code count} is zero) + * @throws IllegalArgumentException if {@code count} is negative + */ + public static String repeat(String string, int count) { + checkNotNull(string); // eager for GWT. + + if (count <= 1) { + checkArgument(count >= 0, "invalid count: %s", count); + return (count == 0) ? "" : string; + } + + // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark + final int len = string.length(); + final long longSize = (long) len * (long) count; + final int size = (int) longSize; + if (size != longSize) { + throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize); + } + + final char[] array = new char[size]; + string.getChars(0, len, array, 0); + int n; + for (n = len; n < size - n; n <<= 1) { + System.arraycopy(array, 0, array, n, n); + } + System.arraycopy(array, 0, array, n, size - n); + return new String(array); + } + + /** + * Returns the longest string {@code prefix} such that {@code a.toString().startsWith(prefix) && + * b.toString().startsWith(prefix)}, taking care not to split surrogate pairs. If {@code a} and + * {@code b} have no common prefix, returns the empty string. + * + * @since 11.0 + */ + public static String commonPrefix(CharSequence a, CharSequence b) { + checkNotNull(a); + checkNotNull(b); + + int maxPrefixLength = Math.min(a.length(), b.length()); + int p = 0; + while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) { + p++; + } + if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) { + p--; + } + return a.subSequence(0, p).toString(); + } + + /** + * Returns the longest string {@code suffix} such that {@code a.toString().endsWith(suffix) && + * b.toString().endsWith(suffix)}, taking care not to split surrogate pairs. If {@code a} and + * {@code b} have no common suffix, returns the empty string. + * + * @since 11.0 + */ + public static String commonSuffix(CharSequence a, CharSequence b) { + checkNotNull(a); + checkNotNull(b); + + int maxSuffixLength = Math.min(a.length(), b.length()); + int s = 0; + while (s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) { + s++; + } + if (validSurrogatePairAt(a, a.length() - s - 1) + || validSurrogatePairAt(b, b.length() - s - 1)) { + s--; + } + return a.subSequence(a.length() - s, a.length()).toString(); + } + + /** + * True when a valid surrogate pair starts at the given {@code index} in the given {@code string}. + * Out-of-range indexes return false. + */ + @VisibleForTesting + static boolean validSurrogatePairAt(CharSequence string, int index) { + return index >= 0 + && index <= (string.length() - 2) + && Character.isHighSurrogate(string.charAt(index)) + && Character.isLowSurrogate(string.charAt(index + 1)); + } + + /** + * Returns the given {@code template} string with each occurrence of {@code "%s"} replaced with + * the corresponding argument value from {@code args}; or, if the placeholder and argument counts + * do not match, returns a best-effort form of that string. Will not throw an exception under + * normal conditions. + * + *

Note: For most string-formatting needs, use {@link String#format String.format}, + * {@link java.io.PrintWriter#format PrintWriter.format}, and related methods. These support the + * full range of format + * specifiers, and alert you to usage errors by throwing {@link + * java.util.IllegalFormatException}. + * + *

In certain cases, such as outputting debugging information or constructing a message to be + * used for another unchecked exception, an exception during string formatting would serve little + * purpose except to supplant the real information you were trying to provide. These are the cases + * this method is made for; it instead generates a best-effort string with all supplied argument + * values present. This method is also useful in environments such as GWT where {@code + * String.format} is not available. As an example, method implementations of the {@link + * Preconditions} class use this formatter, for both of the reasons just discussed. + * + *

Warning: Only the exact two-character placeholder sequence {@code "%s"} is + * recognized. + * + * @param template a string containing zero or more {@code "%s"} placeholder sequences. {@code + * null} is treated as the four-character string {@code "null"}. + * @param args the arguments to be substituted into the message template. The first argument + * specified is substituted for the first occurrence of {@code "%s"} in the template, and so + * forth. A {@code null} argument is converted to the four-character string {@code "null"}; + * non-null values are converted to strings using {@link Object#toString()}. + * @since 25.1 + */ + // TODO(diamondm) consider using Arrays.toString() for array parameters + public static String lenientFormat(String template, Object... args) { + template = String.valueOf(template); // null -> "null" + + if (args == null) { + args = new Object[] {"(Object[])null"}; + } else { + for (int i = 0; i < args.length; i++) { + args[i] = lenientToString(args[i]); + } + } + + // start substituting the arguments into the '%s' placeholders + StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); + int templateStart = 0; + int i = 0; + while (i < args.length) { + int placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + builder.append(template, templateStart, placeholderStart); + builder.append(args[i++]); + templateStart = placeholderStart + 2; + } + builder.append(template, templateStart, template.length()); + + // if we run out of placeholders, append the extra args in square braces + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + while (i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + builder.append(']'); + } + + return builder.toString(); + } + + private static String lenientToString(Object o) { + if (o == null) { + return "null"; + } + try { + return o.toString(); + } catch (Exception e) { + // Default toString() behavior - see Object.toString() + String objectToString = + o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o)); + // Logger is created inline with fixed name to avoid forcing Proguard to create another class. + Logger.getLogger("org.apache.iotdb.google.common.base.Strings") + .log(WARNING, "Exception during lenientFormat for " + objectToString, e); + return "<" + objectToString + " threw " + e.getClass().getName() + ">"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Supplier.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Supplier.java new file mode 100644 index 0000000000000..9450a2f83ff5a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Supplier.java @@ -0,0 +1,55 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Legacy version of {@link java.util.function.Supplier java.util.function.Supplier}. Semantically, + * this could be a factory, generator, builder, closure, or something else entirely. No guarantees + * are implied by this interface. + * + *

The {@link Suppliers} class provides common suppliers and related utilities. + * + *

As this interface extends {@code java.util.function.Supplier}, an instance of this type can be + * used as a {@code java.util.function.Supplier} directly. To use a {@code + * java.util.function.Supplier} in a context where a {@code + * org.apache.iotdb.google.common.base.Supplier} is needed, use {@code supplier::get}. + * + *

See the Guava User Guide article on the use of {@code Function}. + * + * @author Harry Heymann + * @since 2.0 + */ +@GwtCompatible +@FunctionalInterface +@ElementTypesAreNonnullByDefault +public interface Supplier extends java.util.function.Supplier { + /** + * Retrieves an instance of the appropriate type. The returned object may or may not be a new + * instance, depending on the implementation. + * + * @return an instance of the appropriate type + */ + @Override + @ParametricNullness + T get(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Suppliers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Suppliers.java new file mode 100644 index 0000000000000..21fd2a062c3af --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Suppliers.java @@ -0,0 +1,389 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.Serializable; +import java.util.concurrent.TimeUnit; + +import static org.apache.iotdb.google.common.base.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Useful suppliers. + * + *

All methods return serializable suppliers as long as they're given serializable parameters. + * + * @author Laurence Gonsalves + * @author Harry Heymann + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Suppliers { + private Suppliers() {} + + /** + * Returns a new supplier which is the composition of the provided function and supplier. In other + * words, the new supplier's value will be computed by retrieving the value from {@code supplier}, + * and then applying {@code function} to that value. Note that the resulting supplier will not + * call {@code supplier} or invoke {@code function} until it is called. + */ + public static Supplier compose( + Function function, Supplier supplier) { + return new SupplierComposition<>(function, supplier); + } + + private static class SupplierComposition + implements Supplier, Serializable { + final Function function; + final Supplier supplier; + + SupplierComposition(Function function, Supplier supplier) { + this.function = checkNotNull(function); + this.supplier = checkNotNull(supplier); + } + + @Override + @ParametricNullness + public T get() { + return function.apply(supplier.get()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof SupplierComposition) { + SupplierComposition that = (SupplierComposition) obj; + return function.equals(that.function) && supplier.equals(that.supplier); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(function, supplier); + } + + @Override + public String toString() { + return "Suppliers.compose(" + function + ", " + supplier + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a supplier which caches the instance retrieved during the first call to {@code get()} + * and returns that value on subsequent calls to {@code get()}. See: memoization + * + *

The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at + * most once unless the underlying {@code get()} throws an exception. The supplier's serialized + * form does not contain the cached value, which will be recalculated when {@code get()} is called + * on the deserialized instance. + * + *

When the underlying delegate throws an exception then this memoizing supplier will keep + * delegating calls until it returns valid data. + * + *

If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is + * returned directly. + */ + public static Supplier memoize(Supplier delegate) { + if (delegate instanceof NonSerializableMemoizingSupplier + || delegate instanceof MemoizingSupplier) { + return delegate; + } + return delegate instanceof Serializable + ? new MemoizingSupplier(delegate) + : new NonSerializableMemoizingSupplier(delegate); + } + + @VisibleForTesting + static class MemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + transient volatile boolean initialized; + // "value" does not need to be volatile; visibility piggy-backs + // on volatile read of "initialized". + transient T value; + + MemoizingSupplier(Supplier delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + @ParametricNullness + public T get() { + // A 2-field variant of Double Checked Locking. + if (!initialized) { + synchronized (this) { + if (!initialized) { + T t = delegate.get(); + value = t; + initialized = true; + return t; + } + } + } + // This is safe because we checked `initialized.` + return uncheckedCastNullableTToT(value); + } + + @Override + public String toString() { + return "Suppliers.memoize(" + + (initialized ? "" : delegate) + + ")"; + } + + private static final long serialVersionUID = 0; + } + + @VisibleForTesting + static class NonSerializableMemoizingSupplier implements Supplier { + @SuppressWarnings("UnnecessaryLambda") // Must be a fixed singleton object + private static final Supplier SUCCESSFULLY_COMPUTED = + () -> { + throw new IllegalStateException(); // Should never get called. + }; + + private volatile Supplier delegate; + // "value" does not need to be volatile; visibility piggy-backs on volatile read of "delegate". + private T value; + + NonSerializableMemoizingSupplier(Supplier delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + @ParametricNullness + @SuppressWarnings("unchecked") // Cast from Supplier to Supplier is always valid + public T get() { + // Because Supplier is read-heavy, we use the "double-checked locking" pattern. + if (delegate != SUCCESSFULLY_COMPUTED) { + synchronized (this) { + if (delegate != SUCCESSFULLY_COMPUTED) { + T t = delegate.get(); + value = t; + delegate = (Supplier) SUCCESSFULLY_COMPUTED; + return t; + } + } + } + // This is safe because we checked `delegate.` + return uncheckedCastNullableTToT(value); + } + + @Override + public String toString() { + Supplier delegate = this.delegate; + return "Suppliers.memoize(" + + (delegate == SUCCESSFULLY_COMPUTED + ? "" + : delegate) + + ")"; + } + } + + /** + * Returns a supplier that caches the instance supplied by the delegate and removes the cached + * value after the specified time has passed. Subsequent calls to {@code get()} return the cached + * value if the expiration time has not passed. After the expiration time, a new value is + * retrieved, cached, and returned. See: memoization + * + *

The returned supplier is thread-safe. The supplier's serialized form does not contain the + * cached value, which will be recalculated when {@code get()} is called on the reserialized + * instance. The actual memoization does not happen when the underlying delegate throws an + * exception. + * + *

When the underlying delegate throws an exception then this memoizing supplier will keep + * delegating calls until it returns valid data. + * + * @param duration the length of time after a value is created that it should stop being returned + * by subsequent {@code get()} calls + * @param unit the unit that {@code duration} is expressed in + * @throws IllegalArgumentException if {@code duration} is not positive + * @since 2.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static Supplier memoizeWithExpiration( + Supplier delegate, long duration, TimeUnit unit) { + return new ExpiringMemoizingSupplier<>(delegate, duration, unit); + } + + @VisibleForTesting + @SuppressWarnings("GoodTime") // lots of violations + static class ExpiringMemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + final long durationNanos; + transient volatile T value; + // The special value 0 means "not yet initialized". + transient volatile long expirationNanos; + + ExpiringMemoizingSupplier(Supplier delegate, long duration, TimeUnit unit) { + this.delegate = checkNotNull(delegate); + this.durationNanos = unit.toNanos(duration); + checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit); + } + + @Override + @ParametricNullness + @SuppressWarnings("GoodTime") // reading system time without TimeSource + public T get() { + // Another variant of Double Checked Locking. + // + // We use two volatile reads. We could reduce this to one by + // putting our fields into a holder class, but (at least on x86) + // the extra memory consumption and indirection are more + // expensive than the extra volatile reads. + long nanos = expirationNanos; + long now = System.nanoTime(); + if (nanos == 0 || now - nanos >= 0) { + synchronized (this) { + if (nanos == expirationNanos) { // recheck for lost race + T t = delegate.get(); + value = t; + nanos = now + durationNanos; + // In the very unlikely event that nanos is 0, set it to 1; + // no one will notice 1 ns of tardiness. + expirationNanos = (nanos == 0) ? 1 : nanos; + return t; + } + } + } + // This is safe because we checked `expirationNanos.` + return uncheckedCastNullableTToT(value); + } + + @Override + public String toString() { + // This is a little strange if the unit the user provided was not NANOS, + // but we don't want to store the unit just for toString + return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)"; + } + + private static final long serialVersionUID = 0; + } + + /** Returns a supplier that always supplies {@code instance}. */ + public static Supplier ofInstance(@ParametricNullness T instance) { + return new SupplierOfInstance<>(instance); + } + + private static class SupplierOfInstance implements Supplier, Serializable { + @ParametricNullness final T instance; + + SupplierOfInstance(@ParametricNullness T instance) { + this.instance = instance; + } + + @Override + @ParametricNullness + public T get() { + return instance; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof SupplierOfInstance) { + SupplierOfInstance that = (SupplierOfInstance) obj; + return Objects.equal(instance, that.instance); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(instance); + } + + @Override + public String toString() { + return "Suppliers.ofInstance(" + instance + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a supplier whose {@code get()} method synchronizes on {@code delegate} before calling + * it, making it thread-safe. + */ + public static Supplier synchronizedSupplier(Supplier delegate) { + return new ThreadSafeSupplier<>(delegate); + } + + private static class ThreadSafeSupplier implements Supplier, Serializable { + final Supplier delegate; + + ThreadSafeSupplier(Supplier delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + @ParametricNullness + public T get() { + synchronized (delegate) { + return delegate.get(); + } + } + + @Override + public String toString() { + return "Suppliers.synchronizedSupplier(" + delegate + ")"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a function that accepts a supplier and returns the result of invoking {@link + * Supplier#get} on that supplier. + * + *

Java 8 users: use the method reference {@code Supplier::get} instead. + * + * @since 8.0 + */ + public static Function, T> supplierFunction() { + @SuppressWarnings("unchecked") // implementation is "fully variant" + SupplierFunction sf = (SupplierFunction) SupplierFunctionImpl.INSTANCE; + return sf; + } + + private interface SupplierFunction extends Function, T> {} + + private enum SupplierFunctionImpl implements SupplierFunction { + INSTANCE; + + // Note: This makes T a "pass-through type" + @Override + public Object apply(Supplier input) { + return input.get(); + } + + @Override + public String toString() { + return "Suppliers.supplierFunction()"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Throwables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Throwables.java new file mode 100644 index 0000000000000..5b169daaf217b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Throwables.java @@ -0,0 +1,566 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to instances of {@link Throwable}. + * + *

See the Guava User Guide entry on Throwables. + * + * @author Kevin Bourrillion + * @author Ben Yu + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Throwables { + private Throwables() {} + + /** + * Throws {@code throwable} if it is an instance of {@code declaredType}. Example usage: + * + *

+   * for (Foo foo : foos) {
+   *   try {
+   *     foo.bar();
+   *   } catch (BarException | RuntimeException | Error t) {
+   *     failure = t;
+   *   }
+   * }
+   * if (failure != null) {
+   *   throwIfInstanceOf(failure, BarException.class);
+   *   throwIfUnchecked(failure);
+   *   throw new AssertionError(failure);
+   * }
+   * 
+ * + * @since 20.0 + */ + @J2ktIncompatible + @GwtIncompatible // Class.cast, Class.isInstance + public static void throwIfInstanceOf( + Throwable throwable, Class declaredType) throws X { + checkNotNull(throwable); + if (declaredType.isInstance(throwable)) { + throw declaredType.cast(throwable); + } + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@code + * declaredType}. Example usage: + * + *
+   * try {
+   *   someMethodThatCouldThrowAnything();
+   * } catch (IKnowWhatToDoWithThisException e) {
+   *   handle(e);
+   * } catch (Throwable t) {
+   *   Throwables.propagateIfInstanceOf(t, IOException.class);
+   *   Throwables.propagateIfInstanceOf(t, SQLException.class);
+   *   throw Throwables.propagate(t);
+   * }
+   * 
+ * + * @deprecated Use {@link #throwIfInstanceOf}, which has the same behavior but rejects {@code + * null}. + */ + @Deprecated + @J2ktIncompatible + @GwtIncompatible // throwIfInstanceOf + public static void propagateIfInstanceOf( + Throwable throwable, Class declaredType) throws X { + if (throwable != null) { + throwIfInstanceOf(throwable, declaredType); + } + } + + /** + * Throws {@code throwable} if it is a {@link RuntimeException} or {@link Error}. Example usage: + * + *
+   * for (Foo foo : foos) {
+   *   try {
+   *     foo.bar();
+   *   } catch (RuntimeException | Error t) {
+   *     failure = t;
+   *   }
+   * }
+   * if (failure != null) {
+   *   throwIfUnchecked(failure);
+   *   throw new AssertionError(failure);
+   * }
+   * 
+ * + * @since 20.0 + */ + public static void throwIfUnchecked(Throwable throwable) { + checkNotNull(throwable); + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link + * RuntimeException} or {@link Error}. Example usage: + * + *
+   * try {
+   *   someMethodThatCouldThrowAnything();
+   * } catch (IKnowWhatToDoWithThisException e) {
+   *   handle(e);
+   * } catch (Throwable t) {
+   *   Throwables.propagateIfPossible(t);
+   *   throw new RuntimeException("unexpected", t);
+   * }
+   * 
+ * + * @deprecated Use {@link #throwIfUnchecked}, which has the same behavior but rejects {@code + * null}. + */ + @Deprecated + @J2ktIncompatible + @GwtIncompatible + public static void propagateIfPossible(Throwable throwable) { + if (throwable != null) { + throwIfUnchecked(throwable); + } + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link + * RuntimeException}, {@link Error}, or {@code declaredType}. Example usage: + * + *
+   * try {
+   *   someMethodThatCouldThrowAnything();
+   * } catch (IKnowWhatToDoWithThisException e) {
+   *   handle(e);
+   * } catch (Throwable t) {
+   *   Throwables.propagateIfPossible(t, OtherException.class);
+   *   throw new RuntimeException("unexpected", t);
+   * }
+   * 
+ * + * @param throwable the Throwable to possibly propagate + * @param declaredType the single checked exception type declared by the calling method + */ + @J2ktIncompatible + @GwtIncompatible // propagateIfInstanceOf + public static void propagateIfPossible( + Throwable throwable, Class declaredType) throws X { + propagateIfInstanceOf(throwable, declaredType); + propagateIfPossible(throwable); + } + + /** + * Propagates {@code throwable} exactly as-is, if and only if it is an instance of {@link + * RuntimeException}, {@link Error}, {@code declaredType1}, or {@code declaredType2}. In the + * unlikely case that you have three or more declared checked exception types, you can handle them + * all by invoking these methods repeatedly. See usage example in {@link + * #propagateIfPossible(Throwable, Class)}. + * + * @param throwable the Throwable to possibly propagate + * @param declaredType1 any checked exception type declared by the calling method + * @param declaredType2 any other checked exception type declared by the calling method + */ + @J2ktIncompatible + @GwtIncompatible // propagateIfInstanceOf + public static void propagateIfPossible( + Throwable throwable, Class declaredType1, Class declaredType2) throws X1, X2 { + checkNotNull(declaredType2); + propagateIfInstanceOf(throwable, declaredType1); + propagateIfPossible(throwable, declaredType2); + } + + /** + * Propagates {@code throwable} as-is if it is an instance of {@link RuntimeException} or {@link + * Error}, or else as a last resort, wraps it in a {@code RuntimeException} and then propagates. + * + *

This method always throws an exception. The {@code RuntimeException} return type allows + * client code to signal to the compiler that statements after the call are unreachable. Example + * usage: + * + *

+   * T doSomething() {
+   *   try {
+   *     return someMethodThatCouldThrowAnything();
+   *   } catch (IKnowWhatToDoWithThisException e) {
+   *     return handle(e);
+   *   } catch (Throwable t) {
+   *     throw Throwables.propagate(t);
+   *   }
+   * }
+   * 
+ * + * @param throwable the Throwable to propagate + * @return nothing will ever be returned; this return type is only for your convenience, as + * illustrated in the example above + * @deprecated Use {@code throw e} or {@code throw new RuntimeException(e)} directly, or use a + * combination of {@link #throwIfUnchecked} and {@code throw new RuntimeException(e)}. For + * background on the deprecation, read Why we deprecated + * {@code Throwables.propagate}. + */ + @J2ktIncompatible + @GwtIncompatible + @Deprecated + public static RuntimeException propagate(Throwable throwable) { + throwIfUnchecked(throwable); + throw new RuntimeException(throwable); + } + + /** + * Returns the innermost cause of {@code throwable}. The first throwable in a chain provides + * context from when the error or exception was initially detected. Example usage: + * + *
+   * assertEquals("Unable to assign a customer id", Throwables.getRootCause(e).getMessage());
+   * 
+ * + * @throws IllegalArgumentException if there is a loop in the causal chain + */ + public static Throwable getRootCause(Throwable throwable) { + // Keep a second pointer that slowly walks the causal chain. If the fast pointer ever catches + // the slower pointer, then there's a loop. + Throwable slowPointer = throwable; + boolean advanceSlowPointer = false; + + Throwable cause; + while ((cause = throwable.getCause()) != null) { + throwable = cause; + + if (throwable == slowPointer) { + throw new IllegalArgumentException("Loop in causal chain detected.", throwable); + } + if (advanceSlowPointer) { + slowPointer = slowPointer.getCause(); + } + advanceSlowPointer = !advanceSlowPointer; // only advance every other iteration + } + return throwable; + } + + /** + * Gets a {@code Throwable} cause chain as a list. The first entry in the list will be {@code + * throwable} followed by its cause hierarchy. Note that this is a snapshot of the cause chain and + * will not reflect any subsequent changes to the cause chain. + * + *

Here's an example of how it can be used to find specific types of exceptions in the cause + * chain: + * + *

+   * Iterables.filter(Throwables.getCausalChain(e), IOException.class));
+   * 
+ * + * @param throwable the non-null {@code Throwable} to extract causes from + * @return an unmodifiable list containing the cause chain starting with {@code throwable} + * @throws IllegalArgumentException if there is a loop in the causal chain + */ + public static List getCausalChain(Throwable throwable) { + checkNotNull(throwable); + List causes = new ArrayList<>(4); + causes.add(throwable); + + // Keep a second pointer that slowly walks the causal chain. If the fast pointer ever catches + // the slower pointer, then there's a loop. + Throwable slowPointer = throwable; + boolean advanceSlowPointer = false; + + Throwable cause; + while ((cause = throwable.getCause()) != null) { + throwable = cause; + causes.add(throwable); + + if (throwable == slowPointer) { + throw new IllegalArgumentException("Loop in causal chain detected.", throwable); + } + if (advanceSlowPointer) { + slowPointer = slowPointer.getCause(); + } + advanceSlowPointer = !advanceSlowPointer; // only advance every other iteration + } + return Collections.unmodifiableList(causes); + } + + /** + * Returns {@code throwable}'s cause, cast to {@code expectedCauseType}. + * + *

Prefer this method instead of manually casting an exception's cause. For example, {@code + * (IOException) e.getCause()} throws a {@link ClassCastException} that discards the original + * exception {@code e} if the cause is not an {@link IOException}, but {@code + * Throwables.getCauseAs(e, IOException.class)} keeps {@code e} as the {@link + * ClassCastException}'s cause. + * + * @throws ClassCastException if the cause cannot be cast to the expected type. The {@code + * ClassCastException}'s cause is {@code throwable}. + * @since 22.0 + */ + @J2ktIncompatible + @GwtIncompatible // Class.cast(Object) + public static X getCauseAs( + Throwable throwable, Class expectedCauseType) { + try { + return expectedCauseType.cast(throwable.getCause()); + } catch (ClassCastException e) { + e.initCause(throwable); + throw e; + } + } + + /** + * Returns a string containing the result of {@link Throwable#toString() toString()}, followed by + * the full, recursive stack trace of {@code throwable}. Note that you probably should not be + * parsing the resulting string; if you need programmatic access to the stack frames, you can call + * {@link Throwable#getStackTrace()}. + */ + @GwtIncompatible // java.io.PrintWriter, java.io.StringWriter + public static String getStackTraceAsString(Throwable throwable) { + StringWriter stringWriter = new StringWriter(); + throwable.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } + + /** + * Returns the stack trace of {@code throwable}, possibly providing slower iteration over the full + * trace but faster iteration over parts of the trace. Here, "slower" and "faster" are defined in + * comparison to the normal way to access the stack trace, {@link Throwable#getStackTrace() + * throwable.getStackTrace()}. Note, however, that this method's special implementation is not + * available for all platforms and configurations. If that implementation is unavailable, this + * method falls back to {@code getStackTrace}. Callers that require the special implementation can + * check its availability with {@link #lazyStackTraceIsLazy()}. + * + *

The expected (but not guaranteed) performance of the special implementation differs from + * {@code getStackTrace} in one main way: The {@code lazyStackTrace} call itself returns quickly + * by delaying the per-stack-frame work until each element is accessed. Roughly speaking: + * + *

    + *
  • {@code getStackTrace} takes {@code stackSize} time to return but then negligible time to + * retrieve each element of the returned list. + *
  • {@code lazyStackTrace} takes negligible time to return but then {@code 1/stackSize} time + * to retrieve each element of the returned list (probably slightly more than {@code + * 1/stackSize}). + *
+ * + *

Note: The special implementation does not respect calls to {@link Throwable#setStackTrace + * throwable.setStackTrace}. Instead, it always reflects the original stack trace from the + * exception's creation. + * + * @since 19.0 + * @deprecated This method is equivalent to {@link Throwable#getStackTrace()} on JDK versions past + * JDK 8 and on all Android versions. Use {@link Throwable#getStackTrace()} directly, or where + * possible use the {@code java.lang.StackWalker.walk} method introduced in JDK 9. + */ + @Deprecated + @J2ktIncompatible + @GwtIncompatible // lazyStackTraceIsLazy, jlaStackTrace + public static List lazyStackTrace(Throwable throwable) { + return lazyStackTraceIsLazy() + ? jlaStackTrace(throwable) + : unmodifiableList(asList(throwable.getStackTrace())); + } + + /** + * Returns whether {@link #lazyStackTrace} will use the special implementation described in its + * documentation. + * + * @since 19.0 + * @deprecated This method always returns false on JDK versions past JDK 8 and on all Android + * versions. + */ + @Deprecated + @J2ktIncompatible + @GwtIncompatible // getStackTraceElementMethod + public static boolean lazyStackTraceIsLazy() { + return getStackTraceElementMethod != null && getStackTraceDepthMethod != null; + } + + @J2ktIncompatible + @GwtIncompatible // invokeAccessibleNonThrowingMethod + private static List jlaStackTrace(Throwable t) { + checkNotNull(t); + /* + * TODO(cpovirk): Consider optimizing iterator() to catch IOOBE instead of doing bounds checks. + * + * TODO(cpovirk): Consider the UnsignedBytes pattern if it performs faster and doesn't cause + * AOSP grief. + */ + return new AbstractList() { + /* + * The following requireNonNull calls are safe because we use jlaStackTrace() only if + * lazyStackTraceIsLazy() returns true. + */ + @Override + public StackTraceElement get(int n) { + return (StackTraceElement) + invokeAccessibleNonThrowingMethod( + requireNonNull(getStackTraceElementMethod), requireNonNull(jla), t, n); + } + + @Override + public int size() { + return (Integer) + invokeAccessibleNonThrowingMethod( + requireNonNull(getStackTraceDepthMethod), requireNonNull(jla), t); + } + }; + } + + @J2ktIncompatible + @GwtIncompatible // java.lang.reflect + private static Object invokeAccessibleNonThrowingMethod( + Method method, Object receiver, Object... params) { + try { + return method.invoke(receiver, params); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw propagate(e.getCause()); + } + } + + /** JavaLangAccess class name to load using reflection */ + @J2ktIncompatible @GwtIncompatible // not used by GWT emulation + private static final String JAVA_LANG_ACCESS_CLASSNAME = "sun.misc.JavaLangAccess"; + + /** SharedSecrets class name to load using reflection */ + @J2ktIncompatible + @GwtIncompatible // not used by GWT emulation + @VisibleForTesting + static final String SHARED_SECRETS_CLASSNAME = "sun.misc.SharedSecrets"; + + /** Access to some fancy internal JVM internals. */ + @J2ktIncompatible @GwtIncompatible // java.lang.reflect + private static final Object jla = getJLA(); + + /** + * The "getStackTraceElementMethod" method, only available on some JDKs so we use reflection to + * find it when available. When this is null, use the slow way. + */ + @J2ktIncompatible @GwtIncompatible // java.lang.reflect + private static final Method getStackTraceElementMethod = (jla == null) ? null : getGetMethod(); + + /** + * The "getStackTraceDepth" method, only available on some JDKs so we use reflection to find it + * when available. When this is null, use the slow way. + */ + @J2ktIncompatible @GwtIncompatible // java.lang.reflect + private static final Method getStackTraceDepthMethod = (jla == null) ? null : getSizeMethod(jla); + + /** + * Returns the JavaLangAccess class that is present in all Sun JDKs. It is not allowed in + * AppEngine, and not present in non-Sun JDKs. + */ + @J2ktIncompatible + @GwtIncompatible // java.lang.reflect + private static Object getJLA() { + try { + /* + * We load sun.misc.* classes using reflection since Android doesn't support these classes and + * would result in compilation failure if we directly refer to these classes. + */ + Class sharedSecrets = Class.forName(SHARED_SECRETS_CLASSNAME, false, null); + Method langAccess = sharedSecrets.getMethod("getJavaLangAccess"); + return langAccess.invoke(null); + } catch (ThreadDeath death) { + throw death; + } catch (Throwable t) { + /* + * This is not one of AppEngine's allowed classes, so even in Sun JDKs, this can fail with + * a NoClassDefFoundError. Other apps might deny access to sun.misc packages. + */ + return null; + } + } + + /** + * Returns the Method that can be used to resolve an individual StackTraceElement, or null if that + * method cannot be found (it is only to be found in fairly recent JDKs). + */ + @J2ktIncompatible + @GwtIncompatible // java.lang.reflect + private static Method getGetMethod() { + return getJlaMethod("getStackTraceElement", Throwable.class, int.class); + } + + /** + * Returns the Method that can be used to return the size of a stack, or null if that method + * cannot be found (it is only to be found in fairly recent JDKs). Tries to test method {@link + * sun.misc.JavaLangAccess#getStackTraceDepth(Throwable) getStackTraceDepth} prior to return it + * (might fail some JDKs). + * + *

See Throwables#lazyStackTrace throws + * UnsupportedOperationException. + */ + @J2ktIncompatible + @GwtIncompatible // java.lang.reflect + private static Method getSizeMethod(Object jla) { + try { + Method getStackTraceDepth = getJlaMethod("getStackTraceDepth", Throwable.class); + if (getStackTraceDepth == null) { + return null; + } + getStackTraceDepth.invoke(jla, new Throwable()); + return getStackTraceDepth; + } catch (UnsupportedOperationException | IllegalAccessException | InvocationTargetException e) { + return null; + } + } + + @J2ktIncompatible + @GwtIncompatible // java.lang.reflect + private static Method getJlaMethod(String name, Class... parameterTypes) throws ThreadDeath { + try { + return Class.forName(JAVA_LANG_ACCESS_CLASSNAME, false, null).getMethod(name, parameterTypes); + } catch (ThreadDeath death) { + throw death; + } catch (Throwable t) { + /* + * Either the JavaLangAccess class itself is not found, or the method is not supported on the + * JVM. + */ + return null; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Ticker.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Ticker.java new file mode 100644 index 0000000000000..a520d85415d6f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Ticker.java @@ -0,0 +1,61 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * A time source; returns a time value representing the number of nanoseconds elapsed since some + * fixed but arbitrary point in time. Note that most users should use {@link Stopwatch} instead of + * interacting with this class directly. + * + *

Warning: this interface can only be used to measure elapsed time, not wall time. + * + * @author Kevin Bourrillion + * @since 10.0 (mostly + * source-compatible since 9.0) + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class Ticker { + /** Constructor for use by subclasses. */ + protected Ticker() {} + + /** Returns the number of nanoseconds elapsed since this ticker's fixed point of reference. */ + public abstract long read(); + + /** + * A ticker that reads the current time using {@link System#nanoTime}. + * + * @since 10.0 + */ + public static Ticker systemTicker() { + return SYSTEM_TICKER; + } + + private static final Ticker SYSTEM_TICKER = + new Ticker() { + @Override + @SuppressWarnings("GoodTime") // reading system time without TimeSource + public long read() { + return System.nanoTime(); + } + }; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Utf8.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Utf8.java new file mode 100644 index 0000000000000..45ec31120dea8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Utf8.java @@ -0,0 +1,205 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static java.lang.Character.MAX_SURROGATE; +import static java.lang.Character.MIN_SURROGATE; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Low-level, high-performance utility methods related to the {@linkplain Charsets#UTF_8 UTF-8} + * character encoding. UTF-8 is defined in section D92 of The Unicode Standard Core + * Specification, Chapter 3. + * + *

The variant of UTF-8 implemented by this class is the restricted definition of UTF-8 + * introduced in Unicode 3.1. One implication of this is that it rejects "non-shortest form" byte sequences, + * even though the JDK decoder may accept them. + * + * @author Martin Buchholz + * @author Clément Roux + * @since 16.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Utf8 { + /** + * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string, this + * method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in both + * time and space. + * + * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired + * surrogates) + */ + public static int encodedLength(CharSequence sequence) { + // Warning to maintainers: this implementation is highly optimized. + int utf16Length = sequence.length(); + int utf8Length = utf16Length; + int i = 0; + + // This loop optimizes for pure ASCII. + while (i < utf16Length && sequence.charAt(i) < 0x80) { + i++; + } + + // This loop optimizes for chars less than 0x800. + for (; i < utf16Length; i++) { + char c = sequence.charAt(i); + if (c < 0x800) { + utf8Length += ((0x7f - c) >>> 31); // branch free! + } else { + utf8Length += encodedLengthGeneral(sequence, i); + break; + } + } + + if (utf8Length < utf16Length) { + // Necessary and sufficient condition for overflow because of maximum 3x expansion + throw new IllegalArgumentException( + "UTF-8 length does not fit in int: " + (utf8Length + (1L << 32))); + } + return utf8Length; + } + + private static int encodedLengthGeneral(CharSequence sequence, int start) { + int utf16Length = sequence.length(); + int utf8Length = 0; + for (int i = start; i < utf16Length; i++) { + char c = sequence.charAt(i); + if (c < 0x800) { + utf8Length += (0x7f - c) >>> 31; // branch free! + } else { + utf8Length += 2; + // jdk7+: if (Character.isSurrogate(c)) { + if (MIN_SURROGATE <= c && c <= MAX_SURROGATE) { + // Check that we have a well-formed surrogate pair. + if (Character.codePointAt(sequence, i) == c) { + throw new IllegalArgumentException(unpairedSurrogateMsg(i)); + } + i++; + } + } + } + return utf8Length; + } + + /** + * Returns {@code true} if {@code bytes} is a well-formed UTF-8 byte sequence according to + * Unicode 6.0. Note that this is a stronger criterion than simply whether the bytes can be + * decoded. For example, some versions of the JDK decoder will accept "non-shortest form" byte + * sequences, but encoding never reproduces these. Such byte sequences are not considered + * well-formed. + * + *

This method returns {@code true} if and only if {@code Arrays.equals(bytes, new + * String(bytes, UTF_8).getBytes(UTF_8))} does, but is more efficient in both time and space. + */ + public static boolean isWellFormed(byte[] bytes) { + return isWellFormed(bytes, 0, bytes.length); + } + + /** + * Returns whether the given byte array slice is a well-formed UTF-8 byte sequence, as defined by + * {@link #isWellFormed(byte[])}. Note that this can be false even when {@code + * isWellFormed(bytes)} is true. + * + * @param bytes the input buffer + * @param off the offset in the buffer of the first byte to read + * @param len the number of bytes to read from the buffer + */ + public static boolean isWellFormed(byte[] bytes, int off, int len) { + int end = off + len; + checkPositionIndexes(off, end, bytes.length); + // Look for the first non-ASCII character. + for (int i = off; i < end; i++) { + if (bytes[i] < 0) { + return isWellFormedSlowPath(bytes, i, end); + } + } + return true; + } + + private static boolean isWellFormedSlowPath(byte[] bytes, int off, int end) { + int index = off; + while (true) { + int byte1; + + // Optimize for interior runs of ASCII bytes. + do { + if (index >= end) { + return true; + } + } while ((byte1 = bytes[index++]) >= 0); + + if (byte1 < (byte) 0xE0) { + // Two-byte form. + if (index == end) { + return false; + } + // Simultaneously check for illegal trailing-byte in leading position + // and overlong 2-byte form. + if (byte1 < (byte) 0xC2 || bytes[index++] > (byte) 0xBF) { + return false; + } + } else if (byte1 < (byte) 0xF0) { + // Three-byte form. + if (index + 1 >= end) { + return false; + } + int byte2 = bytes[index++]; + if (byte2 > (byte) 0xBF + // Overlong? 5 most significant bits must not all be zero. + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // Check for illegal surrogate codepoints. + || (byte1 == (byte) 0xED && (byte) 0xA0 <= byte2) + // Third byte trailing-byte test. + || bytes[index++] > (byte) 0xBF) { + return false; + } + } else { + // Four-byte form. + if (index + 2 >= end) { + return false; + } + int byte2 = bytes[index++]; + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 + // || byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 + // || byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // Third byte trailing-byte test + || bytes[index++] > (byte) 0xBF + // Fourth byte trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return false; + } + } + } + } + + private static String unpairedSurrogateMsg(int i) { + return "Unpaired surrogate at index " + i; + } + + private Utf8() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Verify.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Verify.java new file mode 100644 index 0000000000000..bbde09ee36364 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/Verify.java @@ -0,0 +1,488 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static org.apache.iotdb.google.common.base.Strings.lenientFormat; + +/** + * Static convenience methods that serve the same purpose as Java language assertions, + * except that they are always enabled. These methods should be used instead of Java assertions + * whenever there is a chance the check may fail "in real life". Example: + * + *

{@code
+ * Bill bill = remoteService.getLastUnpaidBill();
+ *
+ * // In case bug 12345 happens again we'd rather just die
+ * Verify.verify(bill.status() == Status.UNPAID,
+ *     "Unexpected bill status: %s", bill.status());
+ * }
+ * + *

Comparison to alternatives

+ * + *

Note: In some cases the differences explained below can be subtle. When it's unclear + * which approach to use, don't worry too much about it; just pick something that seems + * reasonable and it will be fine. + * + *

    + *
  • If checking whether the caller has violated your method or constructor's contract + * (such as by passing an invalid argument), use the utilities of the {@link Preconditions} + * class instead. + *
  • If checking an impossible condition (which cannot happen unless your own + * class or its trusted dependencies is badly broken), this is what ordinary Java + * assertions are for. Note that assertions are not enabled by default; they are essentially + * considered "compiled comments." + *
  • An explicit {@code if/throw} (as illustrated below) is always acceptable; we still + * recommend using our {@link VerifyException} exception type. Throwing a plain {@link + * RuntimeException} is frowned upon. + *
  • Use of {@link java.util.Objects#requireNonNull(Object)} is generally discouraged, since + * {@link #verifyNotNull(Object)} and {@link Preconditions#checkNotNull(Object)} perform the + * same function with more clarity. + *
+ * + *

Warning about performance

+ * + *

Remember that parameter values for message construction must all be computed eagerly, and + * autoboxing and varargs array creation may happen as well, even when the verification succeeds and + * the message ends up unneeded. Performance-sensitive verification checks should continue to use + * usual form: + * + *

{@code
+ * Bill bill = remoteService.getLastUnpaidBill();
+ * if (bill.status() != Status.UNPAID) {
+ *   throw new VerifyException("Unexpected bill status: " + bill.status());
+ * }
+ * }
+ * + *

Only {@code %s} is supported

+ * + *

As with {@link Preconditions}, {@code Verify} uses {@link Strings#lenientFormat} to format + * error message template strings. This only supports the {@code "%s"} specifier, not the full range + * of {@link java.util.Formatter} specifiers. However, note that if the number of arguments does not + * match the number of occurrences of {@code "%s"} in the format string, {@code Verify} will still + * behave as expected, and will still include all argument values in the error message; the message + * will simply not be formatted exactly as intended. + * + *

More information

+ * + * See Conditional + * failures explained in the Guava User Guide for advice on when this class should be used. + * + * @since 17.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Verify { + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with no + * message otherwise. + * + * @throws VerifyException if {@code expression} is {@code false} + * @see Preconditions#checkState Preconditions.checkState() + */ + public static void verify(boolean expression) { + if (!expression) { + throw new VerifyException(); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws VerifyException if {@code expression} is {@code false} + * @see Preconditions#checkState Preconditions.checkState() + */ + public static void verify( + boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, char p1) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, int p1) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, long p1) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, Object p1) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, char p1, char p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, int p1, char p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, long p1, char p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, Object p1, char p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, char p1, int p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, int p1, int p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, long p1, int p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, Object p1, int p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, char p1, long p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, int p1, long p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, long p1, long p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, Object p1, long p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, char p1, Object p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, int p1, Object p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, long p1, Object p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify(boolean expression, String errorMessageTemplate, Object p1, Object p2) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify( + boolean expression, String errorMessageTemplate, Object p1, Object p2, Object p3) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2, p3)); + } + } + + /** + * Ensures that {@code expression} is {@code true}, throwing a {@code VerifyException} with a + * custom message otherwise. + * + *

See {@link #verify(boolean, String, Object...)} for details. + * + * @since 23.1 (varargs overload since 17.0) + */ + public static void verify( + boolean expression, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) { + if (!expression) { + throw new VerifyException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4)); + } + } + + /* + * For a discussion of the signature of verifyNotNull, see the discussion above + * Preconditions.checkNotNull. + * + * (verifyNotNull has many fewer "problem" callers, so we could try to be stricter. On the other + * hand, verifyNotNull arguably has more reason to accept nullable arguments in the first + * place....) + */ + + /** + * Ensures that {@code reference} is non-null, throwing a {@code VerifyException} with a default + * message otherwise. + * + * @return {@code reference}, guaranteed to be non-null, for convenience + * @throws VerifyException if {@code reference} is {@code null} + * @see Preconditions#checkNotNull Preconditions.checkNotNull() + */ + public static T verifyNotNull(T reference) { + return verifyNotNull(reference, "expected a non-null reference"); + } + + /** + * Ensures that {@code reference} is non-null, throwing a {@code VerifyException} with a custom + * message otherwise. + * + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in + * square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @return {@code reference}, guaranteed to be non-null, for convenience + * @throws VerifyException if {@code reference} is {@code null} + * @see Preconditions#checkNotNull Preconditions.checkNotNull() + */ + public static T verifyNotNull( + T reference, String errorMessageTemplate, Object... errorMessageArgs) { + if (reference == null) { + throw new VerifyException(lenientFormat(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + // TODO(kevinb): consider T verifySingleton(Iterable) to take over for + // Iterables.getOnlyElement() + + private Verify() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/VerifyException.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/VerifyException.java new file mode 100644 index 0000000000000..743a47a6e20d0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/VerifyException.java @@ -0,0 +1,61 @@ +/* + * 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.iotdb.google.common.base; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Exception thrown upon the failure of a verification check, + * including those performed by the convenience methods of the {@link Verify} class. + * + * @since 17.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public class VerifyException extends RuntimeException { + /** Constructs a {@code VerifyException} with no message. */ + public VerifyException() {} + + /** Constructs a {@code VerifyException} with the message {@code message}. */ + public VerifyException(String message) { + super(message); + } + + /** + * Constructs a {@code VerifyException} with the cause {@code cause} and a message that is {@code + * null} if {@code cause} is null, and {@code cause.toString()} otherwise. + * + * @since 19.0 + */ + public VerifyException(Throwable cause) { + super(cause); + } + + /** + * Constructs a {@code VerifyException} with the message {@code message} and the cause {@code + * cause}. + * + * @since 19.0 + */ + public VerifyException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/internal/Finalizer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/internal/Finalizer.java new file mode 100644 index 0000000000000..f023552bee237 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/internal/Finalizer.java @@ -0,0 +1,239 @@ +/* + * 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.iotdb.google.common.base.internal; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Thread that finalizes referents. All references should implement {@code + * org.apache.iotdb.google.common.base.FinalizableReference}. + * + *

While this class is public, we consider it to be *internal* and not part of our published API. + * It is public so we can access it reflectively across class loaders in secure environments. + * + *

This class can't depend on other Guava code. If we were to load this class in the same class + * loader as the rest of Guava, this thread would keep an indirect strong reference to the class + * loader and prevent it from being garbage collected. This poses a problem for environments where + * you want to throw away the class loader. For example, dynamically reloading a web application or + * unloading an OSGi bundle. + * + *

{@code org.apache.iotdb.google.common.base.FinalizableReferenceQueue} loads this class in its + * own class loader. That way, this class doesn't prevent the main class loader from getting garbage + * collected, and this class can detect when the main class loader has been garbage collected and + * stop itself. + */ +// no @ElementTypesAreNonNullByDefault for the reasons discussed above +public class Finalizer implements Runnable { + + private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); + + /** Name of FinalizableReference.class. */ + private static final String FINALIZABLE_REFERENCE = + "org.apache.iotdb.google.common.base.FinalizableReference"; + + /** + * Starts the Finalizer thread. FinalizableReferenceQueue calls this method reflectively. + * + * @param finalizableReferenceClass FinalizableReference.class. + * @param queue a reference queue that the thread will poll. + * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be queued + * either when the FinalizableReferenceQueue is no longer referenced anywhere, or when its + * close() method is called. + */ + public static void startFinalizer( + Class finalizableReferenceClass, + ReferenceQueue queue, + PhantomReference frqReference) { + /* + * We use FinalizableReference.class for two things: + * + * 1) To invoke FinalizableReference.finalizeReferent() + * + * 2) To detect when FinalizableReference's class loader has to be garbage collected, at which + * point, Finalizer can stop running + */ + if (!finalizableReferenceClass.getName().equals(FINALIZABLE_REFERENCE)) { + throw new IllegalArgumentException("Expected " + FINALIZABLE_REFERENCE + "."); + } + + Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); + String threadName = Finalizer.class.getName(); + Thread thread = null; + if (bigThreadConstructor != null) { + try { + boolean inheritThreadLocals = false; + long defaultStackSize = 0; + thread = + bigThreadConstructor.newInstance( + (ThreadGroup) null, finalizer, threadName, defaultStackSize, inheritThreadLocals); + } catch (Throwable t) { + logger.log( + Level.INFO, "Failed to create a thread without inherited thread-local values", t); + } + } + if (thread == null) { + thread = new Thread((ThreadGroup) null, finalizer, threadName); + } + thread.setDaemon(true); + + try { + if (inheritableThreadLocals != null) { + inheritableThreadLocals.set(thread, null); + } + } catch (Throwable t) { + logger.log( + Level.INFO, + "Failed to clear thread local values inherited by reference finalizer thread.", + t); + } + + thread.start(); + } + + private final WeakReference> finalizableReferenceClassReference; + private final PhantomReference frqReference; + private final ReferenceQueue queue; + + // By preference, we will use the Thread constructor that has an `inheritThreadLocals` parameter. + // But before Java 9, our only way not to inherit ThreadLocals is to zap them after the thread + // is created, by accessing a private field. + private static final Constructor bigThreadConstructor = getBigThreadConstructor(); + + private static final Field inheritableThreadLocals = + (bigThreadConstructor == null) ? getInheritableThreadLocalsField() : null; + + /** Constructs a new finalizer thread. */ + private Finalizer( + Class finalizableReferenceClass, + ReferenceQueue queue, + PhantomReference frqReference) { + this.queue = queue; + + this.finalizableReferenceClassReference = new WeakReference<>(finalizableReferenceClass); + + // Keep track of the FRQ that started us so we know when to stop. + this.frqReference = frqReference; + } + + /** Loops continuously, pulling references off the queue and cleaning them up. */ + @SuppressWarnings("InfiniteLoopStatement") + @Override + public void run() { + while (true) { + try { + if (!cleanUp(queue.remove())) { + break; + } + } catch (InterruptedException e) { + // ignore + } + } + } + + /** + * Cleans up a single reference. Catches and logs all throwables. + * + * @return true if the caller should continue, false if the associated FinalizableReferenceQueue + * is no longer referenced. + */ + private boolean cleanUp(Reference reference) { + Method finalizeReferentMethod = getFinalizeReferentMethod(); + if (finalizeReferentMethod == null) { + return false; + } + do { + /* + * This is for the benefit of phantom references. Weak and soft references will have already + * been cleared by this point. + */ + reference.clear(); + + if (reference == frqReference) { + /* + * The client no longer has a reference to the FinalizableReferenceQueue. We can stop. + */ + return false; + } + + try { + finalizeReferentMethod.invoke(reference); + } catch (Throwable t) { + logger.log(Level.SEVERE, "Error cleaning up after reference.", t); + } + + /* + * Loop as long as we have references available so as not to waste CPU looking up the Method + * over and over again. + */ + } while ((reference = queue.poll()) != null); + return true; + } + + /** Looks up FinalizableReference.finalizeReferent() method. */ + private Method getFinalizeReferentMethod() { + Class finalizableReferenceClass = finalizableReferenceClassReference.get(); + if (finalizableReferenceClass == null) { + /* + * FinalizableReference's class loader was reclaimed. While there's a chance that other + * finalizable references could be enqueued subsequently (at which point the class loader + * would be resurrected by virtue of us having a strong reference to it), we should pretty + * much just shut down and make sure we don't keep it alive any longer than necessary. + */ + return null; + } + try { + return finalizableReferenceClass.getMethod("finalizeReferent"); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + private static Field getInheritableThreadLocalsField() { + try { + Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); + inheritableThreadLocals.setAccessible(true); + return inheritableThreadLocals; + } catch (Throwable t) { + logger.log( + Level.INFO, + "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will " + + "inherit thread local values."); + return null; + } + } + + private static Constructor getBigThreadConstructor() { + try { + return Thread.class.getConstructor( + ThreadGroup.class, Runnable.class, String.class, long.class, boolean.class); + } catch (Throwable t) { + // Probably pre Java 9. We'll fall back to Thread.inheritableThreadLocals. + return null; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/package-info.java new file mode 100644 index 0000000000000..d47331f42cc90 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/base/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.base; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/AbstractCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/AbstractCache.java new file mode 100644 index 0000000000000..28ebeb37dd9f6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/AbstractCache.java @@ -0,0 +1,289 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Maps; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +/** + * This class provides a skeletal implementation of the {@code Cache} interface to minimize the + * effort required to implement this interface. + * + *

To implement a cache, the programmer needs only to extend this class and provide an + * implementation for the {@link #put} and {@link #getIfPresent} methods. {@link #getAllPresent} is + * implemented in terms of {@link #getIfPresent}; {@link #putAll} is implemented in terms of {@link + * #put}, {@link #invalidateAll(Iterable)} is implemented in terms of {@link #invalidate}. The + * method {@link #cleanUp} is a no-op. All other methods throw an {@link + * UnsupportedOperationException}. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractCache implements Cache { + + /** Constructor for use by subclasses. */ + protected AbstractCache() {} + + /** + * @since 11.0 + */ + @Override + public V get(K key, Callable valueLoader) throws ExecutionException { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + *

This implementation of {@code getAllPresent} lacks any insight into the internal cache data + * structure, and is thus forced to return the query keys instead of the cached keys. This is only + * possible with an unsafe cast which requires {@code keys} to actually be of type {@code K}. + * + * @since 11.0 + */ + /* + * is mostly the same as to plain Java. But to nullness checkers, they + * differ: means "non-null types," while means "all types." + */ + @Override + public ImmutableMap getAllPresent(Iterable keys) { + Map result = Maps.newLinkedHashMap(); + for (Object key : keys) { + if (!result.containsKey(key)) { + @SuppressWarnings("unchecked") + K castKey = (K) key; + V value = getIfPresent(key); + if (value != null) { + result.put(castKey, value); + } + } + } + return ImmutableMap.copyOf(result); + } + + /** + * @since 11.0 + */ + @Override + public void put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * @since 12.0 + */ + @Override + public void putAll(Map m) { + for (Entry entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void cleanUp() {} + + @Override + public long size() { + throw new UnsupportedOperationException(); + } + + @Override + public void invalidate(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * @since 11.0 + */ + @Override + // For discussion of , see getAllPresent. + public void invalidateAll(Iterable keys) { + for (Object key : keys) { + invalidate(key); + } + } + + @Override + public void invalidateAll() { + throw new UnsupportedOperationException(); + } + + @Override + public CacheStats stats() { + throw new UnsupportedOperationException(); + } + + @Override + public ConcurrentMap asMap() { + throw new UnsupportedOperationException(); + } + + /** + * Accumulates statistics during the operation of a {@link Cache} for presentation by {@link + * Cache#stats}. This is solely intended for consumption by {@code Cache} implementors. + * + * @since 10.0 + */ + public interface StatsCounter { + /** + * Records cache hits. This should be called when a cache request returns a cached value. + * + * @param count the number of hits to record + * @since 11.0 + */ + void recordHits(int count); + + /** + * Records cache misses. This should be called when a cache request returns a value that was not + * found in the cache. This method should be called by the loading thread, as well as by threads + * blocking on the load. Multiple concurrent calls to {@link Cache} lookup methods with the same + * key on an absent value should result in a single call to either {@code recordLoadSuccess} or + * {@code recordLoadException} and multiple calls to this method, despite all being served by + * the results of a single load operation. + * + * @param count the number of misses to record + * @since 11.0 + */ + void recordMisses(int count); + + /** + * Records the successful load of a new entry. This should be called when a cache request causes + * an entry to be loaded, and the loading completes successfully. In contrast to {@link + * #recordMisses}, this method should only be called by the loading thread. + * + * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new + * value + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + void recordLoadSuccess(long loadTime); + + /** + * Records the failed load of a new entry. This should be called when a cache request causes an + * entry to be loaded, but an exception is thrown while loading the entry. In contrast to {@link + * #recordMisses}, this method should only be called by the loading thread. + * + * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new + * value prior to an exception being thrown + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + void recordLoadException(long loadTime); + + /** + * Records the eviction of an entry from the cache. This should only been called when an entry + * is evicted due to the cache's eviction strategy, and not as a result of manual {@linkplain + * Cache#invalidate invalidations}. + */ + void recordEviction(); + + /** + * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as + * it may be interleaved with update operations. + */ + CacheStats snapshot(); + } + + /** + * A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors. + * + * @since 10.0 + */ + public static final class SimpleStatsCounter implements StatsCounter { + private final LongAddable hitCount = LongAddables.create(); + private final LongAddable missCount = LongAddables.create(); + private final LongAddable loadSuccessCount = LongAddables.create(); + private final LongAddable loadExceptionCount = LongAddables.create(); + private final LongAddable totalLoadTime = LongAddables.create(); + private final LongAddable evictionCount = LongAddables.create(); + + /** Constructs an instance with all counts initialized to zero. */ + public SimpleStatsCounter() {} + + /** + * @since 11.0 + */ + @Override + public void recordHits(int count) { + hitCount.add(count); + } + + /** + * @since 11.0 + */ + @Override + public void recordMisses(int count) { + missCount.add(count); + } + + @SuppressWarnings("GoodTime") // b/122668874 + @Override + public void recordLoadSuccess(long loadTime) { + loadSuccessCount.increment(); + totalLoadTime.add(loadTime); + } + + @SuppressWarnings("GoodTime") // b/122668874 + @Override + public void recordLoadException(long loadTime) { + loadExceptionCount.increment(); + totalLoadTime.add(loadTime); + } + + @Override + public void recordEviction() { + evictionCount.increment(); + } + + @Override + public CacheStats snapshot() { + return new CacheStats( + negativeToMaxValue(hitCount.sum()), + negativeToMaxValue(missCount.sum()), + negativeToMaxValue(loadSuccessCount.sum()), + negativeToMaxValue(loadExceptionCount.sum()), + negativeToMaxValue(totalLoadTime.sum()), + negativeToMaxValue(evictionCount.sum())); + } + + /** Returns {@code value}, if non-negative. Otherwise, returns {@link Long#MAX_VALUE}. */ + private static long negativeToMaxValue(long value) { + return (value >= 0) ? value : Long.MAX_VALUE; + } + + /** Increments all counters by the values in {@code other}. */ + public void incrementBy(StatsCounter other) { + CacheStats otherStats = other.snapshot(); + hitCount.add(otherStats.hitCount()); + missCount.add(otherStats.missCount()); + loadSuccessCount.add(otherStats.loadSuccessCount()); + loadExceptionCount.add(otherStats.loadExceptionCount()); + totalLoadTime.add(otherStats.totalLoadTime()); + evictionCount.add(otherStats.evictionCount()); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/AbstractLoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/AbstractLoadingCache.java new file mode 100644 index 0000000000000..85b16f520b7ab --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/AbstractLoadingCache.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.util.concurrent.UncheckedExecutionException; + +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +/** + * This class provides a skeletal implementation of the {@code Cache} interface to minimize the + * effort required to implement this interface. + * + *

To implement a cache, the programmer needs only to extend this class and provide an + * implementation for the {@link #get(Object)} and {@link #getIfPresent} methods. {@link + * #getUnchecked}, {@link #get(Object, Callable)}, and {@link #getAll} are implemented in terms of + * {@code get}; {@link #getAllPresent} is implemented in terms of {@code getIfPresent}; {@link + * #putAll} is implemented in terms of {@link #put}, {@link #invalidateAll(Iterable)} is implemented + * in terms of {@link #invalidate}. The method {@link #cleanUp} is a no-op. All other methods throw + * an {@link UnsupportedOperationException}. + * + * @author Charles Fry + * @since 11.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractLoadingCache extends AbstractCache + implements LoadingCache { + + /** Constructor for use by subclasses. */ + protected AbstractLoadingCache() {} + + // TODO(b/27479612): consider removing this? + @Override + public V getUnchecked(K key) { + try { + return get(key); + } catch (ExecutionException e) { + throw new UncheckedExecutionException(e.getCause()); + } + } + + @Override + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + Map result = Maps.newLinkedHashMap(); + for (K key : keys) { + if (!result.containsKey(key)) { + result.put(key, get(key)); + } + } + return ImmutableMap.copyOf(result); + } + + @Override + public final V apply(K key) { + return getUnchecked(key); + } + + @Override + public void refresh(K key) { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Cache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Cache.java new file mode 100644 index 0000000000000..a18a4a8569739 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Cache.java @@ -0,0 +1,184 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.util.concurrent.ExecutionError; +import org.apache.iotdb.google.common.util.concurrent.UncheckedExecutionException; + +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +/** + * A semi-persistent mapping from keys to values. Cache entries are manually added using {@link + * #get(Object, Callable)} or {@link #put(Object, Object)}, and are stored in the cache until either + * evicted or manually invalidated. The common way to build instances is using {@link CacheBuilder}. + * + *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + * + * @param the type of the cache's keys, which are not permitted to be null + * @param the type of the cache's values, which are not permitted to be null + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface Cache { + + /** + * Returns the value associated with {@code key} in this cache, or {@code null} if there is no + * cached value for {@code key}. + * + * @since 11.0 + */ + // TODO(b/27479612): consider removing this? + V getIfPresent(Object key); + + /** + * Returns the value associated with {@code key} in this cache, obtaining that value from {@code + * loader} if necessary. The method improves upon the conventional "if cached, return; otherwise + * create, cache and return" pattern. For further improvements, use {@link LoadingCache} and its + * {@link LoadingCache#get(Object) get(K)} method instead of this one. + * + *

Among the improvements that this method and {@code LoadingCache.get(K)} both provide are: + * + *

    + *
  • {@linkplain LoadingCache#get(Object) awaiting the result of a pending load} rather than + * starting a redundant one + *
  • eliminating the error-prone caching boilerplate + *
  • tracking load {@linkplain #stats statistics} + *
+ * + *

Among the further improvements that {@code LoadingCache} can provide but this method cannot: + * + *

    + *
  • consolidation of the loader logic to {@linkplain CacheBuilder#build(CacheLoader) a single + * authoritative location} + *
  • {@linkplain LoadingCache#refresh refreshing of entries}, including {@linkplain + * CacheBuilder#refreshAfterWrite automated refreshing} + *
  • {@linkplain LoadingCache#getAll bulk loading requests}, including {@linkplain + * CacheLoader#loadAll bulk loading implementations} + *
+ * + *

Warning: For any given key, every {@code loader} used with it should compute the same + * value. Otherwise, a call that passes one {@code loader} may return the result of another call + * with a differently behaving {@code loader}. For example, a call that requests a short timeout + * for an RPC may wait for a similar call that requests a long timeout, or a call by an + * unprivileged user may return a resource accessible only to a privileged user making a similar + * call. To prevent this problem, create a key object that includes all values that affect the + * result of the query. Or use {@code LoadingCache.get(K)}, which lacks the ability to refer to + * state other than that in the key. + * + *

Warning: as with {@link CacheLoader#load}, {@code loader} must not return + * {@code null}; it may either return a non-null value or throw an exception. + * + *

No observable state associated with this cache is modified until loading completes. + * + * @throws ExecutionException if a checked exception was thrown while loading the value + * @throws UncheckedExecutionException if an unchecked exception was thrown while loading the + * value + * @throws ExecutionError if an error was thrown while loading the value + * @since 11.0 + */ + // TODO(b/27479612): consider removing this + V get(K key, Callable loader) throws ExecutionException; + + /** + * Returns a map of the values associated with {@code keys} in this cache. The returned map will + * only contain entries which are already present in the cache. + * + * @since 11.0 + */ + /* + * is mostly the same as to plain Java. But to nullness checkers, they + * differ: means "non-null types," while means "all types." + */ + ImmutableMap getAllPresent(Iterable keys); + + /** + * Associates {@code value} with {@code key} in this cache. If the cache previously contained a + * value associated with {@code key}, the old value is replaced by {@code value}. + * + *

Prefer {@link #get(Object, Callable)} when using the conventional "if cached, return; + * otherwise create, cache and return" pattern. + * + * @since 11.0 + */ + void put(K key, V value); + + /** + * Copies all of the mappings from the specified map to the cache. The effect of this call is + * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key + * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined + * if the specified map is modified while the operation is in progress. + * + * @since 12.0 + */ + void putAll(Map m); + + /** Discards any cached value for key {@code key}. */ + void invalidate(Object key); + + /** + * Discards any cached values for keys {@code keys}. + * + * @since 11.0 + */ + // For discussion of , see getAllPresent. + void invalidateAll(Iterable keys); + + /** Discards all entries in the cache. */ + void invalidateAll(); + + /** Returns the approximate number of entries in this cache. */ + long size(); + + /** + * Returns a current snapshot of this cache's cumulative statistics, or a set of default values if + * the cache is not recording statistics. All statistics begin at zero and never decrease over the + * lifetime of the cache. + * + *

Warning: this cache may not be recording statistical data. For example, a cache + * created using {@link CacheBuilder} only does so if the {@link CacheBuilder#recordStats} method + * was called. If statistics are not being recorded, a {@code CacheStats} instance with zero for + * all values is returned. + */ + CacheStats stats(); + + /** + * Returns a view of the entries stored in this cache as a thread-safe map. Modifications made to + * the map directly affect the cache. + * + *

Iterators from the returned map are at least weakly consistent: they are safe for + * concurrent use, but if the cache is modified (including by eviction) after the iterator is + * created, it is undefined which of the changes (if any) will be reflected in that iterator. + */ + ConcurrentMap asMap(); + + /** + * Performs any pending maintenance operations needed by the cache. Exactly which activities are + * performed -- if any -- is implementation-dependent. + */ + void cleanUp(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheBuilder.java new file mode 100644 index 0000000000000..cd1857bb82fd5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheBuilder.java @@ -0,0 +1,1109 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Ascii; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.base.Suppliers; +import org.apache.iotdb.google.common.base.Ticker; +import org.apache.iotdb.google.common.cache.AbstractCache.SimpleStatsCounter; +import org.apache.iotdb.google.common.cache.AbstractCache.StatsCounter; +import org.apache.iotdb.google.common.cache.LocalCache.Strength; + +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ConcurrentModificationException; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * A builder of {@link LoadingCache} and {@link Cache} instances. + * + *

Prefer Caffeine over Guava's caching + * API

+ * + *

The successor to Guava's caching API is Caffeine. Its API is designed to make it a + * nearly drop-in replacement -- though it requires Java 8 APIs, is not available for Android or + * GWT/j2cl, and may have different + * (usually better) behavior when multiple threads attempt concurrent mutations. Its equivalent + * to {@code CacheBuilder} is its {@code + * Caffeine} class. Caffeine offers better performance, more features (including asynchronous + * loading), and fewer bugs. + * + *

Caffeine defines its own interfaces ({@code + * Cache}, {@code + * LoadingCache}, {@code + * CacheLoader}, etc.), so you can use Caffeine without needing to use any Guava types. + * Caffeine's types are better than Guava's, especially for their + * deep support for asynchronous operations. But if you want to migrate to Caffeine with minimal + * code changes, you can use its + * {@code CaffeinatedGuava} adapter class, which lets you build a Guava {@code Cache} or a Guava + * {@code LoadingCache} backed by a Guava {@code CacheLoader}. + * + *

Caffeine's API for asynchronous operations uses {@code CompletableFuture}: {@code + * AsyncLoadingCache.get} returns a {@code CompletableFuture}, and implementations of {@code + * AsyncCacheLoader.asyncLoad} must return a {@code CompletableFuture}. Users of Guava's {@link + * org.apache.iotdb.google.common.util.concurrent.ListenableFuture} can adapt between the two {@code + * Future} types by using {@code + * net.javacrumbs.futureconverter.java8guava.FutureConverter}. + * + *

More on {@code CacheBuilder}

+ * + * {@code CacheBuilder} builds caches with any combination of the following features: + * + *
    + *
  • automatic loading of entries into the cache + *
  • least-recently-used eviction when a maximum size is exceeded (note that the cache is + * divided into segments, each of which does LRU internally) + *
  • time-based expiration of entries, measured since last access or last write + *
  • keys automatically wrapped in {@code WeakReference} + *
  • values automatically wrapped in {@code WeakReference} or {@code SoftReference} + *
  • notification of evicted (or otherwise removed) entries + *
  • accumulation of cache access statistics + *
+ * + *

These features are all optional; caches can be created using all or none of them. By default, + * cache instances created by {@code CacheBuilder} will not perform any type of eviction. + * + *

Usage example: + * + *

{@code
+ * LoadingCache graphs = CacheBuilder.newBuilder()
+ *     .maximumSize(10000)
+ *     .expireAfterWrite(Duration.ofMinutes(10))
+ *     .removalListener(MY_LISTENER)
+ *     .build(
+ *         new CacheLoader() {
+ *           public Graph load(Key key) throws AnyException {
+ *             return createExpensiveGraph(key);
+ *           }
+ *         });
+ * }
+ * + *

Or equivalently, + * + *

{@code
+ * // In real life this would come from a command-line flag or config file
+ * String spec = "maximumSize=10000,expireAfterWrite=10m";
+ *
+ * LoadingCache graphs = CacheBuilder.from(spec)
+ *     .removalListener(MY_LISTENER)
+ *     .build(
+ *         new CacheLoader() {
+ *           public Graph load(Key key) throws AnyException {
+ *             return createExpensiveGraph(key);
+ *           }
+ *         });
+ * }
+ * + *

The returned cache implements all optional operations of the {@link LoadingCache} and {@link + * Cache} interfaces. The {@code asMap} view (and its collection views) have weakly consistent + * iterators. This means that they are safe for concurrent use, but if other threads modify the + * cache after the iterator is created, it is undefined which of these changes, if any, are + * reflected in that iterator. These iterators never throw {@link ConcurrentModificationException}. + * + *

Note: by default, the returned cache uses equality comparisons (the {@link + * Object#equals equals} method) to determine equality for keys or values. However, if {@link + * #weakKeys} was specified, the cache uses identity ({@code ==}) comparisons instead for keys. + * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the cache uses identity + * comparisons for values. + * + *

Entries are automatically evicted from the cache when any of {@link #maximumSize(long) + * maximumSize}, {@link #maximumWeight(long) maximumWeight}, {@link #expireAfterWrite + * expireAfterWrite}, {@link #expireAfterAccess expireAfterAccess}, {@link #weakKeys weakKeys}, + * {@link #weakValues weakValues}, or {@link #softValues softValues} are requested. + * + *

If {@link #maximumSize(long) maximumSize} or {@link #maximumWeight(long) maximumWeight} is + * requested entries may be evicted on each cache modification. + * + *

If {@link #expireAfterWrite expireAfterWrite} or {@link #expireAfterAccess expireAfterAccess} + * is requested entries may be evicted on each cache modification, on occasional cache accesses, or + * on calls to {@link Cache#cleanUp}. Expired entries may be counted by {@link Cache#size}, but will + * never be visible to read or write operations. + * + *

If {@link #weakKeys weakKeys}, {@link #weakValues weakValues}, or {@link #softValues + * softValues} are requested, it is possible for a key or value present in the cache to be reclaimed + * by the garbage collector. Entries with reclaimed keys or values may be removed from the cache on + * each cache modification, on occasional cache accesses, or on calls to {@link Cache#cleanUp}; such + * entries may be counted in {@link Cache#size}, but will never be visible to read or write + * operations. + * + *

Certain cache configurations will result in the accrual of periodic maintenance tasks which + * will be performed during write operations, or during occasional read operations in the absence of + * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but + * calling it should not be necessary with a high throughput cache. Only caches built with {@link + * #removalListener removalListener}, {@link #expireAfterWrite expireAfterWrite}, {@link + * #expireAfterAccess expireAfterAccess}, {@link #weakKeys weakKeys}, {@link #weakValues + * weakValues}, or {@link #softValues softValues} perform periodic maintenance. + * + *

The caches produced by {@code CacheBuilder} are serializable, and the deserialized caches + * retain all the configuration properties of the original cache. Note that the serialized form does + * not include cache contents, but only configuration. + * + *

See the Guava User Guide article on caching for a higher-level + * explanation. + * + * @param the most general key type this builder will be able to create caches for. This is + * normally {@code Object} unless it is constrained by using a method like {@code + * #removalListener}. Cache keys may not be null. + * @param the most general value type this builder will be able to create caches for. This is + * normally {@code Object} unless it is constrained by using a method like {@code + * #removalListener}. Cache values may not be null. + * @author Charles Fry + * @author Kevin Bourrillion + * @since 10.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class CacheBuilder { + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_CONCURRENCY_LEVEL = 4; + + @SuppressWarnings("GoodTime") // should be a java.time.Duration + private static final int DEFAULT_EXPIRATION_NANOS = 0; + + @SuppressWarnings("GoodTime") // should be a java.time.Duration + private static final int DEFAULT_REFRESH_NANOS = 0; + + static final Supplier NULL_STATS_COUNTER = + Suppliers.ofInstance( + new StatsCounter() { + @Override + public void recordHits(int count) {} + + @Override + public void recordMisses(int count) {} + + @SuppressWarnings("GoodTime") // b/122668874 + @Override + public void recordLoadSuccess(long loadTime) {} + + @SuppressWarnings("GoodTime") // b/122668874 + @Override + public void recordLoadException(long loadTime) {} + + @Override + public void recordEviction() {} + + @Override + public CacheStats snapshot() { + return EMPTY_STATS; + } + }); + static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0); + + /* + * We avoid using a method reference or lambda here for now: + * + * - method reference: Inside Google, CacheBuilder is used from the implementation of a custom + * ClassLoader that is sometimes used as a system classloader. That's a problem because + * method-reference linking tries to look up the system classloader, and it fails because there + * isn't one yet. + * + * - lambda: Outside Google, we got a report of a similar problem in + * https://github.com/google/guava/issues/6565 + */ + @SuppressWarnings("AnonymousToLambda") + static final Supplier CACHE_STATS_COUNTER = + new Supplier() { + @Override + public StatsCounter get() { + return new SimpleStatsCounter(); + } + }; + + enum NullListener implements RemovalListener { + INSTANCE; + + @Override + public void onRemoval(RemovalNotification notification) {} + } + + enum OneWeigher implements Weigher { + INSTANCE; + + @Override + public int weigh(Object key, Object value) { + return 1; + } + } + + static final Ticker NULL_TICKER = + new Ticker() { + @Override + public long read() { + return 0; + } + }; + + // We use a holder class to delay initialization: https://github.com/google/guava/issues/6566 + private static final class LoggerHolder { + static final Logger logger = Logger.getLogger(CacheBuilder.class.getName()); + } + + static final int UNSET_INT = -1; + + boolean strictParsing = true; + + int initialCapacity = UNSET_INT; + int concurrencyLevel = UNSET_INT; + long maximumSize = UNSET_INT; + long maximumWeight = UNSET_INT; + Weigher weigher; + + Strength keyStrength; + Strength valueStrength; + + @SuppressWarnings("GoodTime") // should be a java.time.Duration + long expireAfterWriteNanos = UNSET_INT; + + @SuppressWarnings("GoodTime") // should be a java.time.Duration + long expireAfterAccessNanos = UNSET_INT; + + @SuppressWarnings("GoodTime") // should be a java.time.Duration + long refreshNanos = UNSET_INT; + + Equivalence keyEquivalence; + Equivalence valueEquivalence; + + RemovalListener removalListener; + Ticker ticker; + + Supplier statsCounterSupplier = NULL_STATS_COUNTER; + + private CacheBuilder() {} + + /** + * Constructs a new {@code CacheBuilder} instance with default settings, including strong keys, + * strong values, and no automatic eviction of any kind. + * + *

Note that while this return type is {@code CacheBuilder}, type parameters on + * the {@link #build} methods allow you to create a cache of any key and value type desired. + */ + public static CacheBuilder newBuilder() { + return new CacheBuilder<>(); + } + + /** + * Constructs a new {@code CacheBuilder} instance with the settings specified in {@code spec}. + * + * @since 12.0 + */ + @GwtIncompatible // To be supported + public static CacheBuilder from(CacheBuilderSpec spec) { + return spec.toCacheBuilder().lenientParsing(); + } + + /** + * Constructs a new {@code CacheBuilder} instance with the settings specified in {@code spec}. + * This is especially useful for command-line configuration of a {@code CacheBuilder}. + * + * @param spec a String in the format specified by {@link CacheBuilderSpec} + * @since 12.0 + */ + @GwtIncompatible // To be supported + public static CacheBuilder from(String spec) { + return from(CacheBuilderSpec.parse(spec)); + } + + /** + * Enables lenient parsing. Useful for tests and spec parsing. + * + * @return this {@code CacheBuilder} instance (for chaining) + */ + @GwtIncompatible // To be supported + CacheBuilder lenientParsing() { + strictParsing = false; + return this; + } + + /** + * Sets a custom {@code Equivalence} strategy for comparing keys. + * + *

By default, the cache uses {@link Equivalence#identity} to determine key equality when + * {@link #weakKeys} is specified, and {@link Equivalence#equals()} otherwise. + * + * @return this {@code CacheBuilder} instance (for chaining) + */ + @GwtIncompatible // To be supported + CacheBuilder keyEquivalence(Equivalence equivalence) { + checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence); + keyEquivalence = checkNotNull(equivalence); + return this; + } + + Equivalence getKeyEquivalence() { + return MoreObjects.firstNonNull(keyEquivalence, getKeyStrength().defaultEquivalence()); + } + + /** + * Sets a custom {@code Equivalence} strategy for comparing values. + * + *

By default, the cache uses {@link Equivalence#identity} to determine value equality when + * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalence#equals()} + * otherwise. + * + * @return this {@code CacheBuilder} instance (for chaining) + */ + @GwtIncompatible // To be supported + CacheBuilder valueEquivalence(Equivalence equivalence) { + checkState( + valueEquivalence == null, "value equivalence was already set to %s", valueEquivalence); + this.valueEquivalence = checkNotNull(equivalence); + return this; + } + + Equivalence getValueEquivalence() { + return MoreObjects.firstNonNull(valueEquivalence, getValueStrength().defaultEquivalence()); + } + + /** + * Sets the minimum total size for the internal hash tables. For example, if the initial capacity + * is {@code 60}, and the concurrency level is {@code 8}, then eight segments are created, each + * having a hash table of size eight. Providing a large enough estimate at construction time + * avoids the need for expensive resizing operations later, but setting this value unnecessarily + * high wastes memory. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code initialCapacity} is negative + * @throws IllegalStateException if an initial capacity was already set + */ + public CacheBuilder initialCapacity(int initialCapacity) { + checkState( + this.initialCapacity == UNSET_INT, + "initial capacity was already set to %s", + this.initialCapacity); + checkArgument(initialCapacity >= 0); + this.initialCapacity = initialCapacity; + return this; + } + + int getInitialCapacity() { + return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity; + } + + /** + * Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The + * table is internally partitioned to try to permit the indicated number of concurrent updates + * without contention. Because assignment of entries to these partitions is not necessarily + * uniform, the actual concurrency observed may vary. Ideally, you should choose a value to + * accommodate as many threads as will ever concurrently modify the table. Using a significantly + * higher value than you need can waste space and time, and a significantly lower value can lead + * to thread contention. But overestimates and underestimates within an order of magnitude do not + * usually have much noticeable impact. A value of one permits only one thread to modify the cache + * at a time, but since read operations and cache loading computations can proceed concurrently, + * this still yields higher concurrency than full synchronization. + * + *

Defaults to 4. Note:The default may change in the future. If you care about this + * value, you should always choose it explicitly. + * + *

The current implementation uses the concurrency level to create a fixed number of hashtable + * segments, each governed by its own write lock. The segment lock is taken once for each explicit + * write, and twice for each cache loading computation (once prior to loading the new value, and + * once after loading completes). Much internal cache management is performed at the segment + * granularity. For example, access queues and write queues are kept per segment when they are + * required by the selected eviction algorithm. As such, when writing unit tests it is not + * uncommon to specify {@code concurrencyLevel(1)} in order to achieve more deterministic eviction + * behavior. + * + *

Note that future implementations may abandon segment locking in favor of more advanced + * concurrency controls. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive + * @throws IllegalStateException if a concurrency level was already set + */ + public CacheBuilder concurrencyLevel(int concurrencyLevel) { + checkState( + this.concurrencyLevel == UNSET_INT, + "concurrency level was already set to %s", + this.concurrencyLevel); + checkArgument(concurrencyLevel > 0); + this.concurrencyLevel = concurrencyLevel; + return this; + } + + int getConcurrencyLevel() { + return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel; + } + + /** + * Specifies the maximum number of entries the cache may contain. + * + *

Note that the cache may evict an entry before this limit is exceeded. For example, in + * the current implementation, when {@code concurrencyLevel} is greater than {@code 1}, each + * resulting segment inside the cache independently limits its own size to approximately + * {@code maximumSize / concurrencyLevel}. + * + *

When eviction is necessary, the cache evicts entries that are less likely to be used again. + * For example, the cache may evict an entry because it hasn't been used recently or very often. + * + *

If {@code maximumSize} is zero, elements will be evicted immediately after being loaded into + * cache. This can be useful in testing, or to disable caching temporarily. + * + *

This feature cannot be used in conjunction with {@link #maximumWeight}. + * + * @param maximumSize the maximum size of the cache + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code maximumSize} is negative + * @throws IllegalStateException if a maximum size or weight was already set + */ + public CacheBuilder maximumSize(long maximumSize) { + checkState( + this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); + checkState( + this.maximumWeight == UNSET_INT, + "maximum weight was already set to %s", + this.maximumWeight); + checkState(this.weigher == null, "maximum size can not be combined with weigher"); + checkArgument(maximumSize >= 0, "maximum size must not be negative"); + this.maximumSize = maximumSize; + return this; + } + + /** + * Specifies the maximum weight of entries the cache may contain. Weight is determined using the + * {@link Weigher} specified with {@link #weigher}, and use of this method requires a + * corresponding call to {@link #weigher} prior to calling {@link #build}. + * + *

Note that the cache may evict an entry before this limit is exceeded. For example, in + * the current implementation, when {@code concurrencyLevel} is greater than {@code 1}, each + * resulting segment inside the cache independently limits its own weight to approximately + * {@code maximumWeight / concurrencyLevel}. + * + *

When eviction is necessary, the cache evicts entries that are less likely to be used again. + * For example, the cache may evict an entry because it hasn't been used recently or very often. + * + *

If {@code maximumWeight} is zero, elements will be evicted immediately after being loaded + * into cache. This can be useful in testing, or to disable caching temporarily. + * + *

Note that weight is only used to determine whether the cache is over capacity; it has no + * effect on selecting which entry should be evicted next. + * + *

This feature cannot be used in conjunction with {@link #maximumSize}. + * + * @param maximumWeight the maximum total weight of entries the cache may contain + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code maximumWeight} is negative + * @throws IllegalStateException if a maximum weight or size was already set + * @since 11.0 + */ + @GwtIncompatible // To be supported + public CacheBuilder maximumWeight(long maximumWeight) { + checkState( + this.maximumWeight == UNSET_INT, + "maximum weight was already set to %s", + this.maximumWeight); + checkState( + this.maximumSize == UNSET_INT, "maximum size was already set to %s", this.maximumSize); + checkArgument(maximumWeight >= 0, "maximum weight must not be negative"); + this.maximumWeight = maximumWeight; + return this; + } + + /** + * Specifies the weigher to use in determining the weight of entries. Entry weight is taken into + * consideration by {@link #maximumWeight(long)} when determining which entries to evict, and use + * of this method requires a corresponding call to {@link #maximumWeight(long)} prior to calling + * {@link #build}. Weights are measured and recorded when entries are inserted into the cache, and + * are thus effectively static during the lifetime of a cache entry. + * + *

When the weight of an entry is zero it will not be considered for size-based eviction + * (though it still may be evicted by other means). + * + *

Important note: Instead of returning this as a {@code CacheBuilder} + * instance, this method returns {@code CacheBuilder}. From this point on, either the + * original reference or the returned reference may be used to complete configuration and build + * the cache, but only the "generic" one is type-safe. That is, it will properly prevent you from + * building caches whose key or value types are incompatible with the types accepted by the + * weigher already provided; the {@code CacheBuilder} type cannot do this. For best results, + * simply use the standard method-chaining idiom, as illustrated in the documentation at top, + * configuring a {@code CacheBuilder} and building your {@link Cache} all in a single statement. + * + *

Warning: if you ignore the above advice, and use this {@code CacheBuilder} to build a + * cache whose key or value type is incompatible with the weigher, you will likely experience a + * {@link ClassCastException} at some undefined point in the future. + * + * @param weigher the weigher to use in calculating the weight of cache entries + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code size} is negative + * @throws IllegalStateException if a maximum size was already set + * @since 11.0 + */ + @GwtIncompatible // To be supported + // TODO(b/27479612): consider removing this + public CacheBuilder weigher( + Weigher weigher) { + checkState(this.weigher == null); + if (strictParsing) { + checkState( + this.maximumSize == UNSET_INT, + "weigher can not be combined with maximum size (%s provided)", + this.maximumSize); + } + + // safely limiting the kinds of caches this can produce + @SuppressWarnings("unchecked") + CacheBuilder me = (CacheBuilder) this; + me.weigher = checkNotNull(weigher); + return me; + } + + long getMaximumWeight() { + if (expireAfterWriteNanos == 0 || expireAfterAccessNanos == 0) { + return 0; + } + return (weigher == null) ? maximumSize : maximumWeight; + } + + // Make a safe contravariant cast now so we don't have to do it over and over. + @SuppressWarnings("unchecked") + Weigher getWeigher() { + return (Weigher) MoreObjects.firstNonNull(weigher, OneWeigher.INSTANCE); + } + + /** + * Specifies that each key (not value) stored in the cache should be wrapped in a {@link + * WeakReference} (by default, strong references are used). + * + *

Warning: when this method is used, the resulting cache will use identity ({@code ==}) + * comparison to determine equality of keys. Its {@link Cache#asMap} view will therefore + * technically violate the {@link Map} specification (in the same way that {@link IdentityHashMap} + * does). + * + *

Entries with keys that have been garbage collected may be counted in {@link Cache#size}, but + * will never be visible to read or write operations; such entries are cleaned up as part of the + * routine maintenance described in the class javadoc. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalStateException if the key strength was already set + */ + @GwtIncompatible // java.lang.ref.WeakReference + public CacheBuilder weakKeys() { + return setKeyStrength(Strength.WEAK); + } + + CacheBuilder setKeyStrength(Strength strength) { + checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); + keyStrength = checkNotNull(strength); + return this; + } + + Strength getKeyStrength() { + return MoreObjects.firstNonNull(keyStrength, Strength.STRONG); + } + + /** + * Specifies that each value (not key) stored in the cache should be wrapped in a {@link + * WeakReference} (by default, strong references are used). + * + *

Weak values will be garbage collected once they are weakly reachable. This makes them a poor + * candidate for caching; consider {@link #softValues} instead. + * + *

Note: when this method is used, the resulting cache will use identity ({@code ==}) + * comparison to determine equality of values. + * + *

Entries with values that have been garbage collected may be counted in {@link Cache#size}, + * but will never be visible to read or write operations; such entries are cleaned up as part of + * the routine maintenance described in the class javadoc. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalStateException if the value strength was already set + */ + @GwtIncompatible // java.lang.ref.WeakReference + public CacheBuilder weakValues() { + return setValueStrength(Strength.WEAK); + } + + /** + * Specifies that each value (not key) stored in the cache should be wrapped in a {@link + * SoftReference} (by default, strong references are used). Softly-referenced objects will be + * garbage-collected in a globally least-recently-used manner, in response to memory + * demand. + * + *

Warning: in most circumstances it is better to set a per-cache {@linkplain + * #maximumSize(long) maximum size} instead of using soft references. You should only use this + * method if you are well familiar with the practical consequences of soft references. + * + *

Note: when this method is used, the resulting cache will use identity ({@code ==}) + * comparison to determine equality of values. + * + *

Entries with values that have been garbage collected may be counted in {@link Cache#size}, + * but will never be visible to read or write operations; such entries are cleaned up as part of + * the routine maintenance described in the class javadoc. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalStateException if the value strength was already set + */ + @GwtIncompatible // java.lang.ref.SoftReference + public CacheBuilder softValues() { + return setValueStrength(Strength.SOFT); + } + + CacheBuilder setValueStrength(Strength strength) { + checkState(valueStrength == null, "Value strength was already set to %s", valueStrength); + valueStrength = checkNotNull(strength); + return this; + } + + Strength getValueStrength() { + return MoreObjects.firstNonNull(valueStrength, Strength.STRONG); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterWrite} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since 25.0 + */ + @GwtIncompatible // java.time.Duration + @SuppressWarnings("GoodTime") // java.time.Duration decomposition + public CacheBuilder expireAfterWrite(java.time.Duration duration) { + return expireAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, or the most recent replacement of its value. + * + *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred + * when feasible), use {@link #expireAfterWrite(Duration)} instead. + * + * @param duration the length of time after an entry is created that it should be automatically + * removed + * @param unit the unit that {@code duration} is expressed in + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterWrite} was already set + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public CacheBuilder expireAfterWrite(long duration, TimeUnit unit) { + checkState( + expireAfterWriteNanos == UNSET_INT, + "expireAfterWrite was already set to %s ns", + expireAfterWriteNanos); + checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterWriteNanos = unit.toNanos(duration); + return this; + } + + @SuppressWarnings("GoodTime") // nanos internally, should be Duration + long getExpireAfterWriteNanos() { + return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos; + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by {@code + * containsKey(Object)}, nor by operations on the collection-views of {@link Cache#asMap}}. So, + * for example, iterating through {@code Cache.asMap().entrySet()} does not reset access time for + * the entries you retrieve. + * + *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterAccess} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since 25.0 + */ + @GwtIncompatible // java.time.Duration + @SuppressWarnings("GoodTime") // java.time.Duration decomposition + public CacheBuilder expireAfterAccess(java.time.Duration duration) { + return expireAfterAccess(toNanosSaturated(duration), TimeUnit.NANOSECONDS); + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including {@code + * Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by {@code + * containsKey(Object)}, nor by operations on the collection-views of {@link Cache#asMap}. So, for + * example, iterating through {@code Cache.asMap().entrySet()} does not reset access time for the + * entries you retrieve. + * + *

When {@code duration} is zero, this method hands off to {@link #maximumSize(long) + * maximumSize}{@code (0)}, ignoring any otherwise-specified maximum size or weight. This can be + * useful in testing, or to disable caching temporarily without a code change. + * + *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred + * when feasible), use {@link #expireAfterAccess(Duration)} instead. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @param unit the unit that {@code duration} is expressed in + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #expireAfterAccess} was already set + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { + checkState( + expireAfterAccessNanos == UNSET_INT, + "expireAfterAccess was already set to %s ns", + expireAfterAccessNanos); + checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterAccessNanos = unit.toNanos(duration); + return this; + } + + @SuppressWarnings("GoodTime") // nanos internally, should be Duration + long getExpireAfterAccessNanos() { + return (expireAfterAccessNanos == UNSET_INT) + ? DEFAULT_EXPIRATION_NANOS + : expireAfterAccessNanos; + } + + /** + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. The semantics + * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link + * CacheLoader#reload}. + * + *

As the default implementation of {@link CacheLoader#reload} is synchronous, it is + * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous + * implementation; otherwise refreshes will be performed during unrelated cache read and write + * operations. + * + *

Currently automatic refreshes are performed when the first stale request for an entry + * occurs. The request triggering refresh will make a synchronous call to {@link + * CacheLoader#reload} to obtain a future of the new value. If the returned future is already + * complete, it is returned immediately. Otherwise, the old value is returned. + * + *

Note: all exceptions thrown during refresh will be logged and then swallowed. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #refreshAfterWrite} was already set + * @throws ArithmeticException for durations greater than +/- approximately 292 years + * @since 25.0 + */ + @GwtIncompatible // java.time.Duration + @SuppressWarnings("GoodTime") // java.time.Duration decomposition + public CacheBuilder refreshAfterWrite(java.time.Duration duration) { + return refreshAfterWrite(toNanosSaturated(duration), TimeUnit.NANOSECONDS); + } + + /** + * Specifies that active entries are eligible for automatic refresh once a fixed duration has + * elapsed after the entry's creation, or the most recent replacement of its value. The semantics + * of refreshes are specified in {@link LoadingCache#refresh}, and are performed by calling {@link + * CacheLoader#reload}. + * + *

As the default implementation of {@link CacheLoader#reload} is synchronous, it is + * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous + * implementation; otherwise refreshes will be performed during unrelated cache read and write + * operations. + * + *

Currently automatic refreshes are performed when the first stale request for an entry + * occurs. The request triggering refresh will make a synchronous call to {@link + * CacheLoader#reload} and immediately return the new value if the returned future is complete, + * and the old value otherwise. + * + *

Note: all exceptions thrown during refresh will be logged and then swallowed. + * + *

If you can represent the duration as a {@link java.time.Duration} (which should be preferred + * when feasible), use {@link #refreshAfterWrite(Duration)} instead. + * + * @param duration the length of time after an entry is created that it should be considered + * stale, and thus eligible for refresh + * @param unit the unit that {@code duration} is expressed in + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if {@link #refreshAfterWrite} was already set + * @since 11.0 + */ + @GwtIncompatible // To be supported (synchronously). + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public CacheBuilder refreshAfterWrite(long duration, TimeUnit unit) { + checkNotNull(unit); + checkState(refreshNanos == UNSET_INT, "refresh was already set to %s ns", refreshNanos); + checkArgument(duration > 0, "duration must be positive: %s %s", duration, unit); + this.refreshNanos = unit.toNanos(duration); + return this; + } + + @SuppressWarnings("GoodTime") // nanos internally, should be Duration + long getRefreshNanos() { + return (refreshNanos == UNSET_INT) ? DEFAULT_REFRESH_NANOS : refreshNanos; + } + + /** + * Specifies a nanosecond-precision time source for this cache. By default, {@link + * System#nanoTime} is used. + * + *

The primary intent of this method is to facilitate testing of caches with a fake or mock + * time source. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalStateException if a ticker was already set + */ + public CacheBuilder ticker(Ticker ticker) { + checkState(this.ticker == null); + this.ticker = checkNotNull(ticker); + return this; + } + + Ticker getTicker(boolean recordsTime) { + if (ticker != null) { + return ticker; + } + return recordsTime ? Ticker.systemTicker() : NULL_TICKER; + } + + /** + * Specifies a listener instance that caches should notify each time an entry is removed for any + * {@linkplain RemovalCause reason}. Each cache created by this builder will invoke this listener + * as part of the routine maintenance described in the class documentation above. + * + *

Warning: after invoking this method, do not continue to use this cache builder + * reference; instead use the reference this method returns. At runtime, these point to the + * same instance, but only the returned reference has the correct generic type information to + * ensure type safety. For best results, use the standard method-chaining idiom illustrated in the + * class documentation above, configuring a builder and building your cache in a single statement. + * Failure to heed this advice can result in a {@link ClassCastException} being thrown by a cache + * operation at some undefined point in the future. + * + *

Warning: any exception thrown by {@code listener} will not be propagated to + * the {@code Cache} user, only logged via a {@link Logger}. + * + * @return the cache builder reference that should be used instead of {@code this} for any + * remaining configuration and cache building + * @return this {@code CacheBuilder} instance (for chaining) + * @throws IllegalStateException if a removal listener was already set + */ + public CacheBuilder removalListener( + RemovalListener listener) { + checkState(this.removalListener == null); + + // safely limiting the kinds of caches this can produce + @SuppressWarnings("unchecked") + CacheBuilder me = (CacheBuilder) this; + me.removalListener = checkNotNull(listener); + return me; + } + + // Make a safe contravariant cast now so we don't have to do it over and over. + @SuppressWarnings("unchecked") + RemovalListener getRemovalListener() { + return (RemovalListener) + MoreObjects.firstNonNull(removalListener, NullListener.INSTANCE); + } + + /** + * Enable the accumulation of {@link CacheStats} during the operation of the cache. Without this + * {@link Cache#stats} will return zero for all statistics. Note that recording stats requires + * bookkeeping to be performed with each operation, and thus imposes a performance penalty on + * cache operation. + * + * @return this {@code CacheBuilder} instance (for chaining) + * @since 12.0 (previously, stats collection was automatic) + */ + public CacheBuilder recordStats() { + statsCounterSupplier = CACHE_STATS_COUNTER; + return this; + } + + boolean isRecordingStats() { + return statsCounterSupplier == CACHE_STATS_COUNTER; + } + + Supplier getStatsCounterSupplier() { + return statsCounterSupplier; + } + + /** + * Builds a cache, which either returns an already-loaded value for a given key or atomically + * computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently + * loading the value for this key, simply waits for that thread to finish and returns its loaded + * value. Note that multiple threads can concurrently load values for distinct keys. + * + *

This method does not alter the state of this {@code CacheBuilder} instance, so it can be + * invoked again to create multiple independent caches. + * + * @param loader the cache loader used to obtain new values + * @return a cache having the requested features + */ + public LoadingCache build( + CacheLoader loader) { + checkWeightWithWeigher(); + return new LocalCache.LocalLoadingCache<>(this, loader); + } + + /** + * Builds a cache which does not automatically load values when keys are requested. + * + *

Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a {@code + * CacheLoader}. + * + *

This method does not alter the state of this {@code CacheBuilder} instance, so it can be + * invoked again to create multiple independent caches. + * + * @return a cache having the requested features + * @since 11.0 + */ + public Cache build() { + checkWeightWithWeigher(); + checkNonLoadingCache(); + return new LocalCache.LocalManualCache<>(this); + } + + private void checkNonLoadingCache() { + checkState(refreshNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache"); + } + + private void checkWeightWithWeigher() { + if (weigher == null) { + checkState(maximumWeight == UNSET_INT, "maximumWeight requires weigher"); + } else { + if (strictParsing) { + checkState(maximumWeight != UNSET_INT, "weigher requires maximumWeight"); + } else { + if (maximumWeight == UNSET_INT) { + LoggerHolder.logger.log( + Level.WARNING, "ignoring weigher specified without maximumWeight"); + } + } + } + } + + /** + * Returns a string representation for this CacheBuilder instance. The exact form of the returned + * string is not specified. + */ + @Override + public String toString() { + MoreObjects.ToStringHelper s = MoreObjects.toStringHelper(this); + if (initialCapacity != UNSET_INT) { + s.add("initialCapacity", initialCapacity); + } + if (concurrencyLevel != UNSET_INT) { + s.add("concurrencyLevel", concurrencyLevel); + } + if (maximumSize != UNSET_INT) { + s.add("maximumSize", maximumSize); + } + if (maximumWeight != UNSET_INT) { + s.add("maximumWeight", maximumWeight); + } + if (expireAfterWriteNanos != UNSET_INT) { + s.add("expireAfterWrite", expireAfterWriteNanos + "ns"); + } + if (expireAfterAccessNanos != UNSET_INT) { + s.add("expireAfterAccess", expireAfterAccessNanos + "ns"); + } + if (keyStrength != null) { + s.add("keyStrength", Ascii.toLowerCase(keyStrength.toString())); + } + if (valueStrength != null) { + s.add("valueStrength", Ascii.toLowerCase(valueStrength.toString())); + } + if (keyEquivalence != null) { + s.addValue("keyEquivalence"); + } + if (valueEquivalence != null) { + s.addValue("valueEquivalence"); + } + if (removalListener != null) { + s.addValue("removalListener"); + } + return s.toString(); + } + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + @GwtIncompatible // java.time.Duration + @SuppressWarnings("GoodTime") // duration decomposition + private static long toNanosSaturated(java.time.Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheBuilderSpec.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheBuilderSpec.java new file mode 100644 index 0000000000000..411ff72cd9630 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheBuilderSpec.java @@ -0,0 +1,494 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Splitter; +import org.apache.iotdb.google.common.cache.LocalCache.Strength; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; + +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Strings.isNullOrEmpty; + +/** + * A specification of a {@link CacheBuilder} configuration. + * + *

{@code CacheBuilderSpec} supports parsing configuration off of a string, which makes it + * especially useful for command-line configuration of a {@code CacheBuilder}. + * + *

The string syntax is a series of comma-separated keys or key-value pairs, each corresponding + * to a {@code CacheBuilder} method. + * + *

    + *
  • {@code concurrencyLevel=[integer]}: sets {@link CacheBuilder#concurrencyLevel}. + *
  • {@code initialCapacity=[integer]}: sets {@link CacheBuilder#initialCapacity}. + *
  • {@code maximumSize=[long]}: sets {@link CacheBuilder#maximumSize}. + *
  • {@code maximumWeight=[long]}: sets {@link CacheBuilder#maximumWeight}. + *
  • {@code expireAfterAccess=[duration]}: sets {@link CacheBuilder#expireAfterAccess}. + *
  • {@code expireAfterWrite=[duration]}: sets {@link CacheBuilder#expireAfterWrite}. + *
  • {@code refreshAfterWrite=[duration]}: sets {@link CacheBuilder#refreshAfterWrite}. + *
  • {@code weakKeys}: sets {@link CacheBuilder#weakKeys}. + *
  • {@code softValues}: sets {@link CacheBuilder#softValues}. + *
  • {@code weakValues}: sets {@link CacheBuilder#weakValues}. + *
  • {@code recordStats}: sets {@link CacheBuilder#recordStats}. + *
+ * + *

The set of supported keys will grow as {@code CacheBuilder} evolves, but existing keys will + * never be removed. + * + *

Durations are represented by an integer, followed by one of "d", "h", "m", or "s", + * representing days, hours, minutes, or seconds respectively. (There is currently no syntax to + * request expiration in milliseconds, microseconds, or nanoseconds.) + * + *

Whitespace before and after commas and equal signs is ignored. Keys may not be repeated; it is + * also illegal to use the following pairs of keys in a single value: + * + *

    + *
  • {@code maximumSize} and {@code maximumWeight} + *
  • {@code softValues} and {@code weakValues} + *
+ * + *

{@code CacheBuilderSpec} does not support configuring {@code CacheBuilder} methods with + * non-value parameters. These must be configured in code. + * + *

A new {@code CacheBuilder} can be instantiated from a {@code CacheBuilderSpec} using {@link + * CacheBuilder#from(CacheBuilderSpec)} or {@link CacheBuilder#from(String)}. + * + * @author Adam Winer + * @since 12.0 + */ +@SuppressWarnings("GoodTime") // lots of violations (nanosecond math) +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class CacheBuilderSpec { + /** Parses a single value. */ + private interface ValueParser { + void parse(CacheBuilderSpec spec, String key, String value); + } + + /** Splits each key-value pair. */ + private static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults(); + + /** Splits the key from the value. */ + private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults(); + + /** Map of names to ValueParser. */ + private static final ImmutableMap VALUE_PARSERS = + ImmutableMap.builder() + .put("initialCapacity", new InitialCapacityParser()) + .put("maximumSize", new MaximumSizeParser()) + .put("maximumWeight", new MaximumWeightParser()) + .put("concurrencyLevel", new ConcurrencyLevelParser()) + .put("weakKeys", new KeyStrengthParser(Strength.WEAK)) + .put("softValues", new ValueStrengthParser(Strength.SOFT)) + .put("weakValues", new ValueStrengthParser(Strength.WEAK)) + .put("recordStats", new RecordStatsParser()) + .put("expireAfterAccess", new AccessDurationParser()) + .put("expireAfterWrite", new WriteDurationParser()) + .put("refreshAfterWrite", new RefreshDurationParser()) + .put("refreshInterval", new RefreshDurationParser()) + .buildOrThrow(); + + @VisibleForTesting Integer initialCapacity; + @VisibleForTesting Long maximumSize; + @VisibleForTesting Long maximumWeight; + @VisibleForTesting Integer concurrencyLevel; + @VisibleForTesting Strength keyStrength; + @VisibleForTesting Strength valueStrength; + @VisibleForTesting Boolean recordStats; + @VisibleForTesting long writeExpirationDuration; + @VisibleForTesting TimeUnit writeExpirationTimeUnit; + @VisibleForTesting long accessExpirationDuration; + @VisibleForTesting TimeUnit accessExpirationTimeUnit; + @VisibleForTesting long refreshDuration; + @VisibleForTesting TimeUnit refreshTimeUnit; + + /** Specification; used for toParseableString(). */ + private final String specification; + + private CacheBuilderSpec(String specification) { + this.specification = specification; + } + + /** + * Creates a CacheBuilderSpec from a string. + * + * @param cacheBuilderSpecification the string form + */ + public static CacheBuilderSpec parse(String cacheBuilderSpecification) { + CacheBuilderSpec spec = new CacheBuilderSpec(cacheBuilderSpecification); + if (!cacheBuilderSpecification.isEmpty()) { + for (String keyValuePair : KEYS_SPLITTER.split(cacheBuilderSpecification)) { + List keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair)); + checkArgument(!keyAndValue.isEmpty(), "blank key-value pair"); + checkArgument( + keyAndValue.size() <= 2, + "key-value pair %s with more than one equals sign", + keyValuePair); + + // Find the ValueParser for the current key. + String key = keyAndValue.get(0); + ValueParser valueParser = VALUE_PARSERS.get(key); + checkArgument(valueParser != null, "unknown key %s", key); + + String value = keyAndValue.size() == 1 ? null : keyAndValue.get(1); + valueParser.parse(spec, key, value); + } + } + + return spec; + } + + /** Returns a CacheBuilderSpec that will prevent caching. */ + public static CacheBuilderSpec disableCaching() { + // Maximum size of zero is one way to block caching + return CacheBuilderSpec.parse("maximumSize=0"); + } + + /** Returns a CacheBuilder configured according to this instance's specification. */ + CacheBuilder toCacheBuilder() { + CacheBuilder builder = CacheBuilder.newBuilder(); + if (initialCapacity != null) { + builder.initialCapacity(initialCapacity); + } + if (maximumSize != null) { + builder.maximumSize(maximumSize); + } + if (maximumWeight != null) { + builder.maximumWeight(maximumWeight); + } + if (concurrencyLevel != null) { + builder.concurrencyLevel(concurrencyLevel); + } + if (keyStrength != null) { + switch (keyStrength) { + case WEAK: + builder.weakKeys(); + break; + default: + throw new AssertionError(); + } + } + if (valueStrength != null) { + switch (valueStrength) { + case SOFT: + builder.softValues(); + break; + case WEAK: + builder.weakValues(); + break; + default: + throw new AssertionError(); + } + } + if (recordStats != null && recordStats) { + builder.recordStats(); + } + if (writeExpirationTimeUnit != null) { + builder.expireAfterWrite(writeExpirationDuration, writeExpirationTimeUnit); + } + if (accessExpirationTimeUnit != null) { + builder.expireAfterAccess(accessExpirationDuration, accessExpirationTimeUnit); + } + if (refreshTimeUnit != null) { + builder.refreshAfterWrite(refreshDuration, refreshTimeUnit); + } + + return builder; + } + + /** + * Returns a string that can be used to parse an equivalent {@code CacheBuilderSpec}. The order + * and form of this representation is not guaranteed, except that reparsing its output will + * produce a {@code CacheBuilderSpec} equal to this instance. + */ + public String toParsableString() { + return specification; + } + + /** + * Returns a string representation for this CacheBuilderSpec instance. The form of this + * representation is not guaranteed. + */ + @Override + public String toString() { + return MoreObjects.toStringHelper(this).addValue(toParsableString()).toString(); + } + + @Override + public int hashCode() { + return Objects.hashCode( + initialCapacity, + maximumSize, + maximumWeight, + concurrencyLevel, + keyStrength, + valueStrength, + recordStats, + durationInNanos(writeExpirationDuration, writeExpirationTimeUnit), + durationInNanos(accessExpirationDuration, accessExpirationTimeUnit), + durationInNanos(refreshDuration, refreshTimeUnit)); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof CacheBuilderSpec)) { + return false; + } + CacheBuilderSpec that = (CacheBuilderSpec) obj; + return Objects.equal(initialCapacity, that.initialCapacity) + && Objects.equal(maximumSize, that.maximumSize) + && Objects.equal(maximumWeight, that.maximumWeight) + && Objects.equal(concurrencyLevel, that.concurrencyLevel) + && Objects.equal(keyStrength, that.keyStrength) + && Objects.equal(valueStrength, that.valueStrength) + && Objects.equal(recordStats, that.recordStats) + && Objects.equal( + durationInNanos(writeExpirationDuration, writeExpirationTimeUnit), + durationInNanos(that.writeExpirationDuration, that.writeExpirationTimeUnit)) + && Objects.equal( + durationInNanos(accessExpirationDuration, accessExpirationTimeUnit), + durationInNanos(that.accessExpirationDuration, that.accessExpirationTimeUnit)) + && Objects.equal( + durationInNanos(refreshDuration, refreshTimeUnit), + durationInNanos(that.refreshDuration, that.refreshTimeUnit)); + } + + /** + * Converts an expiration duration/unit pair into a single Long for hashing and equality. Uses + * nanos to match CacheBuilder implementation. + */ + private static Long durationInNanos(long duration, TimeUnit unit) { + return (unit == null) ? null : unit.toNanos(duration); + } + + /** Base class for parsing integers. */ + abstract static class IntegerParser implements ValueParser { + protected abstract void parseInteger(CacheBuilderSpec spec, int value); + + @Override + public void parse(CacheBuilderSpec spec, String key, String value) { + if (isNullOrEmpty(value)) { + throw new IllegalArgumentException("value of key " + key + " omitted"); + } + try { + parseInteger(spec, Integer.parseInt(value)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + format("key %s value set to %s, must be integer", key, value), e); + } + } + } + + /** Base class for parsing integers. */ + abstract static class LongParser implements ValueParser { + protected abstract void parseLong(CacheBuilderSpec spec, long value); + + @Override + public void parse(CacheBuilderSpec spec, String key, String value) { + if (isNullOrEmpty(value)) { + throw new IllegalArgumentException("value of key " + key + " omitted"); + } + try { + parseLong(spec, Long.parseLong(value)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + format("key %s value set to %s, must be integer", key, value), e); + } + } + } + + /** Parse initialCapacity */ + static class InitialCapacityParser extends IntegerParser { + @Override + protected void parseInteger(CacheBuilderSpec spec, int value) { + checkArgument( + spec.initialCapacity == null, + "initial capacity was already set to %s", + spec.initialCapacity); + spec.initialCapacity = value; + } + } + + /** Parse maximumSize */ + static class MaximumSizeParser extends LongParser { + @Override + protected void parseLong(CacheBuilderSpec spec, long value) { + checkArgument( + spec.maximumSize == null, "maximum size was already set to %s", spec.maximumSize); + checkArgument( + spec.maximumWeight == null, "maximum weight was already set to %s", spec.maximumWeight); + spec.maximumSize = value; + } + } + + /** Parse maximumWeight */ + static class MaximumWeightParser extends LongParser { + @Override + protected void parseLong(CacheBuilderSpec spec, long value) { + checkArgument( + spec.maximumWeight == null, "maximum weight was already set to %s", spec.maximumWeight); + checkArgument( + spec.maximumSize == null, "maximum size was already set to %s", spec.maximumSize); + spec.maximumWeight = value; + } + } + + /** Parse concurrencyLevel */ + static class ConcurrencyLevelParser extends IntegerParser { + @Override + protected void parseInteger(CacheBuilderSpec spec, int value) { + checkArgument( + spec.concurrencyLevel == null, + "concurrency level was already set to %s", + spec.concurrencyLevel); + spec.concurrencyLevel = value; + } + } + + /** Parse weakKeys */ + static class KeyStrengthParser implements ValueParser { + private final Strength strength; + + public KeyStrengthParser(Strength strength) { + this.strength = strength; + } + + @Override + public void parse(CacheBuilderSpec spec, String key, String value) { + checkArgument(value == null, "key %s does not take values", key); + checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength); + spec.keyStrength = strength; + } + } + + /** Parse weakValues and softValues */ + static class ValueStrengthParser implements ValueParser { + private final Strength strength; + + public ValueStrengthParser(Strength strength) { + this.strength = strength; + } + + @Override + public void parse(CacheBuilderSpec spec, String key, String value) { + checkArgument(value == null, "key %s does not take values", key); + checkArgument( + spec.valueStrength == null, "%s was already set to %s", key, spec.valueStrength); + + spec.valueStrength = strength; + } + } + + /** Parse recordStats */ + static class RecordStatsParser implements ValueParser { + + @Override + public void parse(CacheBuilderSpec spec, String key, String value) { + checkArgument(value == null, "recordStats does not take values"); + checkArgument(spec.recordStats == null, "recordStats already set"); + spec.recordStats = true; + } + } + + /** Base class for parsing times with durations */ + abstract static class DurationParser implements ValueParser { + protected abstract void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit); + + @Override + public void parse(CacheBuilderSpec spec, String key, String value) { + if (isNullOrEmpty(value)) { + throw new IllegalArgumentException("value of key " + key + " omitted"); + } + try { + char lastChar = value.charAt(value.length() - 1); + TimeUnit timeUnit; + switch (lastChar) { + case 'd': + timeUnit = TimeUnit.DAYS; + break; + case 'h': + timeUnit = TimeUnit.HOURS; + break; + case 'm': + timeUnit = TimeUnit.MINUTES; + break; + case 's': + timeUnit = TimeUnit.SECONDS; + break; + default: + throw new IllegalArgumentException( + format("key %s invalid unit: was %s, must end with one of [dhms]", key, value)); + } + + long duration = Long.parseLong(value.substring(0, value.length() - 1)); + parseDuration(spec, duration, timeUnit); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + format("key %s value set to %s, must be integer", key, value)); + } + } + } + + /** Parse expireAfterAccess */ + static class AccessDurationParser extends DurationParser { + @Override + protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { + checkArgument(spec.accessExpirationTimeUnit == null, "expireAfterAccess already set"); + spec.accessExpirationDuration = duration; + spec.accessExpirationTimeUnit = unit; + } + } + + /** Parse expireAfterWrite */ + static class WriteDurationParser extends DurationParser { + @Override + protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { + checkArgument(spec.writeExpirationTimeUnit == null, "expireAfterWrite already set"); + spec.writeExpirationDuration = duration; + spec.writeExpirationTimeUnit = unit; + } + } + + /** Parse refreshAfterWrite */ + static class RefreshDurationParser extends DurationParser { + @Override + protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { + checkArgument(spec.refreshTimeUnit == null, "refreshAfterWrite already set"); + spec.refreshDuration = duration; + spec.refreshTimeUnit = unit; + } + } + + private static String format(String format, Object... args) { + return String.format(Locale.ROOT, format, args); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheLoader.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheLoader.java new file mode 100644 index 0000000000000..91625d21ee903 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheLoader.java @@ -0,0 +1,255 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.ListenableFutureTask; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.Executor; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Computes or retrieves values, based on a key, for use in populating a {@link LoadingCache}. + * + *

Most implementations will only need to implement {@link #load}. Other methods may be + * overridden as desired. + * + *

Usage example: + * + *

{@code
+ * CacheLoader loader = new CacheLoader() {
+ *   public Graph load(Key key) throws AnyException {
+ *     return createExpensiveGraph(key);
+ *   }
+ * };
+ * LoadingCache cache = CacheBuilder.newBuilder().build(loader);
+ * }
+ * + *

Since this example doesn't support reloading or bulk loading, it can also be specified much + * more simply: + * + *

{@code
+ * CacheLoader loader = CacheLoader.from(key -> createExpensiveGraph(key));
+ * }
+ * + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class CacheLoader { + /** Constructor for use by subclasses. */ + protected CacheLoader() {} + + /** + * Computes or retrieves the value corresponding to {@code key}. + * + * @param key the non-null key whose value should be loaded + * @return the value associated with {@code key}; must not be null + * @throws Exception if unable to load the result + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + */ + public abstract V load(K key) throws Exception; + + /** + * Computes or retrieves a replacement value corresponding to an already-cached {@code key}. This + * method is called when an existing cache entry is refreshed by {@link + * CacheBuilder#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}. + * + *

This implementation synchronously delegates to {@link #load}. It is recommended that it be + * overridden with an asynchronous implementation when using {@link + * CacheBuilder#refreshAfterWrite}. + * + *

Note: all exceptions thrown by this method will be logged and then swallowed. + * + * @param key the non-null key whose value should be loaded + * @param oldValue the non-null old value corresponding to {@code key} + * @return the future new value associated with {@code key}; must not be null, must not return + * null + * @throws Exception if unable to reload the result + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + * @since 11.0 + */ + @GwtIncompatible // Futures + public ListenableFuture reload(K key, V oldValue) throws Exception { + checkNotNull(key); + checkNotNull(oldValue); + return Futures.immediateFuture(load(key)); + } + + /** + * Computes or retrieves the values corresponding to {@code keys}. This method is called by {@link + * LoadingCache#getAll}. + * + *

If the returned map doesn't contain all requested {@code keys} then the entries it does + * contain will be cached, but {@code getAll} will throw an exception. If the returned map + * contains extra keys not present in {@code keys} then all returned entries will be cached, but + * only the entries for {@code keys} will be returned from {@code getAll}. + * + *

This method should be overridden when bulk retrieval is significantly more efficient than + * many individual lookups. Note that {@link LoadingCache#getAll} will defer to individual calls + * to {@link LoadingCache#get} if this method is not overridden. + * + * @param keys the unique, non-null keys whose values should be loaded + * @return a map from each key in {@code keys} to the value associated with that key; may not + * contain null values + * @throws Exception if unable to load the result + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + * @since 11.0 + */ + public Map loadAll(Iterable keys) throws Exception { + // This will be caught by getAll(), causing it to fall back to multiple calls to + // LoadingCache.get + throw new UnsupportedLoadingOperationException(); + } + + /** + * Returns a cache loader that uses {@code function} to load keys, without supporting either + * reloading or bulk loading. This allows creating a cache loader using a lambda expression. + * + *

The returned object is serializable if {@code function} is serializable. + * + * @param function the function to be used for loading values; must never return {@code null} + * @return a cache loader that loads values by passing each key to {@code function} + */ + public static CacheLoader from(Function function) { + return new FunctionToCacheLoader<>(function); + } + + /** + * Returns a cache loader based on an existing supplier instance. Note that there's no need + * to create a new supplier just to pass it in here; just subclass {@code CacheLoader} and + * implement {@link #load load} instead. + * + *

The returned object is serializable if {@code supplier} is serializable. + * + * @param supplier the supplier to be used for loading values; must never return {@code null} + * @return a cache loader that loads values by calling {@link Supplier#get}, irrespective of the + * key + */ + public static CacheLoader from(Supplier supplier) { + return new SupplierToCacheLoader<>(supplier); + } + + private static final class FunctionToCacheLoader extends CacheLoader + implements Serializable { + private final Function computingFunction; + + public FunctionToCacheLoader(Function computingFunction) { + this.computingFunction = checkNotNull(computingFunction); + } + + @Override + public V load(K key) { + return computingFunction.apply(checkNotNull(key)); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a {@code CacheLoader} which wraps {@code loader}, executing calls to {@link + * CacheLoader#reload} using {@code executor}. + * + *

This method is useful only when {@code loader.reload} has a synchronous implementation, such + * as {@linkplain #reload the default implementation}. + * + * @since 17.0 + */ + @GwtIncompatible // Executor + Futures + public static CacheLoader asyncReloading( + final CacheLoader loader, final Executor executor) { + checkNotNull(loader); + checkNotNull(executor); + return new CacheLoader() { + @Override + public V load(K key) throws Exception { + return loader.load(key); + } + + @Override + public ListenableFuture reload(final K key, final V oldValue) { + ListenableFutureTask task = + ListenableFutureTask.create(() -> loader.reload(key, oldValue).get()); + executor.execute(task); + return task; + } + + @Override + public Map loadAll(Iterable keys) throws Exception { + return loader.loadAll(keys); + } + }; + } + + private static final class SupplierToCacheLoader extends CacheLoader + implements Serializable { + private final Supplier computingSupplier; + + public SupplierToCacheLoader(Supplier computingSupplier) { + this.computingSupplier = checkNotNull(computingSupplier); + } + + @Override + public V load(Object key) { + checkNotNull(key); + return computingSupplier.get(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Exception thrown by {@code loadAll()} to indicate that it is not supported. + * + * @since 19.0 + */ + public static final class UnsupportedLoadingOperationException + extends UnsupportedOperationException { + // Package-private because this should only be thrown by loadAll() when it is not overridden. + // Cache implementors may want to catch it but should not need to be able to throw it. + UnsupportedLoadingOperationException() {} + } + + /** + * Thrown to indicate that an invalid response was returned from a call to {@link CacheLoader}. + * + * @since 11.0 + */ + public static final class InvalidCacheLoadException extends RuntimeException { + public InvalidCacheLoadException(String message) { + super(message); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheStats.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheStats.java new file mode 100644 index 0000000000000..3ba6b6cf97123 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/CacheStats.java @@ -0,0 +1,309 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.concurrent.Callable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.math.LongMath.saturatedAdd; +import static org.apache.iotdb.google.common.math.LongMath.saturatedSubtract; + +/** + * Statistics about the performance of a {@link Cache}. Instances of this class are immutable. + * + *

Cache statistics are incremented according to the following rules: + * + *

    + *
  • When a cache lookup encounters an existing cache entry {@code hitCount} is incremented. + *
  • When a cache lookup first encounters a missing cache entry, a new entry is loaded. + *
      + *
    • After successfully loading an entry {@code missCount} and {@code loadSuccessCount} + * are incremented, and the total loading time, in nanoseconds, is added to {@code + * totalLoadTime}. + *
    • When an exception is thrown while loading an entry, {@code missCount} and {@code + * loadExceptionCount} are incremented, and the total loading time, in nanoseconds, is + * added to {@code totalLoadTime}. + *
    • Cache lookups that encounter a missing cache entry that is still loading will wait + * for loading to complete (whether successful or not) and then increment {@code + * missCount}. + *
    + *
  • When an entry is evicted from the cache, {@code evictionCount} is incremented. + *
  • No stats are modified when a cache entry is invalidated or manually removed. + *
  • No stats are modified by operations invoked on the {@linkplain Cache#asMap asMap} view of + * the cache. + *
+ * + *

A lookup is specifically defined as an invocation of one of the methods {@link + * LoadingCache#get(Object)}, {@link LoadingCache#getUnchecked(Object)}, {@link Cache#get(Object, + * Callable)}, or {@link LoadingCache#getAll(Iterable)}. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class CacheStats { + private final long hitCount; + private final long missCount; + private final long loadSuccessCount; + private final long loadExceptionCount; + + @SuppressWarnings("GoodTime") // should be a java.time.Duration + private final long totalLoadTime; + + private final long evictionCount; + + /** + * Constructs a new {@code CacheStats} instance. + * + *

Five parameters of the same type in a row is a bad thing, but this class is not constructed + * by end users and is too fine-grained for a builder. + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public CacheStats( + long hitCount, + long missCount, + long loadSuccessCount, + long loadExceptionCount, + long totalLoadTime, + long evictionCount) { + checkArgument(hitCount >= 0); + checkArgument(missCount >= 0); + checkArgument(loadSuccessCount >= 0); + checkArgument(loadExceptionCount >= 0); + checkArgument(totalLoadTime >= 0); + checkArgument(evictionCount >= 0); + + this.hitCount = hitCount; + this.missCount = missCount; + this.loadSuccessCount = loadSuccessCount; + this.loadExceptionCount = loadExceptionCount; + this.totalLoadTime = totalLoadTime; + this.evictionCount = evictionCount; + } + + /** + * Returns the number of times {@link Cache} lookup methods have returned either a cached or + * uncached value. This is defined as {@code hitCount + missCount}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + */ + public long requestCount() { + return saturatedAdd(hitCount, missCount); + } + + /** Returns the number of times {@link Cache} lookup methods have returned a cached value. */ + public long hitCount() { + return hitCount; + } + + /** + * Returns the ratio of cache requests which were hits. This is defined as {@code hitCount / + * requestCount}, or {@code 1.0} when {@code requestCount == 0}. Note that {@code hitRate + + * missRate =~ 1.0}. + */ + public double hitRate() { + long requestCount = requestCount(); + return (requestCount == 0) ? 1.0 : (double) hitCount / requestCount; + } + + /** + * Returns the number of times {@link Cache} lookup methods have returned an uncached (newly + * loaded) value, or null. Multiple concurrent calls to {@link Cache} lookup methods on an absent + * value can result in multiple misses, all returning the results of a single cache load + * operation. + */ + public long missCount() { + return missCount; + } + + /** + * Returns the ratio of cache requests which were misses. This is defined as {@code missCount / + * requestCount}, or {@code 0.0} when {@code requestCount == 0}. Note that {@code hitRate + + * missRate =~ 1.0}. Cache misses include all requests which weren't cache hits, including + * requests which resulted in either successful or failed loading attempts, and requests which + * waited for other threads to finish loading. It is thus the case that {@code missCount >= + * loadSuccessCount + loadExceptionCount}. Multiple concurrent misses for the same key will result + * in a single load operation. + */ + public double missRate() { + long requestCount = requestCount(); + return (requestCount == 0) ? 0.0 : (double) missCount / requestCount; + } + + /** + * Returns the total number of times that {@link Cache} lookup methods attempted to load new + * values. This includes both successful load operations and those that threw exceptions. This is + * defined as {@code loadSuccessCount + loadExceptionCount}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + */ + public long loadCount() { + return saturatedAdd(loadSuccessCount, loadExceptionCount); + } + + /** + * Returns the number of times {@link Cache} lookup methods have successfully loaded a new value. + * This is usually incremented in conjunction with {@link #missCount}, though {@code missCount} is + * also incremented when an exception is encountered during cache loading (see {@link + * #loadExceptionCount}). Multiple concurrent misses for the same key will result in a single load + * operation. This may be incremented not in conjunction with {@code missCount} if the load occurs + * as a result of a refresh or if the cache loader returned more items than was requested. {@code + * missCount} may also be incremented not in conjunction with this (nor {@link + * #loadExceptionCount}) on calls to {@code getIfPresent}. + */ + public long loadSuccessCount() { + return loadSuccessCount; + } + + /** + * Returns the number of times {@link Cache} lookup methods threw an exception while loading a new + * value. This is usually incremented in conjunction with {@code missCount}, though {@code + * missCount} is also incremented when cache loading completes successfully (see {@link + * #loadSuccessCount}). Multiple concurrent misses for the same key will result in a single load + * operation. This may be incremented not in conjunction with {@code missCount} if the load occurs + * as a result of a refresh or if the cache loader returned more items than was requested. {@code + * missCount} may also be incremented not in conjunction with this (nor {@link #loadSuccessCount}) + * on calls to {@code getIfPresent}. + */ + public long loadExceptionCount() { + return loadExceptionCount; + } + + /** + * Returns the ratio of cache loading attempts which threw exceptions. This is defined as {@code + * loadExceptionCount / (loadSuccessCount + loadExceptionCount)}, or {@code 0.0} when {@code + * loadSuccessCount + loadExceptionCount == 0}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + */ + public double loadExceptionRate() { + long totalLoadCount = saturatedAdd(loadSuccessCount, loadExceptionCount); + return (totalLoadCount == 0) ? 0.0 : (double) loadExceptionCount / totalLoadCount; + } + + /** + * Returns the total number of nanoseconds the cache has spent loading new values. This can be + * used to calculate the miss penalty. This value is increased every time {@code loadSuccessCount} + * or {@code loadExceptionCount} is incremented. + */ + @SuppressWarnings("GoodTime") // should return a java.time.Duration + public long totalLoadTime() { + return totalLoadTime; + } + + /** + * Returns the average time spent loading new values. This is defined as {@code totalLoadTime / + * (loadSuccessCount + loadExceptionCount)}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + */ + public double averageLoadPenalty() { + long totalLoadCount = saturatedAdd(loadSuccessCount, loadExceptionCount); + return (totalLoadCount == 0) ? 0.0 : (double) totalLoadTime / totalLoadCount; + } + + /** + * Returns the number of times an entry has been evicted. This count does not include manual + * {@linkplain Cache#invalidate invalidations}. + */ + public long evictionCount() { + return evictionCount; + } + + /** + * Returns a new {@code CacheStats} representing the difference between this {@code CacheStats} + * and {@code other}. Negative values, which aren't supported by {@code CacheStats} will be + * rounded up to zero. + */ + public CacheStats minus(CacheStats other) { + return new CacheStats( + Math.max(0, saturatedSubtract(hitCount, other.hitCount)), + Math.max(0, saturatedSubtract(missCount, other.missCount)), + Math.max(0, saturatedSubtract(loadSuccessCount, other.loadSuccessCount)), + Math.max(0, saturatedSubtract(loadExceptionCount, other.loadExceptionCount)), + Math.max(0, saturatedSubtract(totalLoadTime, other.totalLoadTime)), + Math.max(0, saturatedSubtract(evictionCount, other.evictionCount))); + } + + /** + * Returns a new {@code CacheStats} representing the sum of this {@code CacheStats} and {@code + * other}. + * + *

Note: the values of the metrics are undefined in case of overflow (though it is + * guaranteed not to throw an exception). If you require specific handling, we recommend + * implementing your own stats collector. + * + * @since 11.0 + */ + public CacheStats plus(CacheStats other) { + return new CacheStats( + saturatedAdd(hitCount, other.hitCount), + saturatedAdd(missCount, other.missCount), + saturatedAdd(loadSuccessCount, other.loadSuccessCount), + saturatedAdd(loadExceptionCount, other.loadExceptionCount), + saturatedAdd(totalLoadTime, other.totalLoadTime), + saturatedAdd(evictionCount, other.evictionCount)); + } + + @Override + public int hashCode() { + return Objects.hashCode( + hitCount, missCount, loadSuccessCount, loadExceptionCount, totalLoadTime, evictionCount); + } + + @Override + public boolean equals(Object object) { + if (object instanceof CacheStats) { + CacheStats other = (CacheStats) object; + return hitCount == other.hitCount + && missCount == other.missCount + && loadSuccessCount == other.loadSuccessCount + && loadExceptionCount == other.loadExceptionCount + && totalLoadTime == other.totalLoadTime + && evictionCount == other.evictionCount; + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("hitCount", hitCount) + .add("missCount", missCount) + .add("loadSuccessCount", loadSuccessCount) + .add("loadExceptionCount", loadExceptionCount) + .add("totalLoadTime", totalLoadTime) + .add("evictionCount", evictionCount) + .toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..98982b8f748bf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ForwardingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ForwardingCache.java new file mode 100644 index 0000000000000..0780f1ac1b3fb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ForwardingCache.java @@ -0,0 +1,151 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.ForwardingObject; +import org.apache.iotdb.google.common.collect.ImmutableMap; + +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +/** + * A cache which forwards all its method calls to another cache. Subclasses should override one or + * more methods to modify the behavior of the backing cache as desired per the decorator pattern. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingCache extends ForwardingObject implements Cache { + + /** Constructor for use by subclasses. */ + protected ForwardingCache() {} + + @Override + protected abstract Cache delegate(); + + /** + * @since 11.0 + */ + @Override + public V getIfPresent(Object key) { + return delegate().getIfPresent(key); + } + + /** + * @since 11.0 + */ + @Override + public V get(K key, Callable valueLoader) throws ExecutionException { + return delegate().get(key, valueLoader); + } + + /** + * @since 11.0 + */ + @Override + /* + * is mostly the same as to plain Java. But to nullness checkers, they + * differ: means "non-null types," while means "all types." + */ + public ImmutableMap getAllPresent(Iterable keys) { + return delegate().getAllPresent(keys); + } + + /** + * @since 11.0 + */ + @Override + public void put(K key, V value) { + delegate().put(key, value); + } + + /** + * @since 12.0 + */ + @Override + public void putAll(Map m) { + delegate().putAll(m); + } + + @Override + public void invalidate(Object key) { + delegate().invalidate(key); + } + + /** + * @since 11.0 + */ + @Override + // For discussion of , see getAllPresent. + public void invalidateAll(Iterable keys) { + delegate().invalidateAll(keys); + } + + @Override + public void invalidateAll() { + delegate().invalidateAll(); + } + + @Override + public long size() { + return delegate().size(); + } + + @Override + public CacheStats stats() { + return delegate().stats(); + } + + @Override + public ConcurrentMap asMap() { + return delegate().asMap(); + } + + @Override + public void cleanUp() { + delegate().cleanUp(); + } + + /** + * A simplified version of {@link ForwardingCache} where subclasses can pass in an already + * constructed {@link Cache} as the delegate. + * + * @since 10.0 + */ + public abstract static class SimpleForwardingCache extends ForwardingCache { + private final Cache delegate; + + protected SimpleForwardingCache(Cache delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final Cache delegate() { + return delegate; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ForwardingLoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ForwardingLoadingCache.java new file mode 100644 index 0000000000000..9804060ac4762 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ForwardingLoadingCache.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.ImmutableMap; + +import java.util.concurrent.ExecutionException; + +/** + * A cache which forwards all its method calls to another cache. Subclasses should override one or + * more methods to modify the behavior of the backing cache as desired per the decorator pattern. + * + *

Note that {@link #get}, {@link #getUnchecked}, and {@link #apply} all expose the same + * underlying functionality, so should probably be overridden as a group. + * + * @author Charles Fry + * @since 11.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingLoadingCache extends ForwardingCache + implements LoadingCache { + + /** Constructor for use by subclasses. */ + protected ForwardingLoadingCache() {} + + @Override + protected abstract LoadingCache delegate(); + + // TODO(b/27479612): consider removing this + @Override + public V get(K key) throws ExecutionException { + return delegate().get(key); + } + + // TODO(b/27479612): consider removing this + @Override + public V getUnchecked(K key) { + return delegate().getUnchecked(key); + } + + // TODO(b/27479612): consider removing this + @Override + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + return delegate().getAll(keys); + } + + @Override + public V apply(K key) { + return delegate().apply(key); + } + + @Override + public void refresh(K key) { + delegate().refresh(key); + } + + /** + * A simplified version of {@link ForwardingLoadingCache} where subclasses can pass in an already + * constructed {@link LoadingCache} as the delegate. + * + * @since 10.0 + */ + public abstract static class SimpleForwardingLoadingCache + extends ForwardingLoadingCache { + private final LoadingCache delegate; + + protected SimpleForwardingLoadingCache(LoadingCache delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final LoadingCache delegate() { + return delegate; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LoadingCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LoadingCache.java new file mode 100644 index 0000000000000..171df3b6bd05c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LoadingCache.java @@ -0,0 +1,172 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.util.concurrent.ExecutionError; +import org.apache.iotdb.google.common.util.concurrent.UncheckedExecutionException; + +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +/** + * A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and + * are stored in the cache until either evicted or manually invalidated. The common way to build + * instances is using {@link CacheBuilder}. + * + *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + * + *

When evaluated as a {@link Function}, a cache yields the same result as invoking {@link + * #getUnchecked}. + * + * @param the type of the cache's keys, which are not permitted to be null + * @param the type of the cache's values, which are not permitted to be null + * @author Charles Fry + * @since 11.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface LoadingCache extends Cache, Function { + + /** + * Returns the value associated with {@code key} in this cache, first loading that value if + * necessary. No observable state associated with this cache is modified until loading completes. + * + *

If another call to {@link #get} or {@link #getUnchecked} is currently loading the value for + * {@code key}, simply waits for that thread to finish and returns its loaded value. Note that + * multiple threads can concurrently load values for distinct keys. + * + *

Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#load} to load new values + * into the cache. Newly loaded values are added to the cache using {@code + * Cache.asMap().putIfAbsent} after loading has completed; if another value was associated with + * {@code key} while the new value was loading then a removal notification will be sent for the + * new value. + * + *

If the cache loader associated with this cache is known not to throw checked exceptions, + * then prefer {@link #getUnchecked} over this method. + * + * @throws ExecutionException if a checked exception was thrown while loading the value. ({@code + * ExecutionException} is thrown even if + * computation was interrupted by an {@code InterruptedException}.) + * @throws UncheckedExecutionException if an unchecked exception was thrown while loading the + * value + * @throws ExecutionError if an error was thrown while loading the value + */ + // TODO(b/27479612): consider removing this? + V get(K key) throws ExecutionException; + + /** + * Returns the value associated with {@code key} in this cache, first loading that value if + * necessary. No observable state associated with this cache is modified until loading completes. + * Unlike {@link #get}, this method does not throw a checked exception, and thus should only be + * used in situations where checked exceptions are not thrown by the cache loader. + * + *

If another call to {@link #get} or {@link #getUnchecked} is currently loading the value for + * {@code key}, simply waits for that thread to finish and returns its loaded value. Note that + * multiple threads can concurrently load values for distinct keys. + * + *

Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#load} to load new values + * into the cache. Newly loaded values are added to the cache using {@code + * Cache.asMap().putIfAbsent} after loading has completed; if another value was associated with + * {@code key} while the new value was loading then a removal notification will be sent for the + * new value. + * + *

Warning: this method silently converts checked exceptions to unchecked exceptions, + * and should not be used with cache loaders which throw checked exceptions. In such cases use + * {@link #get} instead. + * + * @throws UncheckedExecutionException if an exception was thrown while loading the value. (As + * explained in the last paragraph above, this should be an unchecked exception only.) + * @throws ExecutionError if an error was thrown while loading the value + */ + // TODO(b/27479612): consider removing this? + V getUnchecked(K key); + + /** + * Returns a map of the values associated with {@code keys}, creating or retrieving those values + * if necessary. The returned map contains entries that were already cached, combined with newly + * loaded entries; it will never contain null keys or values. + * + *

Caches loaded by a {@link CacheLoader} will issue a single request to {@link + * CacheLoader#loadAll} for all keys which are not already present in the cache. All entries + * returned by {@link CacheLoader#loadAll} will be stored in the cache, over-writing any + * previously cached values. This method will throw an exception if {@link CacheLoader#loadAll} + * returns {@code null}, returns a map containing null keys or values, or fails to return an entry + * for each requested key. + * + *

Note that duplicate elements in {@code keys}, as determined by {@link Object#equals}, will + * be ignored. + * + * @throws ExecutionException if a checked exception was thrown while loading the value. ({@code + * ExecutionException} is thrown even if + * computation was interrupted by an {@code InterruptedException}.) + * @throws UncheckedExecutionException if an unchecked exception was thrown while loading the + * values + * @throws ExecutionError if an error was thrown while loading the values + * @since 11.0 + */ + // TODO(b/27479612): consider removing this + ImmutableMap getAll(Iterable keys) throws ExecutionException; + + /** + * @deprecated Provided to satisfy the {@code Function} interface; use {@link #get} or {@link + * #getUnchecked} instead. + * @throws UncheckedExecutionException if an exception was thrown while loading the value. (As + * described in the documentation for {@link #getUnchecked}, {@code LoadingCache} should be + * used as a {@code Function} only with cache loaders that throw only unchecked exceptions.) + */ + @Deprecated + @Override + V apply(K key); + + /** + * Loads a new value for {@code key}, possibly asynchronously. While the new value is loading the + * previous value (if any) will continue to be returned by {@code get(key)} unless it is evicted. + * If the new value is loaded successfully it will replace the previous value in the cache; if an + * exception is thrown while refreshing the previous value will remain, and the exception will + * be logged (using {@link java.util.logging.Logger}) and swallowed. + * + *

Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#reload} if the cache + * currently contains a value for {@code key}, and {@link CacheLoader#load} otherwise. Loading is + * asynchronous only if {@link CacheLoader#reload} was overridden with an asynchronous + * implementation. + * + *

Returns without doing anything if another thread is currently loading the value for {@code + * key}. If the cache loader associated with this cache performs refresh asynchronously then this + * method may return before refresh completes. + * + * @since 11.0 + */ + void refresh(K key); + + /** + * {@inheritDoc} + * + *

Note that although the view is modifiable, no method on the returned map will ever + * cause entries to be automatically loaded. + */ + @Override + ConcurrentMap asMap(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LocalCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LocalCache.java new file mode 100644 index 0000000000000..d104f2b9f70aa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LocalCache.java @@ -0,0 +1,4966 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.base.Stopwatch; +import org.apache.iotdb.google.common.base.Ticker; +import org.apache.iotdb.google.common.cache.AbstractCache.SimpleStatsCounter; +import org.apache.iotdb.google.common.cache.AbstractCache.StatsCounter; +import org.apache.iotdb.google.common.cache.CacheBuilder.NullListener; +import org.apache.iotdb.google.common.cache.CacheBuilder.OneWeigher; +import org.apache.iotdb.google.common.cache.CacheLoader.InvalidCacheLoadException; +import org.apache.iotdb.google.common.cache.CacheLoader.UnsupportedLoadingOperationException; +import org.apache.iotdb.google.common.collect.AbstractSequentialIterator; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Sets; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.util.concurrent.ExecutionError; +import org.apache.iotdb.google.common.util.concurrent.Futures; +import org.apache.iotdb.google.common.util.concurrent.ListenableFuture; +import org.apache.iotdb.google.common.util.concurrent.SettableFuture; +import org.apache.iotdb.google.common.util.concurrent.UncheckedExecutionException; +import org.apache.iotdb.google.common.util.concurrent.Uninterruptibles; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractQueue; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Collections.unmodifiableSet; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.cache.CacheBuilder.NULL_TICKER; +import static org.apache.iotdb.google.common.cache.CacheBuilder.UNSET_INT; +import static org.apache.iotdb.google.common.util.concurrent.Futures.transform; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; + +/** + * The concurrent hash map implementation built by {@link CacheBuilder}. + * + *

This implementation is heavily derived from revision 1.96 of ConcurrentHashMap.java. + * + * @author Charles Fry + * @author Bob Lee ({@code org.apache.iotdb.google.common.collect.MapMaker}) + * @author Doug Lea ({@code ConcurrentHashMap}) + */ +@SuppressWarnings({ + "GoodTime", // lots of violations (nanosecond math) + "nullness", // too much trouble for the payoff +}) +@GwtCompatible(emulated = true) +// TODO(cpovirk): Annotate for nullness. +class LocalCache extends AbstractMap implements ConcurrentMap { + + /* + * The basic strategy is to subdivide the table among Segments, each of which itself is a + * concurrently readable hash table. The map supports non-blocking reads and concurrent writes + * across different segments. + * + * If a maximum size is specified, a best-effort bounding is performed per segment, using a + * page-replacement algorithm to determine which entries to evict when the capacity has been + * exceeded. + * + * The page replacement algorithm's data structures are kept casually consistent with the map. The + * ordering of writes to a segment is sequentially consistent. An update to the map and recording + * of reads may not be immediately reflected on the algorithm's data structures. These structures + * are guarded by a lock and operations are applied in batches to avoid lock contention. The + * penalty of applying the batches is spread across threads so that the amortized cost is slightly + * higher than performing just the operation without enforcing the capacity constraint. + * + * This implementation uses a per-segment queue to record a memento of the additions, removals, + * and accesses that were performed on the map. The queue is drained on writes and when it exceeds + * its capacity threshold. + * + * The Least Recently Used page replacement algorithm was chosen due to its simplicity, high hit + * rate, and ability to be implemented with O(1) time complexity. The initial LRU implementation + * operates per-segment rather than globally for increased implementation simplicity. We expect + * the cache hit rate to be similar to that of a global LRU algorithm. + */ + + // Constants + + /** + * The maximum capacity, used if a higher value is implicitly specified by either of the + * constructors with arguments. MUST be a power of two {@code <= 1<<30} to ensure that entries are + * indexable using ints. + */ + static final int MAXIMUM_CAPACITY = 1 << 30; + + /** The maximum number of segments to allow; used to bound constructor arguments. */ + static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + + /** Number of (unsynchronized) retries in the containsValue method. */ + static final int CONTAINS_VALUE_RETRIES = 3; + + /** + * Number of cache access operations that can be buffered per segment before the cache's recency + * ordering information is updated. This is used to avoid lock contention by recording a memento + * of reads and delaying a lock acquisition until the threshold is crossed or a mutation occurs. + * + *

This must be a (2^n)-1 as it is used as a mask. + */ + static final int DRAIN_THRESHOLD = 0x3F; + + /** + * Maximum number of entries to be drained in a single cleanup run. This applies independently to + * the cleanup queue and both reference queues. + */ + // TODO(fry): empirically optimize this + static final int DRAIN_MAX = 16; + + // Fields + + static final Logger logger = Logger.getLogger(LocalCache.class.getName()); + + /** + * Mask value for indexing into segments. The upper bits of a key's hash code are used to choose + * the segment. + */ + final int segmentMask; + + /** + * Shift value for indexing within segments. Helps prevent entries that end up in the same segment + * from also ending up in the same bucket. + */ + final int segmentShift; + + /** The segments, each of which is a specialized hash table. */ + final Segment[] segments; + + /** The concurrency level. */ + final int concurrencyLevel; + + /** Strategy for comparing keys. */ + final Equivalence keyEquivalence; + + /** Strategy for comparing values. */ + final Equivalence valueEquivalence; + + /** Strategy for referencing keys. */ + final Strength keyStrength; + + /** Strategy for referencing values. */ + final Strength valueStrength; + + /** The maximum weight of this map. UNSET_INT if there is no maximum. */ + final long maxWeight; + + /** Weigher to weigh cache entries. */ + final Weigher weigher; + + /** How long after the last access to an entry the map will retain that entry. */ + final long expireAfterAccessNanos; + + /** How long after the last write to an entry the map will retain that entry. */ + final long expireAfterWriteNanos; + + /** How long after the last write an entry becomes a candidate for refresh. */ + final long refreshNanos; + + /** Entries waiting to be consumed by the removal listener. */ + // TODO(fry): define a new type which creates event objects and automates the clear logic + final Queue> removalNotificationQueue; + + /** + * A listener that is invoked when an entry is removed due to expiration or garbage collection of + * soft/weak entries. + */ + final RemovalListener removalListener; + + /** Measures time in a testable way. */ + final Ticker ticker; + + /** Factory used to create new entries. */ + final EntryFactory entryFactory; + + /** + * Accumulates global cache statistics. Note that there are also per-segments stats counters which + * must be aggregated to obtain a global stats view. + */ + final StatsCounter globalStatsCounter; + + /** The default cache loader to use on loading operations. */ + final CacheLoader defaultLoader; + + /** + * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. + */ + LocalCache(CacheBuilder builder, CacheLoader loader) { + concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); + + keyStrength = builder.getKeyStrength(); + valueStrength = builder.getValueStrength(); + + keyEquivalence = builder.getKeyEquivalence(); + valueEquivalence = builder.getValueEquivalence(); + + maxWeight = builder.getMaximumWeight(); + weigher = builder.getWeigher(); + expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); + expireAfterWriteNanos = builder.getExpireAfterWriteNanos(); + refreshNanos = builder.getRefreshNanos(); + + removalListener = builder.getRemovalListener(); + removalNotificationQueue = + (removalListener == NullListener.INSTANCE) + ? LocalCache.discardingQueue() + : new ConcurrentLinkedQueue<>(); + + ticker = builder.getTicker(recordsTime()); + entryFactory = EntryFactory.getFactory(keyStrength, usesAccessEntries(), usesWriteEntries()); + globalStatsCounter = builder.getStatsCounterSupplier().get(); + defaultLoader = loader; + + int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); + if (evictsBySize() && !customWeigher()) { + initialCapacity = (int) Math.min(initialCapacity, maxWeight); + } + + // Find the lowest power-of-two segmentCount that exceeds concurrencyLevel, unless + // maximumSize/Weight is specified in which case ensure that each segment gets at least 10 + // entries. The special casing for size-based eviction is only necessary because that eviction + // happens per segment instead of globally, so too many segments compared to the maximum size + // will result in random eviction behavior. + int segmentShift = 0; + int segmentCount = 1; + while (segmentCount < concurrencyLevel && (!evictsBySize() || segmentCount * 20 <= maxWeight)) { + ++segmentShift; + segmentCount <<= 1; + } + this.segmentShift = 32 - segmentShift; + segmentMask = segmentCount - 1; + + this.segments = newSegmentArray(segmentCount); + + int segmentCapacity = initialCapacity / segmentCount; + if (segmentCapacity * segmentCount < initialCapacity) { + ++segmentCapacity; + } + + int segmentSize = 1; + while (segmentSize < segmentCapacity) { + segmentSize <<= 1; + } + + if (evictsBySize()) { + // Ensure sum of segment max weights = overall max weights + long maxSegmentWeight = maxWeight / segmentCount + 1; + long remainder = maxWeight % segmentCount; + for (int i = 0; i < this.segments.length; ++i) { + if (i == remainder) { + maxSegmentWeight--; + } + this.segments[i] = + createSegment(segmentSize, maxSegmentWeight, builder.getStatsCounterSupplier().get()); + } + } else { + for (int i = 0; i < this.segments.length; ++i) { + this.segments[i] = + createSegment(segmentSize, UNSET_INT, builder.getStatsCounterSupplier().get()); + } + } + } + + boolean evictsBySize() { + return maxWeight >= 0; + } + + boolean customWeigher() { + return weigher != OneWeigher.INSTANCE; + } + + boolean expires() { + return expiresAfterWrite() || expiresAfterAccess(); + } + + boolean expiresAfterWrite() { + return expireAfterWriteNanos > 0; + } + + boolean expiresAfterAccess() { + return expireAfterAccessNanos > 0; + } + + boolean refreshes() { + return refreshNanos > 0; + } + + boolean usesAccessQueue() { + return expiresAfterAccess() || evictsBySize(); + } + + boolean usesWriteQueue() { + return expiresAfterWrite(); + } + + boolean recordsWrite() { + return expiresAfterWrite() || refreshes(); + } + + boolean recordsAccess() { + return expiresAfterAccess(); + } + + boolean recordsTime() { + return recordsWrite() || recordsAccess(); + } + + boolean usesWriteEntries() { + return usesWriteQueue() || recordsWrite(); + } + + boolean usesAccessEntries() { + return usesAccessQueue() || recordsAccess(); + } + + boolean usesKeyReferences() { + return keyStrength != Strength.STRONG; + } + + boolean usesValueReferences() { + return valueStrength != Strength.STRONG; + } + + enum Strength { + /* + * TODO(kevinb): If we strongly reference the value and aren't loading, we needn't wrap the + * value. This could save ~8 bytes per entry. + */ + + STRONG { + @Override + ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight) { + return (weight == 1) + ? new StrongValueReference(value) + : new WeightedStrongValueReference(value, weight); + } + + @Override + Equivalence defaultEquivalence() { + return Equivalence.equals(); + } + }, + SOFT { + @Override + ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight) { + return (weight == 1) + ? new SoftValueReference(segment.valueReferenceQueue, value, entry) + : new WeightedSoftValueReference( + segment.valueReferenceQueue, value, entry, weight); + } + + @Override + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }, + WEAK { + @Override + ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight) { + return (weight == 1) + ? new WeakValueReference(segment.valueReferenceQueue, value, entry) + : new WeightedWeakValueReference( + segment.valueReferenceQueue, value, entry, weight); + } + + @Override + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }; + + /** Creates a reference for the given value according to this value strength. */ + abstract ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight); + + /** + * Returns the default equivalence strategy used to compare and hash keys or values referenced + * at this strength. This strategy will be used unless the user explicitly specifies an + * alternate strategy. + */ + abstract Equivalence defaultEquivalence(); + } + + /** Creates new entries. */ + enum EntryFactory { + STRONG { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongEntry<>(key, hash, next); + } + }, + STRONG_ACCESS { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongAccessEntry<>(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); + copyAccessEntry(original, newEntry); + return newEntry; + } + }, + STRONG_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongWriteEntry<>(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); + copyWriteEntry(original, newEntry); + return newEntry; + } + }, + STRONG_ACCESS_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongAccessWriteEntry<>(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); + copyAccessEntry(original, newEntry); + copyWriteEntry(original, newEntry); + return newEntry; + } + }, + WEAK { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakEntry<>(segment.keyReferenceQueue, key, hash, next); + } + }, + WEAK_ACCESS { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakAccessEntry<>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); + copyAccessEntry(original, newEntry); + return newEntry; + } + }, + WEAK_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakWriteEntry<>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); + copyWriteEntry(original, newEntry); + return newEntry; + } + }, + WEAK_ACCESS_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakAccessWriteEntry<>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, + ReferenceEntry original, + ReferenceEntry newNext, + K key) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext, key); + copyAccessEntry(original, newEntry); + copyWriteEntry(original, newEntry); + return newEntry; + } + }; + + // Masks used to compute indices in the following table. + + static final int ACCESS_MASK = 1; + static final int WRITE_MASK = 2; + static final int WEAK_MASK = 4; + + /** Look-up table for factories. */ + static final EntryFactory[] factories = { + STRONG, + STRONG_ACCESS, + STRONG_WRITE, + STRONG_ACCESS_WRITE, + WEAK, + WEAK_ACCESS, + WEAK_WRITE, + WEAK_ACCESS_WRITE, + }; + + static EntryFactory getFactory( + Strength keyStrength, boolean usesAccessQueue, boolean usesWriteQueue) { + int flags = + ((keyStrength == Strength.WEAK) ? WEAK_MASK : 0) + | (usesAccessQueue ? ACCESS_MASK : 0) + | (usesWriteQueue ? WRITE_MASK : 0); + return factories[flags]; + } + + /** + * Creates a new entry. + * + * @param segment to create the entry for + * @param key of the entry + * @param hash of the key + * @param next entry in the same bucket + */ + abstract ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next); + + /** + * Copies an entry, assigning it a new {@code next} entry. + * + * @param original the entry to copy. But avoid calling {@code getKey} on it: Instead, use the + * {@code key} parameter. That way, we prevent the key from being garbage collected in the + * case of weak keys. If we create a new entry with a key that is null at construction time, + * we're not sure if entry will necessarily ever be garbage collected. + * @param newNext entry in the same bucket + * @param key the key to copy from the original entry to the new one. Use this in preference to + * {@code original.getKey()}. + */ + // Guarded By Segment.this + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext, K key) { + return newEntry(segment, key, original.getHash(), newNext); + } + + // Guarded By Segment.this + void copyAccessEntry(ReferenceEntry original, ReferenceEntry newEntry) { + // TODO(fry): when we link values instead of entries this method can go + // away, as can connectAccessOrder, nullifyAccessOrder. + newEntry.setAccessTime(original.getAccessTime()); + + connectAccessOrder(original.getPreviousInAccessQueue(), newEntry); + connectAccessOrder(newEntry, original.getNextInAccessQueue()); + + nullifyAccessOrder(original); + } + + // Guarded By Segment.this + void copyWriteEntry(ReferenceEntry original, ReferenceEntry newEntry) { + // TODO(fry): when we link values instead of entries this method can go + // away, as can connectWriteOrder, nullifyWriteOrder. + newEntry.setWriteTime(original.getWriteTime()); + + connectWriteOrder(original.getPreviousInWriteQueue(), newEntry); + connectWriteOrder(newEntry, original.getNextInWriteQueue()); + + nullifyWriteOrder(original); + } + } + + /** A reference to a value. */ + interface ValueReference { + /** Returns the value. Does not block or throw exceptions. */ + V get(); + + /** + * Waits for a value that may still be loading. Unlike get(), this method can block (in the case + * of FutureValueReference). + * + * @throws ExecutionException if the loading thread throws an exception + * @throws ExecutionError if the loading thread throws an error + */ + V waitForValue() throws ExecutionException; + + /** Returns the weight of this entry. This is assumed to be static between calls to setValue. */ + int getWeight(); + + /** + * Returns the entry associated with this value reference, or {@code null} if this value + * reference is independent of any entry. + */ + ReferenceEntry getEntry(); + + /** + * Creates a copy of this reference for the given entry. + * + *

{@code value} may be null only for a loading reference. + */ + ValueReference copyFor(ReferenceQueue queue, V value, ReferenceEntry entry); + + /** + * Notify pending loads that a new value was set. This is only relevant to loading value + * references. + */ + void notifyNewValue(V newValue); + + /** + * Returns true if a new value is currently loading, regardless of whether there is an existing + * value. It is assumed that the return value of this method is constant for any given + * ValueReference instance. + */ + boolean isLoading(); + + /** + * Returns true if this reference contains an active value, meaning one that is still considered + * present in the cache. Active values consist of live values, which are returned by cache + * lookups, and dead values, which have been evicted but awaiting removal. Non-active values + * consist strictly of loading values, though during refresh a value may be both active and + * loading. + */ + boolean isActive(); + } + + /** Placeholder. Indicates that the value hasn't been set yet. */ + static final ValueReference UNSET = + new ValueReference() { + @Override + public Object get() { + return null; + } + + @Override + public int getWeight() { + return 0; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, Object value, ReferenceEntry entry) { + return this; + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public Object waitForValue() { + return null; + } + + @Override + public void notifyNewValue(Object newValue) {} + }; + + /** Singleton placeholder that indicates a value is being loaded. */ + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + static ValueReference unset() { + return (ValueReference) UNSET; + } + + private enum NullEntry implements ReferenceEntry { + INSTANCE; + + @Override + public ValueReference getValueReference() { + return null; + } + + @Override + public void setValueReference(ValueReference valueReference) {} + + @Override + public ReferenceEntry getNext() { + return null; + } + + @Override + public int getHash() { + return 0; + } + + @Override + public Object getKey() { + return null; + } + + @Override + public long getAccessTime() { + return 0; + } + + @Override + public void setAccessTime(long time) {} + + @Override + public ReferenceEntry getNextInAccessQueue() { + return this; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) {} + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return this; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) {} + + @Override + public long getWriteTime() { + return 0; + } + + @Override + public void setWriteTime(long time) {} + + @Override + public ReferenceEntry getNextInWriteQueue() { + return this; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) {} + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return this; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) {} + } + + abstract static class AbstractReferenceEntry implements ReferenceEntry { + @Override + public ValueReference getValueReference() { + throw new UnsupportedOperationException(); + } + + @Override + public void setValueReference(ValueReference valueReference) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNext() { + throw new UnsupportedOperationException(); + } + + @Override + public int getHash() { + throw new UnsupportedOperationException(); + } + + @Override + public K getKey() { + throw new UnsupportedOperationException(); + } + + @Override + public long getAccessTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setAccessTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public long getWriteTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setWriteTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + } + + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + static ReferenceEntry nullEntry() { + return (ReferenceEntry) NullEntry.INSTANCE; + } + + static final Queue DISCARDING_QUEUE = + new AbstractQueue() { + @Override + public boolean offer(Object o) { + return true; + } + + @Override + public Object peek() { + return null; + } + + @Override + public Object poll() { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override + public Iterator iterator() { + return ImmutableSet.of().iterator(); + } + }; + + /** Queue that discards all elements. */ + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + static Queue discardingQueue() { + return (Queue) DISCARDING_QUEUE; + } + + /* + * Note: All of this duplicate code sucks, but it saves a lot of memory. If only Java had mixins! + * To maintain this code, make a change for the strong reference type. Then, cut and paste, and + * replace "Strong" with "Soft" or "Weak" within the pasted text. The primary difference is that + * strong entries store the key reference directly while soft and weak entries delegate to their + * respective superclasses. + */ + + /** Used for strongly-referenced keys. */ + static class StrongEntry extends AbstractReferenceEntry { + final K key; + + StrongEntry(K key, int hash, ReferenceEntry next) { + this.key = key; + this.hash = hash; + this.next = next; + } + + @Override + public K getKey() { + return this.key; + } + + // The code below is exactly the same for each entry type. + + final int hash; + final ReferenceEntry next; + volatile ValueReference valueReference = unset(); + + @Override + public ValueReference getValueReference() { + return valueReference; + } + + @Override + public void setValueReference(ValueReference valueReference) { + this.valueReference = valueReference; + } + + @Override + public int getHash() { + return hash; + } + + @Override + public ReferenceEntry getNext() { + return next; + } + } + + static final class StrongAccessEntry extends StrongEntry { + StrongAccessEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + // The code below is exactly the same for each access entry type. + + volatile long accessTime = Long.MAX_VALUE; + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + } + + static final class StrongWriteEntry extends StrongEntry { + StrongWriteEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + // The code below is exactly the same for each write entry type. + + volatile long writeTime = Long.MAX_VALUE; + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class StrongAccessWriteEntry extends StrongEntry { + StrongAccessWriteEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + // The code below is exactly the same for each access entry type. + + volatile long accessTime = Long.MAX_VALUE; + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + + // The code below is exactly the same for each write entry type. + + volatile long writeTime = Long.MAX_VALUE; + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + /** Used for weakly-referenced keys. */ + static class WeakEntry extends WeakReference implements ReferenceEntry { + WeakEntry(ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(key, queue); + this.hash = hash; + this.next = next; + } + + @Override + public K getKey() { + return get(); + } + + /* + * It'd be nice to get these for free from AbstractReferenceEntry, but we're already extending + * WeakReference. + */ + + // null access + + @Override + public long getAccessTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setAccessTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + // null write + + @Override + public long getWriteTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setWriteTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + // The code below is exactly the same for each entry type. + + final int hash; + final ReferenceEntry next; + volatile ValueReference valueReference = unset(); + + @Override + public ValueReference getValueReference() { + return valueReference; + } + + @Override + public void setValueReference(ValueReference valueReference) { + this.valueReference = valueReference; + } + + @Override + public int getHash() { + return hash; + } + + @Override + public ReferenceEntry getNext() { + return next; + } + } + + static final class WeakAccessEntry extends WeakEntry { + WeakAccessEntry(ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + // The code below is exactly the same for each access entry type. + + volatile long accessTime = Long.MAX_VALUE; + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + } + + static final class WeakWriteEntry extends WeakEntry { + WeakWriteEntry(ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + // The code below is exactly the same for each write entry type. + + volatile long writeTime = Long.MAX_VALUE; + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class WeakAccessWriteEntry extends WeakEntry { + WeakAccessWriteEntry(ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + // The code below is exactly the same for each access entry type. + + volatile long accessTime = Long.MAX_VALUE; + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + + // The code below is exactly the same for each write entry type. + + volatile long writeTime = Long.MAX_VALUE; + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + /** References a weak value. */ + static class WeakValueReference extends WeakReference implements ValueReference { + final ReferenceEntry entry; + + WeakValueReference(ReferenceQueue queue, V referent, ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + @Override + public int getWeight() { + return 1; + } + + @Override + public ReferenceEntry getEntry() { + return entry; + } + + @Override + public void notifyNewValue(V newValue) {} + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return new WeakValueReference<>(queue, value, entry); + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public V waitForValue() { + return get(); + } + } + + /** References a soft value. */ + static class SoftValueReference extends SoftReference implements ValueReference { + final ReferenceEntry entry; + + SoftValueReference(ReferenceQueue queue, V referent, ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + @Override + public int getWeight() { + return 1; + } + + @Override + public ReferenceEntry getEntry() { + return entry; + } + + @Override + public void notifyNewValue(V newValue) {} + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return new SoftValueReference<>(queue, value, entry); + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public V waitForValue() { + return get(); + } + } + + /** References a strong value. */ + static class StrongValueReference implements ValueReference { + final V referent; + + StrongValueReference(V referent) { + this.referent = referent; + } + + @Override + public V get() { + return referent; + } + + @Override + public int getWeight() { + return 1; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return this; + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public V waitForValue() { + return get(); + } + + @Override + public void notifyNewValue(V newValue) {} + } + + /** References a weak value. */ + static final class WeightedWeakValueReference extends WeakValueReference { + final int weight; + + WeightedWeakValueReference( + ReferenceQueue queue, V referent, ReferenceEntry entry, int weight) { + super(queue, referent, entry); + this.weight = weight; + } + + @Override + public int getWeight() { + return weight; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return new WeightedWeakValueReference<>(queue, value, entry, weight); + } + } + + /** References a soft value. */ + static final class WeightedSoftValueReference extends SoftValueReference { + final int weight; + + WeightedSoftValueReference( + ReferenceQueue queue, V referent, ReferenceEntry entry, int weight) { + super(queue, referent, entry); + this.weight = weight; + } + + @Override + public int getWeight() { + return weight; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return new WeightedSoftValueReference<>(queue, value, entry, weight); + } + } + + /** References a strong value. */ + static final class WeightedStrongValueReference extends StrongValueReference { + final int weight; + + WeightedStrongValueReference(V referent, int weight) { + super(referent); + this.weight = weight; + } + + @Override + public int getWeight() { + return weight; + } + } + + /** + * Applies a supplemental hash function to a given hash code, which defends against poor quality + * hash functions. This is critical when the concurrent hash map uses power-of-two length hash + * tables, that otherwise encounter collisions for hash codes that do not differ in lower or upper + * bits. + * + * @param h hash code + */ + static int rehash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + // TODO(kevinb): use Hashing/move this to Hashing? + h += (h << 15) ^ 0xffffcd7d; + h ^= (h >>> 10); + h += (h << 3); + h ^= (h >>> 6); + h += (h << 2) + (h << 14); + return h ^ (h >>> 16); + } + + /** + * This method is a convenience for testing. Code should call {@link Segment#newEntry} directly. + */ + @VisibleForTesting + ReferenceEntry newEntry(K key, int hash, ReferenceEntry next) { + Segment segment = segmentFor(hash); + segment.lock(); + try { + return segment.newEntry(key, hash, next); + } finally { + segment.unlock(); + } + } + + /** + * This method is a convenience for testing. Code should call {@link Segment#copyEntry} directly. + */ + // Guarded By Segment.this + @SuppressWarnings("GuardedBy") + @VisibleForTesting + ReferenceEntry copyEntry(ReferenceEntry original, ReferenceEntry newNext) { + int hash = original.getHash(); + return segmentFor(hash).copyEntry(original, newNext); + } + + /** + * This method is a convenience for testing. Code should call {@link Segment#setValue} instead. + */ + // Guarded By Segment.this + @VisibleForTesting + ValueReference newValueReference(ReferenceEntry entry, V value, int weight) { + int hash = entry.getHash(); + return valueStrength.referenceValue(segmentFor(hash), entry, checkNotNull(value), weight); + } + + int hash(Object key) { + int h = keyEquivalence.hash(key); + return rehash(h); + } + + void reclaimValue(ValueReference valueReference) { + ReferenceEntry entry = valueReference.getEntry(); + int hash = entry.getHash(); + segmentFor(hash).reclaimValue(entry.getKey(), hash, valueReference); + } + + void reclaimKey(ReferenceEntry entry) { + int hash = entry.getHash(); + segmentFor(hash).reclaimKey(entry, hash); + } + + /** + * This method is a convenience for testing. Code should call {@link Segment#getLiveValue} + * instead. + */ + @VisibleForTesting + boolean isLive(ReferenceEntry entry, long now) { + return segmentFor(entry.getHash()).getLiveValue(entry, now) != null; + } + + /** + * Returns the segment that should be used for a key with the given hash. + * + * @param hash the hash code for the key + * @return the segment + */ + Segment segmentFor(int hash) { + // TODO(fry): Lazily create segments? + return segments[(hash >>> segmentShift) & segmentMask]; + } + + Segment createSegment( + int initialCapacity, long maxSegmentWeight, StatsCounter statsCounter) { + return new Segment<>(this, initialCapacity, maxSegmentWeight, statsCounter); + } + + /** + * Gets the value from an entry. Returns null if the entry is invalid, partially-collected, + * loading, or expired. Unlike {@link Segment#getLiveValue} this method does not attempt to clean + * up stale entries. As such it should only be called outside a segment context, such as during + * iteration. + */ + V getLiveValue(ReferenceEntry entry, long now) { + if (entry.getKey() == null) { + return null; + } + V value = entry.getValueReference().get(); + if (value == null) { + return null; + } + + if (isExpired(entry, now)) { + return null; + } + return value; + } + + // expiration + + /** Returns true if the entry has expired. */ + boolean isExpired(ReferenceEntry entry, long now) { + checkNotNull(entry); + if (expiresAfterAccess() && (now - entry.getAccessTime() >= expireAfterAccessNanos)) { + return true; + } + if (expiresAfterWrite() && (now - entry.getWriteTime() >= expireAfterWriteNanos)) { + return true; + } + return false; + } + + // queues + + // Guarded By Segment.this + static void connectAccessOrder(ReferenceEntry previous, ReferenceEntry next) { + previous.setNextInAccessQueue(next); + next.setPreviousInAccessQueue(previous); + } + + // Guarded By Segment.this + static void nullifyAccessOrder(ReferenceEntry nulled) { + ReferenceEntry nullEntry = nullEntry(); + nulled.setNextInAccessQueue(nullEntry); + nulled.setPreviousInAccessQueue(nullEntry); + } + + // Guarded By Segment.this + static void connectWriteOrder(ReferenceEntry previous, ReferenceEntry next) { + previous.setNextInWriteQueue(next); + next.setPreviousInWriteQueue(previous); + } + + // Guarded By Segment.this + static void nullifyWriteOrder(ReferenceEntry nulled) { + ReferenceEntry nullEntry = nullEntry(); + nulled.setNextInWriteQueue(nullEntry); + nulled.setPreviousInWriteQueue(nullEntry); + } + + /** + * Notifies listeners that an entry has been automatically removed due to expiration, eviction, or + * eligibility for garbage collection. This should be called every time expireEntries or + * evictEntry is called (once the lock is released). + */ + void processPendingNotifications() { + RemovalNotification notification; + while ((notification = removalNotificationQueue.poll()) != null) { + try { + removalListener.onRemoval(notification); + } catch (Throwable e) { + logger.log(Level.WARNING, "Exception thrown by removal listener", e); + } + } + } + + @SuppressWarnings("unchecked") + final Segment[] newSegmentArray(int ssize) { + return new Segment[ssize]; + } + + // Inner Classes + + /** + * Segments are specialized versions of hash tables. This subclass inherits from ReentrantLock + * opportunistically, just to simplify some locking and avoid separate construction. + */ + @SuppressWarnings("serial") // This class is never serialized. + static class Segment extends ReentrantLock { + + /* + * TODO(fry): Consider copying variables (like evictsBySize) from outer class into this class. + * It will require more memory but will reduce indirection. + */ + + /* + * Segments maintain a table of entry lists that are ALWAYS kept in a consistent state, so can + * be read without locking. Next fields of nodes are immutable (final). All list additions are + * performed at the front of each bin. This makes it easy to check changes, and also fast to + * traverse. When nodes would otherwise be changed, new nodes are created to replace them. This + * works well for hash tables since the bin lists tend to be short. (The average length is less + * than two.) + * + * Read operations can thus proceed without locking, but rely on selected uses of volatiles to + * ensure that completed write operations performed by other threads are noticed. For most + * purposes, the "count" field, tracking the number of elements, serves as that volatile + * variable ensuring visibility. This is convenient because this field needs to be read in many + * read operations anyway: + * + * - All (unsynchronized) read operations must first read the "count" field, and should not look + * at table entries if it is 0. + * + * - All (synchronized) write operations should write to the "count" field after structurally + * changing any bin. The operations must not take any action that could even momentarily cause a + * concurrent read operation to see inconsistent data. This is made easier by the nature of the + * read operations in Map. For example, no operation can reveal that the table has grown but the + * threshold has not yet been updated, so there are no atomicity requirements for this with + * respect to reads. + * + * As a guide, all critical volatile reads and writes to the count field are marked in code + * comments. + */ + + final LocalCache map; + + /** The number of live elements in this segment's region. */ + volatile int count; + + /** The weight of the live elements in this segment's region. */ + long totalWeight; + + /** + * Number of updates that alter the size of the table. This is used during bulk-read methods to + * make sure they see a consistent snapshot: If modCounts change during a traversal of segments + * loading size or checking containsValue, then we might have an inconsistent view of state so + * (usually) must retry. + */ + int modCount; + + /** + * The table is expanded when its size exceeds this threshold. (The value of this field is + * always {@code (int) (capacity * 0.75)}.) + */ + int threshold; + + /** The per-segment table. */ + volatile AtomicReferenceArray> table; + + /** The maximum weight of this segment. UNSET_INT if there is no maximum. */ + final long maxSegmentWeight; + + /** + * The key reference queue contains entries whose keys have been garbage collected, and which + * need to be cleaned up internally. + */ + final ReferenceQueue keyReferenceQueue; + + /** + * The value reference queue contains value references whose values have been garbage collected, + * and which need to be cleaned up internally. + */ + final ReferenceQueue valueReferenceQueue; + + /** + * The recency queue is used to record which entries were accessed for updating the access + * list's ordering. It is drained as a batch operation when either the DRAIN_THRESHOLD is + * crossed or a write occurs on the segment. + */ + final Queue> recencyQueue; + + /** + * A counter of the number of reads since the last write, used to drain queues on a small + * fraction of read operations. + */ + final AtomicInteger readCount = new AtomicInteger(); + + /** + * A queue of elements currently in the map, ordered by write time. Elements are added to the + * tail of the queue on write. + */ + final Queue> writeQueue; + + /** + * A queue of elements currently in the map, ordered by access time. Elements are added to the + * tail of the queue on access (note that writes count as accesses). + */ + final Queue> accessQueue; + + /** Accumulates cache statistics. */ + final StatsCounter statsCounter; + + Segment( + LocalCache map, + int initialCapacity, + long maxSegmentWeight, + StatsCounter statsCounter) { + this.map = map; + this.maxSegmentWeight = maxSegmentWeight; + this.statsCounter = checkNotNull(statsCounter); + initTable(newEntryArray(initialCapacity)); + + keyReferenceQueue = map.usesKeyReferences() ? new ReferenceQueue<>() : null; + + valueReferenceQueue = map.usesValueReferences() ? new ReferenceQueue<>() : null; + + recencyQueue = + map.usesAccessQueue() ? new ConcurrentLinkedQueue<>() : LocalCache.discardingQueue(); + + writeQueue = map.usesWriteQueue() ? new WriteQueue<>() : LocalCache.discardingQueue(); + + accessQueue = map.usesAccessQueue() ? new AccessQueue<>() : LocalCache.discardingQueue(); + } + + AtomicReferenceArray> newEntryArray(int size) { + return new AtomicReferenceArray<>(size); + } + + void initTable(AtomicReferenceArray> newTable) { + this.threshold = newTable.length() * 3 / 4; // 0.75 + if (!map.customWeigher() && this.threshold == maxSegmentWeight) { + // prevent spurious expansion before eviction + this.threshold++; + } + this.table = newTable; + } + + ReferenceEntry newEntry(K key, int hash, ReferenceEntry next) { + return map.entryFactory.newEntry(this, checkNotNull(key), hash, next); + } + + /** + * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, + * or {@code null} if {@code original} was already garbage collected. + */ + ReferenceEntry copyEntry(ReferenceEntry original, ReferenceEntry newNext) { + K key = original.getKey(); + if (key == null) { + // key collected + return null; + } + + ValueReference valueReference = original.getValueReference(); + V value = valueReference.get(); + if ((value == null) && valueReference.isActive()) { + // value collected + return null; + } + + ReferenceEntry newEntry = map.entryFactory.copyEntry(this, original, newNext, key); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + return newEntry; + } + + /** Sets a new value of an entry. Adds newly created entries at the end of the access queue. */ + void setValue(ReferenceEntry entry, K key, V value, long now) { + ValueReference previous = entry.getValueReference(); + int weight = map.weigher.weigh(key, value); + checkState(weight >= 0, "Weights must be non-negative"); + + ValueReference valueReference = + map.valueStrength.referenceValue(this, entry, value, weight); + entry.setValueReference(valueReference); + recordWrite(entry, weight, now); + previous.notifyNewValue(value); + } + + // loading + + V get(K key, int hash, CacheLoader loader) throws ExecutionException { + checkNotNull(key); + checkNotNull(loader); + try { + if (count != 0) { // read-volatile + // don't call getLiveEntry, which would ignore loading values + ReferenceEntry e = getEntry(key, hash); + if (e != null) { + long now = map.ticker.read(); + V value = getLiveValue(e, now); + if (value != null) { + recordRead(e, now); + statsCounter.recordHits(1); + return scheduleRefresh(e, key, hash, value, now, loader); + } + ValueReference valueReference = e.getValueReference(); + if (valueReference.isLoading()) { + return waitForLoadingValue(e, key, valueReference); + } + } + } + + // at this point e is either null or expired; + return lockedGetOrLoad(key, hash, loader); + } catch (ExecutionException ee) { + Throwable cause = ee.getCause(); + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } else if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } + throw ee; + } finally { + postReadCleanup(); + } + } + + V get(Object key, int hash) { + try { + if (count != 0) { // read-volatile + long now = map.ticker.read(); + ReferenceEntry e = getLiveEntry(key, hash, now); + if (e == null) { + return null; + } + + V value = e.getValueReference().get(); + if (value != null) { + recordRead(e, now); + return scheduleRefresh(e, e.getKey(), hash, value, now, map.defaultLoader); + } + tryDrainReferenceQueues(); + } + return null; + } finally { + postReadCleanup(); + } + } + + V lockedGetOrLoad(K key, int hash, CacheLoader loader) throws ExecutionException { + ReferenceEntry e; + ValueReference valueReference = null; + LoadingValueReference loadingValueReference = null; + boolean createNewEntry = true; + + lock(); + try { + // re-read ticker once inside the lock + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + valueReference = e.getValueReference(); + if (valueReference.isLoading()) { + createNewEntry = false; + } else { + V value = valueReference.get(); + if (value == null) { + enqueueNotification( + entryKey, hash, value, valueReference.getWeight(), RemovalCause.COLLECTED); + } else if (map.isExpired(e, now)) { + // This is a duplicate check, as preWriteCleanup already purged expired + // entries, but let's accommodate an incorrect expiration queue. + enqueueNotification( + entryKey, hash, value, valueReference.getWeight(), RemovalCause.EXPIRED); + } else { + recordLockedRead(e, now); + statsCounter.recordHits(1); + // we were concurrent with loading; don't consider refresh + return value; + } + + // immediately reuse invalid entries + writeQueue.remove(e); + accessQueue.remove(e); + this.count = newCount; // write-volatile + } + break; + } + } + + if (createNewEntry) { + loadingValueReference = new LoadingValueReference<>(); + + if (e == null) { + e = newEntry(key, hash, first); + e.setValueReference(loadingValueReference); + table.set(index, e); + } else { + e.setValueReference(loadingValueReference); + } + } + } finally { + unlock(); + postWriteCleanup(); + } + + if (createNewEntry) { + try { + // Synchronizes on the entry to allow failing fast when a recursive load is + // detected. This may be circumvented when an entry is copied, but will fail fast most + // of the time. + synchronized (e) { + return loadSync(key, hash, loadingValueReference, loader); + } + } finally { + statsCounter.recordMisses(1); + } + } else { + // The entry already exists. Wait for loading. + return waitForLoadingValue(e, key, valueReference); + } + } + + V waitForLoadingValue(ReferenceEntry e, K key, ValueReference valueReference) + throws ExecutionException { + if (!valueReference.isLoading()) { + throw new AssertionError(); + } + + checkState(!Thread.holdsLock(e), "Recursive load of: %s", key); + // don't consider expiration as we're concurrent with loading + try { + V value = valueReference.waitForValue(); + if (value == null) { + throw new InvalidCacheLoadException("CacheLoader returned null for key " + key + "."); + } + // re-read ticker now that loading has completed + long now = map.ticker.read(); + recordRead(e, now); + return value; + } finally { + statsCounter.recordMisses(1); + } + } + + V compute(K key, int hash, BiFunction function) { + ReferenceEntry e; + ValueReference valueReference = null; + ComputingValueReference computingValueReference = null; + boolean createNewEntry = true; + V newValue; + + lock(); + try { + // re-read ticker once inside the lock + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + valueReference = e.getValueReference(); + if (map.isExpired(e, now)) { + // This is a duplicate check, as preWriteCleanup already purged expired + // entries, but let's accommodate an incorrect expiration queue. + enqueueNotification( + entryKey, + hash, + valueReference.get(), + valueReference.getWeight(), + RemovalCause.EXPIRED); + } + + // immediately reuse invalid entries + writeQueue.remove(e); + accessQueue.remove(e); + createNewEntry = false; + break; + } + } + + // note valueReference can be an existing value or even itself another loading value if + // the value for the key is already being computed. + computingValueReference = new ComputingValueReference<>(valueReference); + + if (e == null) { + createNewEntry = true; + e = newEntry(key, hash, first); + e.setValueReference(computingValueReference); + table.set(index, e); + } else { + e.setValueReference(computingValueReference); + } + + newValue = computingValueReference.compute(key, function); + if (newValue != null) { + if (valueReference != null && newValue == valueReference.get()) { + computingValueReference.set(newValue); + e.setValueReference(valueReference); + recordWrite(e, 0, now); // no change in weight + return newValue; + } + try { + return getAndRecordStats( + key, hash, computingValueReference, Futures.immediateFuture(newValue)); + } catch (ExecutionException exception) { + throw new AssertionError("impossible; Futures.immediateFuture can't throw"); + } + } else if (createNewEntry || valueReference.isLoading()) { + removeLoadingValue(key, hash, computingValueReference); + return null; + } else { + removeEntry(e, hash, RemovalCause.EXPLICIT); + return null; + } + } finally { + unlock(); + postWriteCleanup(); + } + } + + // at most one of loadSync/loadAsync may be called for any given LoadingValueReference + + V loadSync( + K key, + int hash, + LoadingValueReference loadingValueReference, + CacheLoader loader) + throws ExecutionException { + ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + return getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } + + ListenableFuture loadAsync( + final K key, + final int hash, + final LoadingValueReference loadingValueReference, + CacheLoader loader) { + final ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + loadingFuture.addListener( + () -> { + try { + getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown during refresh", t); + loadingValueReference.setException(t); + } + }, + directExecutor()); + return loadingFuture; + } + + /** Waits uninterruptibly for {@code newValue} to be loaded, and then records loading stats. */ + V getAndRecordStats( + K key, + int hash, + LoadingValueReference loadingValueReference, + ListenableFuture newValue) + throws ExecutionException { + V value = null; + try { + value = getUninterruptibly(newValue); + if (value == null) { + throw new InvalidCacheLoadException("CacheLoader returned null for key " + key + "."); + } + statsCounter.recordLoadSuccess(loadingValueReference.elapsedNanos()); + storeLoadedValue(key, hash, loadingValueReference, value); + return value; + } finally { + if (value == null) { + statsCounter.recordLoadException(loadingValueReference.elapsedNanos()); + removeLoadingValue(key, hash, loadingValueReference); + } + } + } + + V scheduleRefresh( + ReferenceEntry entry, + K key, + int hash, + V oldValue, + long now, + CacheLoader loader) { + if (map.refreshes() + && (now - entry.getWriteTime() > map.refreshNanos) + && !entry.getValueReference().isLoading()) { + V newValue = refresh(key, hash, loader, true); + if (newValue != null) { + return newValue; + } + } + return oldValue; + } + + /** + * Refreshes the value associated with {@code key}, unless another thread is already doing so. + * Returns the newly refreshed value associated with {@code key} if it was refreshed inline, or + * {@code null} if another thread is performing the refresh or if an error occurs during + * refresh. + */ + V refresh(K key, int hash, CacheLoader loader, boolean checkTime) { + final LoadingValueReference loadingValueReference = + insertLoadingValueReference(key, hash, checkTime); + if (loadingValueReference == null) { + return null; + } + + ListenableFuture result = loadAsync(key, hash, loadingValueReference, loader); + if (result.isDone()) { + try { + return Uninterruptibles.getUninterruptibly(result); + } catch (Throwable t) { + // don't let refresh exceptions propagate; error was already logged + } + } + return null; + } + + /** + * Returns a newly inserted {@code LoadingValueReference}, or null if the live value reference + * is already loading. + */ + LoadingValueReference insertLoadingValueReference( + final K key, final int hash, boolean checkTime) { + ReferenceEntry e = null; + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + // Look for an existing entry. + for (e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // We found an existing entry. + + ValueReference valueReference = e.getValueReference(); + if (valueReference.isLoading() + || (checkTime && (now - e.getWriteTime() < map.refreshNanos))) { + // refresh is a no-op if loading is pending + // if checkTime, we want to check *after* acquiring the lock if refresh still needs + // to be scheduled + return null; + } + + // continue returning old value while loading + ++modCount; + LoadingValueReference loadingValueReference = + new LoadingValueReference<>(valueReference); + e.setValueReference(loadingValueReference); + return loadingValueReference; + } + } + + ++modCount; + LoadingValueReference loadingValueReference = new LoadingValueReference<>(); + e = newEntry(key, hash, first); + e.setValueReference(loadingValueReference); + table.set(index, e); + return loadingValueReference; + } finally { + unlock(); + postWriteCleanup(); + } + } + + // reference queues, for garbage collection cleanup + + /** Cleanup collected entries when the lock is available. */ + void tryDrainReferenceQueues() { + if (tryLock()) { + try { + drainReferenceQueues(); + } finally { + unlock(); + } + } + } + + /** + * Drain the key and value reference queues, cleaning up internal entries containing garbage + * collected keys or values. + */ + void drainReferenceQueues() { + if (map.usesKeyReferences()) { + drainKeyReferenceQueue(); + } + if (map.usesValueReferences()) { + drainValueReferenceQueue(); + } + } + + void drainKeyReferenceQueue() { + Reference ref; + int i = 0; + while ((ref = keyReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + ReferenceEntry entry = (ReferenceEntry) ref; + map.reclaimKey(entry); + if (++i == DRAIN_MAX) { + break; + } + } + } + + void drainValueReferenceQueue() { + Reference ref; + int i = 0; + while ((ref = valueReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + ValueReference valueReference = (ValueReference) ref; + map.reclaimValue(valueReference); + if (++i == DRAIN_MAX) { + break; + } + } + } + + /** Clears all entries from the key and value reference queues. */ + void clearReferenceQueues() { + if (map.usesKeyReferences()) { + clearKeyReferenceQueue(); + } + if (map.usesValueReferences()) { + clearValueReferenceQueue(); + } + } + + void clearKeyReferenceQueue() { + while (keyReferenceQueue.poll() != null) {} + } + + void clearValueReferenceQueue() { + while (valueReferenceQueue.poll() != null) {} + } + + // recency queue, shared by expiration and eviction + + /** + * Records the relative order in which this read was performed by adding {@code entry} to the + * recency queue. At write-time, or when the queue is full past the threshold, the queue will be + * drained and the entries therein processed. + * + *

Note: locked reads should use {@link #recordLockedRead}. + */ + void recordRead(ReferenceEntry entry, long now) { + if (map.recordsAccess()) { + entry.setAccessTime(now); + } + recencyQueue.add(entry); + } + + /** + * Updates the eviction metadata that {@code entry} was just read. This currently amounts to + * adding {@code entry} to relevant eviction lists. + * + *

Note: this method should only be called under lock, as it directly manipulates the + * eviction queues. Unlocked reads should use {@link #recordRead}. + */ + void recordLockedRead(ReferenceEntry entry, long now) { + if (map.recordsAccess()) { + entry.setAccessTime(now); + } + accessQueue.add(entry); + } + + /** + * Updates eviction metadata that {@code entry} was just written. This currently amounts to + * adding {@code entry} to relevant eviction lists. + */ + void recordWrite(ReferenceEntry entry, int weight, long now) { + // we are already under lock, so drain the recency queue immediately + drainRecencyQueue(); + totalWeight += weight; + + if (map.recordsAccess()) { + entry.setAccessTime(now); + } + if (map.recordsWrite()) { + entry.setWriteTime(now); + } + accessQueue.add(entry); + writeQueue.add(entry); + } + + /** + * Drains the recency queue, updating eviction metadata that the entries therein were read in + * the specified relative order. This currently amounts to adding them to relevant eviction + * lists (accounting for the fact that they could have been removed from the map since being + * added to the recency queue). + */ + void drainRecencyQueue() { + ReferenceEntry e; + while ((e = recencyQueue.poll()) != null) { + // An entry may be in the recency queue despite it being removed from + // the map . This can occur when the entry was concurrently read while a + // writer is removing it from the segment or after a clear has removed + // all the segment's entries. + if (accessQueue.contains(e)) { + accessQueue.add(e); + } + } + } + + // expiration + + /** Cleanup expired entries when the lock is available. */ + void tryExpireEntries(long now) { + if (tryLock()) { + try { + expireEntries(now); + } finally { + unlock(); + // don't call postWriteCleanup as we're in a read + } + } + } + + void expireEntries(long now) { + drainRecencyQueue(); + + ReferenceEntry e; + while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) { + if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + while ((e = accessQueue.peek()) != null && map.isExpired(e, now)) { + if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + } + + // eviction + + void enqueueNotification(K key, int hash, V value, int weight, RemovalCause cause) { + totalWeight -= weight; + if (cause.wasEvicted()) { + statsCounter.recordEviction(); + } + if (map.removalNotificationQueue != DISCARDING_QUEUE) { + RemovalNotification notification = RemovalNotification.create(key, value, cause); + map.removalNotificationQueue.offer(notification); + } + } + + /** + * Performs eviction if the segment is over capacity. Avoids flushing the entire cache if the + * newest entry exceeds the maximum weight all on its own. + * + * @param newest the most recently added entry + */ + void evictEntries(ReferenceEntry newest) { + if (!map.evictsBySize()) { + return; + } + + drainRecencyQueue(); + + // If the newest entry by itself is too heavy for the segment, don't bother evicting + // anything else, just that + if (newest.getValueReference().getWeight() > maxSegmentWeight) { + if (!removeEntry(newest, newest.getHash(), RemovalCause.SIZE)) { + throw new AssertionError(); + } + } + + while (totalWeight > maxSegmentWeight) { + ReferenceEntry e = getNextEvictable(); + if (!removeEntry(e, e.getHash(), RemovalCause.SIZE)) { + throw new AssertionError(); + } + } + } + + // TODO(fry): instead implement this with an eviction head + + ReferenceEntry getNextEvictable() { + for (ReferenceEntry e : accessQueue) { + int weight = e.getValueReference().getWeight(); + if (weight > 0) { + return e; + } + } + throw new AssertionError(); + } + + /** Returns first entry of bin for given hash. */ + ReferenceEntry getFirst(int hash) { + // read this volatile field only once + AtomicReferenceArray> table = this.table; + return table.get(hash & (table.length() - 1)); + } + + // Specialized implementations of map methods + + ReferenceEntry getEntry(Object key, int hash) { + for (ReferenceEntry e = getFirst(hash); e != null; e = e.getNext()) { + if (e.getHash() != hash) { + continue; + } + + K entryKey = e.getKey(); + if (entryKey == null) { + tryDrainReferenceQueues(); + continue; + } + + if (map.keyEquivalence.equivalent(key, entryKey)) { + return e; + } + } + + return null; + } + + ReferenceEntry getLiveEntry(Object key, int hash, long now) { + ReferenceEntry e = getEntry(key, hash); + if (e == null) { + return null; + } else if (map.isExpired(e, now)) { + tryExpireEntries(now); + return null; + } + return e; + } + + /** + * Gets the value from an entry. Returns null if the entry is invalid, partially-collected, + * loading, or expired. + */ + V getLiveValue(ReferenceEntry entry, long now) { + if (entry.getKey() == null) { + tryDrainReferenceQueues(); + return null; + } + V value = entry.getValueReference().get(); + if (value == null) { + tryDrainReferenceQueues(); + return null; + } + + if (map.isExpired(entry, now)) { + tryExpireEntries(now); + return null; + } + return value; + } + + boolean containsKey(Object key, int hash) { + try { + if (count != 0) { // read-volatile + long now = map.ticker.read(); + ReferenceEntry e = getLiveEntry(key, hash, now); + if (e == null) { + return false; + } + return e.getValueReference().get() != null; + } + + return false; + } finally { + postReadCleanup(); + } + } + + /** + * This method is a convenience for testing. Code should call {@link LocalCache#containsValue} + * directly. + */ + @VisibleForTesting + boolean containsValue(Object value) { + try { + if (count != 0) { // read-volatile + long now = map.ticker.read(); + AtomicReferenceArray> table = this.table; + int length = table.length(); + for (int i = 0; i < length; ++i) { + for (ReferenceEntry e = table.get(i); e != null; e = e.getNext()) { + V entryValue = getLiveValue(e, now); + if (entryValue == null) { + continue; + } + if (map.valueEquivalence.equivalent(value, entryValue)) { + return true; + } + } + } + } + + return false; + } finally { + postReadCleanup(); + } + } + + V put(K key, int hash, V value, boolean onlyIfAbsent) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count + 1; + if (newCount > this.threshold) { // ensure capacity + expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + // Look for an existing entry. + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // We found an existing entry. + + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + if (entryValue == null) { + ++modCount; + if (valueReference.isActive()) { + enqueueNotification( + key, hash, entryValue, valueReference.getWeight(), RemovalCause.COLLECTED); + setValue(e, key, value, now); + newCount = this.count; // count remains unchanged + } else { + setValue(e, key, value, now); + newCount = this.count + 1; + } + this.count = newCount; // write-volatile + evictEntries(e); + return null; + } else if (onlyIfAbsent) { + // Mimic + // "if (!map.containsKey(key)) ... + // else return map.get(key); + recordLockedRead(e, now); + return entryValue; + } else { + // clobber existing entry, count remains unchanged + ++modCount; + enqueueNotification( + key, hash, entryValue, valueReference.getWeight(), RemovalCause.REPLACED); + setValue(e, key, value, now); + evictEntries(e); + return entryValue; + } + } + } + + // Create a new entry. + ++modCount; + ReferenceEntry newEntry = newEntry(key, hash, first); + setValue(newEntry, key, value, now); + table.set(index, newEntry); + newCount = this.count + 1; + this.count = newCount; // write-volatile + evictEntries(newEntry); + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + /** Expands the table if possible. */ + void expand() { + AtomicReferenceArray> oldTable = table; + int oldCapacity = oldTable.length(); + if (oldCapacity >= MAXIMUM_CAPACITY) { + return; + } + + /* + * Reclassify nodes in each list to new Map. Because we are using power-of-two expansion, the + * elements from each bin must either stay at same index, or move with a power of two offset. + * We eliminate unnecessary node creation by catching cases where old nodes can be reused + * because their next fields won't change. Statistically, at the default threshold, only about + * one-sixth of them need cloning when a table doubles. The nodes they replace will be garbage + * collectable as soon as they are no longer referenced by any reader thread that may be in + * the midst of traversing table right now. + */ + + int newCount = count; + AtomicReferenceArray> newTable = newEntryArray(oldCapacity << 1); + threshold = newTable.length() * 3 / 4; + int newMask = newTable.length() - 1; + for (int oldIndex = 0; oldIndex < oldCapacity; ++oldIndex) { + // We need to guarantee that any existing reads of old Map can + // proceed. So we cannot yet null out each bin. + ReferenceEntry head = oldTable.get(oldIndex); + + if (head != null) { + ReferenceEntry next = head.getNext(); + int headIndex = head.getHash() & newMask; + + // Single node on list + if (next == null) { + newTable.set(headIndex, head); + } else { + // Reuse the consecutive sequence of nodes with the same target + // index from the end of the list. tail points to the first + // entry in the reusable list. + ReferenceEntry tail = head; + int tailIndex = headIndex; + for (ReferenceEntry e = next; e != null; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + if (newIndex != tailIndex) { + // The index changed. We'll need to copy the previous entry. + tailIndex = newIndex; + tail = e; + } + } + newTable.set(tailIndex, tail); + + // Clone nodes leading up to the tail. + for (ReferenceEntry e = head; e != tail; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + ReferenceEntry newNext = newTable.get(newIndex); + ReferenceEntry newFirst = copyEntry(e, newNext); + if (newFirst != null) { + newTable.set(newIndex, newFirst); + } else { + removeCollectedEntry(e); + newCount--; + } + } + } + } + } + table = newTable; + this.count = newCount; + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + if (entryValue == null) { + if (valueReference.isActive()) { + // If the value disappeared, this entry is partially collected. + int newCount = this.count - 1; + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain( + first, + e, + entryKey, + hash, + entryValue, + valueReference, + RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return false; + } + + if (map.valueEquivalence.equivalent(oldValue, entryValue)) { + ++modCount; + enqueueNotification( + key, hash, entryValue, valueReference.getWeight(), RemovalCause.REPLACED); + setValue(e, key, newValue, now); + evictEntries(e); + return true; + } else { + // Mimic + // "if (map.containsKey(key) && map.get(key).equals(oldValue))..." + recordLockedRead(e, now); + return false; + } + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + V replace(K key, int hash, V newValue) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + if (entryValue == null) { + if (valueReference.isActive()) { + // If the value disappeared, this entry is partially collected. + int newCount = this.count - 1; + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain( + first, + e, + entryKey, + hash, + entryValue, + valueReference, + RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return null; + } + + ++modCount; + enqueueNotification( + key, hash, entryValue, valueReference.getWeight(), RemovalCause.REPLACED); + setValue(e, key, newValue, now); + evictEntries(e); + return entryValue; + } + } + + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + V remove(Object key, int hash) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + RemovalCause cause; + if (entryValue != null) { + cause = RemovalCause.EXPLICIT; + } else if (valueReference.isActive()) { + cause = RemovalCause.COLLECTED; + } else { + // currently loading + return null; + } + + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain(first, e, entryKey, hash, entryValue, valueReference, cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return entryValue; + } + } + + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + boolean remove(Object key, int hash, Object value) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + RemovalCause cause; + if (map.valueEquivalence.equivalent(value, entryValue)) { + cause = RemovalCause.EXPLICIT; + } else if (entryValue == null && valueReference.isActive()) { + cause = RemovalCause.COLLECTED; + } else { + // currently loading + return false; + } + + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain(first, e, entryKey, hash, entryValue, valueReference, cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return (cause == RemovalCause.EXPLICIT); + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + boolean storeLoadedValue( + K key, int hash, LoadingValueReference oldValueReference, V newValue) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count + 1; + if (newCount > this.threshold) { // ensure capacity + expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + // replace the old LoadingValueReference if it's live, otherwise + // perform a putIfAbsent + if (oldValueReference == valueReference + || (entryValue == null && valueReference != UNSET)) { + ++modCount; + if (oldValueReference.isActive()) { + RemovalCause cause = + (entryValue == null) ? RemovalCause.COLLECTED : RemovalCause.REPLACED; + enqueueNotification(key, hash, entryValue, oldValueReference.getWeight(), cause); + newCount--; + } + setValue(e, key, newValue, now); + this.count = newCount; // write-volatile + evictEntries(e); + return true; + } + + // the loaded value was already clobbered + enqueueNotification(key, hash, newValue, 0, RemovalCause.REPLACED); + return false; + } + } + + ++modCount; + ReferenceEntry newEntry = newEntry(key, hash, first); + setValue(newEntry, key, newValue, now); + table.set(index, newEntry); + this.count = newCount; // write-volatile + evictEntries(newEntry); + return true; + } finally { + unlock(); + postWriteCleanup(); + } + } + + void clear() { + if (count != 0) { // read-volatile + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + for (int i = 0; i < table.length(); ++i) { + for (ReferenceEntry e = table.get(i); e != null; e = e.getNext()) { + // Loading references aren't actually in the map yet. + if (e.getValueReference().isActive()) { + K key = e.getKey(); + V value = e.getValueReference().get(); + RemovalCause cause = + (key == null || value == null) ? RemovalCause.COLLECTED : RemovalCause.EXPLICIT; + enqueueNotification( + key, e.getHash(), value, e.getValueReference().getWeight(), cause); + } + } + } + for (int i = 0; i < table.length(); ++i) { + table.set(i, null); + } + clearReferenceQueues(); + writeQueue.clear(); + accessQueue.clear(); + readCount.set(0); + + ++modCount; + count = 0; // write-volatile + } finally { + unlock(); + postWriteCleanup(); + } + } + } + + ReferenceEntry removeValueFromChain( + ReferenceEntry first, + ReferenceEntry entry, + K key, + int hash, + V value, + ValueReference valueReference, + RemovalCause cause) { + enqueueNotification(key, hash, value, valueReference.getWeight(), cause); + writeQueue.remove(entry); + accessQueue.remove(entry); + + if (valueReference.isLoading()) { + valueReference.notifyNewValue(null); + return first; + } else { + return removeEntryFromChain(first, entry); + } + } + + ReferenceEntry removeEntryFromChain( + ReferenceEntry first, ReferenceEntry entry) { + int newCount = count; + ReferenceEntry newFirst = entry.getNext(); + for (ReferenceEntry e = first; e != entry; e = e.getNext()) { + ReferenceEntry next = copyEntry(e, newFirst); + if (next != null) { + newFirst = next; + } else { + removeCollectedEntry(e); + newCount--; + } + } + this.count = newCount; + return newFirst; + } + + void removeCollectedEntry(ReferenceEntry entry) { + enqueueNotification( + entry.getKey(), + entry.getHash(), + entry.getValueReference().get(), + entry.getValueReference().getWeight(), + RemovalCause.COLLECTED); + writeQueue.remove(entry); + accessQueue.remove(entry); + } + + /** Removes an entry whose key has been garbage collected. */ + boolean reclaimKey(ReferenceEntry entry, int hash) { + lock(); + try { + int newCount = count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain( + first, + e, + e.getKey(), + hash, + e.getValueReference().get(), + e.getValueReference(), + RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + /** Removes an entry whose value has been garbage collected. */ + boolean reclaimValue(K key, int hash, ValueReference valueReference) { + lock(); + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference v = e.getValueReference(); + if (v == valueReference) { + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain( + first, + e, + entryKey, + hash, + valueReference.get(), + valueReference, + RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + if (!isHeldByCurrentThread()) { // don't clean up inside of put + postWriteCleanup(); + } + } + } + + boolean removeLoadingValue(K key, int hash, LoadingValueReference valueReference) { + lock(); + try { + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference v = e.getValueReference(); + if (v == valueReference) { + if (valueReference.isActive()) { + e.setValueReference(valueReference.getOldValue()); + } else { + ReferenceEntry newFirst = removeEntryFromChain(first, e); + table.set(index, newFirst); + } + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + @VisibleForTesting + boolean removeEntry(ReferenceEntry entry, int hash, RemovalCause cause) { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + ReferenceEntry newFirst = + removeValueFromChain( + first, + e, + e.getKey(), + hash, + e.getValueReference().get(), + e.getValueReference(), + cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } + + /** + * Performs routine cleanup following a read. Normally cleanup happens during writes. If cleanup + * is not observed after a sufficient number of reads, try cleaning up from the read thread. + */ + void postReadCleanup() { + if ((readCount.incrementAndGet() & DRAIN_THRESHOLD) == 0) { + cleanUp(); + } + } + + /** + * Performs routine cleanup prior to executing a write. This should be called every time a write + * thread acquires the segment lock, immediately after acquiring the lock. + * + *

Post-condition: expireEntries has been run. + */ + void preWriteCleanup(long now) { + runLockedCleanup(now); + } + + /** Performs routine cleanup following a write. */ + void postWriteCleanup() { + runUnlockedCleanup(); + } + + void cleanUp() { + long now = map.ticker.read(); + runLockedCleanup(now); + runUnlockedCleanup(); + } + + void runLockedCleanup(long now) { + if (tryLock()) { + try { + drainReferenceQueues(); + expireEntries(now); // calls drainRecencyQueue + readCount.set(0); + } finally { + unlock(); + } + } + } + + void runUnlockedCleanup() { + // locked cleanup may generate notifications we can send unlocked + if (!isHeldByCurrentThread()) { + map.processPendingNotifications(); + } + } + } + + static class LoadingValueReference implements ValueReference { + volatile ValueReference oldValue; + + // TODO(fry): rename get, then extend AbstractFuture instead of containing SettableFuture + final SettableFuture futureValue = SettableFuture.create(); + final Stopwatch stopwatch = Stopwatch.createUnstarted(); + + public LoadingValueReference() { + this(null); + } + + public LoadingValueReference(ValueReference oldValue) { + this.oldValue = (oldValue == null) ? LocalCache.unset() : oldValue; + } + + @Override + public boolean isLoading() { + return true; + } + + @Override + public boolean isActive() { + return oldValue.isActive(); + } + + @Override + public int getWeight() { + return oldValue.getWeight(); + } + + public boolean set(V newValue) { + return futureValue.set(newValue); + } + + public boolean setException(Throwable t) { + return futureValue.setException(t); + } + + private ListenableFuture fullyFailedFuture(Throwable t) { + return Futures.immediateFailedFuture(t); + } + + @Override + public void notifyNewValue(V newValue) { + if (newValue != null) { + // The pending load was clobbered by a manual write. + // Unblock all pending gets, and have them return the new value. + set(newValue); + } else { + // The pending load was removed. Delay notifications until loading completes. + oldValue = unset(); + } + + // TODO(fry): could also cancel loading if we had a handle on its future + } + + public ListenableFuture loadFuture(K key, CacheLoader loader) { + try { + stopwatch.start(); + V previousValue = oldValue.get(); + if (previousValue == null) { + V newValue = loader.load(key); + return set(newValue) ? futureValue : Futures.immediateFuture(newValue); + } + ListenableFuture newValue = loader.reload(key, previousValue); + if (newValue == null) { + return Futures.immediateFuture(null); + } + // To avoid a race, make sure the refreshed value is set into loadingValueReference + // *before* returning newValue from the cache query. + return transform( + newValue, + newResult -> { + LoadingValueReference.this.set(newResult); + return newResult; + }, + directExecutor()); + } catch (Throwable t) { + ListenableFuture result = setException(t) ? futureValue : fullyFailedFuture(t); + if (t instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + return result; + } + } + + public V compute(K key, BiFunction function) { + stopwatch.start(); + V previousValue; + try { + previousValue = oldValue.waitForValue(); + } catch (ExecutionException e) { + previousValue = null; + } + V newValue; + try { + newValue = function.apply(key, previousValue); + } catch (Throwable th) { + this.setException(th); + throw th; + } + this.set(newValue); + return newValue; + } + + public long elapsedNanos() { + return stopwatch.elapsed(NANOSECONDS); + } + + @Override + public V waitForValue() throws ExecutionException { + return getUninterruptibly(futureValue); + } + + @Override + public V get() { + return oldValue.get(); + } + + public ValueReference getOldValue() { + return oldValue; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return this; + } + } + + static class ComputingValueReference extends LoadingValueReference { + ComputingValueReference(ValueReference oldValue) { + super(oldValue); + } + + @Override + public boolean isLoading() { + return false; + } + } + + // Queues + + /** + * A custom queue for managing eviction order. Note that this is tightly integrated with {@code + * ReferenceEntry}, upon which it relies to perform its linking. + * + *

Note that this entire implementation makes the assumption that all elements which are in the + * map are also in this queue, and that all elements not in the queue are not in the map. + * + *

The benefits of creating our own queue are that (1) we can replace elements in the middle of + * the queue as part of copyWriteEntry, and (2) the contains method is highly optimized for the + * current model. + */ + static final class WriteQueue extends AbstractQueue> { + final ReferenceEntry head = + new AbstractReferenceEntry() { + + @Override + public long getWriteTime() { + return Long.MAX_VALUE; + } + + @Override + public void setWriteTime(long time) {} + + ReferenceEntry nextWrite = this; + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + ReferenceEntry previousWrite = this; + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + }; + + // implements Queue + + @Override + public boolean offer(ReferenceEntry entry) { + // unlink + connectWriteOrder(entry.getPreviousInWriteQueue(), entry.getNextInWriteQueue()); + + // add to tail + connectWriteOrder(head.getPreviousInWriteQueue(), entry); + connectWriteOrder(entry, head); + + return true; + } + + @Override + public ReferenceEntry peek() { + ReferenceEntry next = head.getNextInWriteQueue(); + return (next == head) ? null : next; + } + + @Override + public ReferenceEntry poll() { + ReferenceEntry next = head.getNextInWriteQueue(); + if (next == head) { + return null; + } + + remove(next); + return next; + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + ReferenceEntry previous = e.getPreviousInWriteQueue(); + ReferenceEntry next = e.getNextInWriteQueue(); + connectWriteOrder(previous, next); + nullifyWriteOrder(e); + + return next != NullEntry.INSTANCE; + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + return e.getNextInWriteQueue() != NullEntry.INSTANCE; + } + + @Override + public boolean isEmpty() { + return head.getNextInWriteQueue() == head; + } + + @Override + public int size() { + int size = 0; + for (ReferenceEntry e = head.getNextInWriteQueue(); + e != head; + e = e.getNextInWriteQueue()) { + size++; + } + return size; + } + + @Override + public void clear() { + ReferenceEntry e = head.getNextInWriteQueue(); + while (e != head) { + ReferenceEntry next = e.getNextInWriteQueue(); + nullifyWriteOrder(e); + e = next; + } + + head.setNextInWriteQueue(head); + head.setPreviousInWriteQueue(head); + } + + @Override + public Iterator> iterator() { + return new AbstractSequentialIterator>(peek()) { + @Override + protected ReferenceEntry computeNext(ReferenceEntry previous) { + ReferenceEntry next = previous.getNextInWriteQueue(); + return (next == head) ? null : next; + } + }; + } + } + + /** + * A custom queue for managing access order. Note that this is tightly integrated with {@code + * ReferenceEntry}, upon which it relies to perform its linking. + * + *

Note that this entire implementation makes the assumption that all elements which are in the + * map are also in this queue, and that all elements not in the queue are not in the map. + * + *

The benefits of creating our own queue are that (1) we can replace elements in the middle of + * the queue as part of copyWriteEntry, and (2) the contains method is highly optimized for the + * current model. + */ + static final class AccessQueue extends AbstractQueue> { + final ReferenceEntry head = + new AbstractReferenceEntry() { + + @Override + public long getAccessTime() { + return Long.MAX_VALUE; + } + + @Override + public void setAccessTime(long time) {} + + ReferenceEntry nextAccess = this; + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + ReferenceEntry previousAccess = this; + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + }; + + // implements Queue + + @Override + public boolean offer(ReferenceEntry entry) { + // unlink + connectAccessOrder(entry.getPreviousInAccessQueue(), entry.getNextInAccessQueue()); + + // add to tail + connectAccessOrder(head.getPreviousInAccessQueue(), entry); + connectAccessOrder(entry, head); + + return true; + } + + @Override + public ReferenceEntry peek() { + ReferenceEntry next = head.getNextInAccessQueue(); + return (next == head) ? null : next; + } + + @Override + public ReferenceEntry poll() { + ReferenceEntry next = head.getNextInAccessQueue(); + if (next == head) { + return null; + } + + remove(next); + return next; + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + ReferenceEntry previous = e.getPreviousInAccessQueue(); + ReferenceEntry next = e.getNextInAccessQueue(); + connectAccessOrder(previous, next); + nullifyAccessOrder(e); + + return next != NullEntry.INSTANCE; + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + return e.getNextInAccessQueue() != NullEntry.INSTANCE; + } + + @Override + public boolean isEmpty() { + return head.getNextInAccessQueue() == head; + } + + @Override + public int size() { + int size = 0; + for (ReferenceEntry e = head.getNextInAccessQueue(); + e != head; + e = e.getNextInAccessQueue()) { + size++; + } + return size; + } + + @Override + public void clear() { + ReferenceEntry e = head.getNextInAccessQueue(); + while (e != head) { + ReferenceEntry next = e.getNextInAccessQueue(); + nullifyAccessOrder(e); + e = next; + } + + head.setNextInAccessQueue(head); + head.setPreviousInAccessQueue(head); + } + + @Override + public Iterator> iterator() { + return new AbstractSequentialIterator>(peek()) { + @Override + protected ReferenceEntry computeNext(ReferenceEntry previous) { + ReferenceEntry next = previous.getNextInAccessQueue(); + return (next == head) ? null : next; + } + }; + } + } + + // Cache support + + public void cleanUp() { + for (Segment segment : segments) { + segment.cleanUp(); + } + } + + // ConcurrentMap methods + + @Override + public boolean isEmpty() { + /* + * Sum per-segment modCounts to avoid mis-reporting when elements are concurrently added and + * removed in one segment while checking another, in which case the table was never actually + * empty at any point. (The sum ensures accuracy up through at least 1<<31 per-segment + * modifications before recheck.) Method containsValue() uses similar constructions for + * stability checks. + */ + long sum = 0L; + Segment[] segments = this.segments; + for (Segment segment : segments) { + if (segment.count != 0) { + return false; + } + sum += segment.modCount; + } + + if (sum != 0L) { // recheck unless no modifications + for (Segment segment : segments) { + if (segment.count != 0) { + return false; + } + sum -= segment.modCount; + } + return sum == 0L; + } + return true; + } + + long longSize() { + Segment[] segments = this.segments; + long sum = 0; + for (Segment segment : segments) { + sum += segment.count; + } + return sum; + } + + @Override + public int size() { + return Ints.saturatedCast(longSize()); + } + + // TODO(b/27479612): consider removing this + @Override + public V get(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).get(key, hash); + } + + // TODO(b/27479612): consider removing this + V get(K key, CacheLoader loader) throws ExecutionException { + int hash = hash(checkNotNull(key)); + return segmentFor(hash).get(key, hash, loader); + } + + public V getIfPresent(Object key) { + int hash = hash(checkNotNull(key)); + V value = segmentFor(hash).get(key, hash); + if (value == null) { + globalStatsCounter.recordMisses(1); + } else { + globalStatsCounter.recordHits(1); + } + return value; + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + V result = get(key); + return (result != null) ? result : defaultValue; + } + + V getOrLoad(K key) throws ExecutionException { + return get(key, defaultLoader); + } + + ImmutableMap getAllPresent(Iterable keys) { + int hits = 0; + int misses = 0; + + ImmutableMap.Builder result = ImmutableMap.builder(); + for (Object key : keys) { + V value = get(key); + if (value == null) { + misses++; + } else { + // TODO(fry): store entry key instead of query key + @SuppressWarnings("unchecked") + K castKey = (K) key; + result.put(castKey, value); + hits++; + } + } + globalStatsCounter.recordHits(hits); + globalStatsCounter.recordMisses(misses); + return result.buildKeepingLast(); + } + + ImmutableMap getAll(Iterable keys) throws ExecutionException { + int hits = 0; + int misses = 0; + + Map result = Maps.newLinkedHashMap(); + Set keysToLoad = Sets.newLinkedHashSet(); + for (K key : keys) { + V value = get(key); + if (!result.containsKey(key)) { + result.put(key, value); + if (value == null) { + misses++; + keysToLoad.add(key); + } else { + hits++; + } + } + } + + try { + if (!keysToLoad.isEmpty()) { + try { + Map newEntries = loadAll(unmodifiableSet(keysToLoad), defaultLoader); + for (K key : keysToLoad) { + V value = newEntries.get(key); + if (value == null) { + throw new InvalidCacheLoadException("loadAll failed to return a value for " + key); + } + result.put(key, value); + } + } catch (UnsupportedLoadingOperationException e) { + // loadAll not implemented, fallback to load + for (K key : keysToLoad) { + misses--; // get will count this miss + result.put(key, get(key, defaultLoader)); + } + } + } + return ImmutableMap.copyOf(result); + } finally { + globalStatsCounter.recordHits(hits); + globalStatsCounter.recordMisses(misses); + } + } + + /** + * Returns the result of calling {@link CacheLoader#loadAll}, or null if {@code loader} doesn't + * implement {@code loadAll}. + */ + Map loadAll(Set keys, CacheLoader loader) + throws ExecutionException { + checkNotNull(loader); + checkNotNull(keys); + Stopwatch stopwatch = Stopwatch.createStarted(); + Map result; + boolean success = false; + try { + @SuppressWarnings("unchecked") // safe since all keys extend K + Map map = (Map) loader.loadAll(keys); + result = map; + success = true; + } catch (UnsupportedLoadingOperationException e) { + success = true; + throw e; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new ExecutionException(e); + } catch (RuntimeException e) { + throw new UncheckedExecutionException(e); + } catch (Exception e) { + throw new ExecutionException(e); + } catch (Error e) { + throw new ExecutionError(e); + } finally { + if (!success) { + globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS)); + } + } + + if (result == null) { + globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS)); + throw new InvalidCacheLoadException(loader + " returned null map from loadAll"); + } + + stopwatch.stop(); + // TODO(fry): batch by segment + boolean nullsPresent = false; + for (Entry entry : result.entrySet()) { + K key = entry.getKey(); + V value = entry.getValue(); + if (key == null || value == null) { + // delay failure until non-null entries are stored + nullsPresent = true; + } else { + put(key, value); + } + } + + if (nullsPresent) { + globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS)); + throw new InvalidCacheLoadException(loader + " returned null keys or values from loadAll"); + } + + // TODO(fry): record count of loaded entries + globalStatsCounter.recordLoadSuccess(stopwatch.elapsed(NANOSECONDS)); + return result; + } + + /** + * Returns the internal entry for the specified key. The entry may be loading, expired, or + * partially collected. + */ + ReferenceEntry getEntry(Object key) { + // does not impact recency ordering + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).getEntry(key, hash); + } + + void refresh(K key) { + int hash = hash(checkNotNull(key)); + segmentFor(hash).refresh(key, hash, defaultLoader, false); + } + + @Override + public boolean containsKey(Object key) { + // does not impact recency ordering + if (key == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).containsKey(key, hash); + } + + @Override + public boolean containsValue(Object value) { + // does not impact recency ordering + if (value == null) { + return false; + } + + // This implementation is patterned after ConcurrentHashMap, but without the locking. The only + // way for it to return a false negative would be for the target value to jump around in the map + // such that none of the subsequent iterations observed it, despite the fact that at every point + // in time it was present somewhere int the map. This becomes increasingly unlikely as + // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. + long now = ticker.read(); + final Segment[] segments = this.segments; + long last = -1L; + for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { + long sum = 0L; + for (Segment segment : segments) { + // ensure visibility of most recent completed write + int unused = segment.count; // read-volatile + + AtomicReferenceArray> table = segment.table; + for (int j = 0; j < table.length(); j++) { + for (ReferenceEntry e = table.get(j); e != null; e = e.getNext()) { + V v = segment.getLiveValue(e, now); + if (v != null && valueEquivalence.equivalent(value, v)) { + return true; + } + } + } + sum += segment.modCount; + } + if (sum == last) { + break; + } + last = sum; + } + return false; + } + + @Override + public V put(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, false); + } + + @Override + public V putIfAbsent(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, true); + } + + @Override + public V compute(K key, BiFunction function) { + checkNotNull(key); + checkNotNull(function); + int hash = hash(key); + return segmentFor(hash).compute(key, hash, function); + } + + @Override + public V computeIfAbsent(K key, Function function) { + checkNotNull(key); + checkNotNull(function); + return compute(key, (k, oldValue) -> (oldValue == null) ? function.apply(key) : oldValue); + } + + @Override + public V computeIfPresent(K key, BiFunction function) { + checkNotNull(key); + checkNotNull(function); + return compute(key, (k, oldValue) -> (oldValue == null) ? null : function.apply(k, oldValue)); + } + + @Override + public V merge(K key, V newValue, BiFunction function) { + checkNotNull(key); + checkNotNull(newValue); + checkNotNull(function); + return compute( + key, (k, oldValue) -> (oldValue == null) ? newValue : function.apply(oldValue, newValue)); + } + + @Override + public void putAll(Map m) { + for (Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + @Override + public V remove(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash); + } + + @Override + public boolean remove(Object key, Object value) { + if (key == null || value == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + checkNotNull(key); + checkNotNull(newValue); + if (oldValue == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).replace(key, hash, oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).replace(key, hash, value); + } + + @Override + public void clear() { + for (Segment segment : segments) { + segment.clear(); + } + } + + void invalidateAll(Iterable keys) { + // TODO(fry): batch by segment + for (Object key : keys) { + remove(key); + } + } + + Set keySet; + + @Override + public Set keySet() { + // does not impact recency ordering + Set ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet()); + } + + Collection values; + + @Override + public Collection values() { + // does not impact recency ordering + Collection vs = values; + return (vs != null) ? vs : (values = new Values()); + } + + Set> entrySet; + + @Override + @GwtIncompatible // Not supported. + public Set> entrySet() { + // does not impact recency ordering + Set> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet()); + } + + // Iterator Support + + abstract class HashIterator implements Iterator { + + int nextSegmentIndex; + int nextTableIndex; + Segment currentSegment; + AtomicReferenceArray> currentTable; + ReferenceEntry nextEntry; + WriteThroughEntry nextExternal; + WriteThroughEntry lastReturned; + + HashIterator() { + nextSegmentIndex = segments.length - 1; + nextTableIndex = -1; + advance(); + } + + @Override + public abstract T next(); + + final void advance() { + nextExternal = null; + + if (nextInChain()) { + return; + } + + if (nextInTable()) { + return; + } + + while (nextSegmentIndex >= 0) { + currentSegment = segments[nextSegmentIndex--]; + if (currentSegment.count != 0) { + currentTable = currentSegment.table; + nextTableIndex = currentTable.length() - 1; + if (nextInTable()) { + return; + } + } + } + } + + /** Finds the next entry in the current chain. Returns true if an entry was found. */ + boolean nextInChain() { + if (nextEntry != null) { + for (nextEntry = nextEntry.getNext(); nextEntry != null; nextEntry = nextEntry.getNext()) { + if (advanceTo(nextEntry)) { + return true; + } + } + } + return false; + } + + /** Finds the next entry in the current table. Returns true if an entry was found. */ + boolean nextInTable() { + while (nextTableIndex >= 0) { + if ((nextEntry = currentTable.get(nextTableIndex--)) != null) { + if (advanceTo(nextEntry) || nextInChain()) { + return true; + } + } + } + return false; + } + + /** + * Advances to the given entry. Returns true if the entry was valid, false if it should be + * skipped. + */ + boolean advanceTo(ReferenceEntry entry) { + try { + long now = ticker.read(); + K key = entry.getKey(); + V value = getLiveValue(entry, now); + if (value != null) { + nextExternal = new WriteThroughEntry(key, value); + return true; + } else { + // Skip stale entry. + return false; + } + } finally { + currentSegment.postReadCleanup(); + } + } + + @Override + public boolean hasNext() { + return nextExternal != null; + } + + WriteThroughEntry nextEntry() { + if (nextExternal == null) { + throw new NoSuchElementException(); + } + lastReturned = nextExternal; + advance(); + return lastReturned; + } + + @Override + public void remove() { + checkState(lastReturned != null); + LocalCache.this.remove(lastReturned.getKey()); + lastReturned = null; + } + } + + final class KeyIterator extends HashIterator { + + @Override + public K next() { + return nextEntry().getKey(); + } + } + + final class ValueIterator extends HashIterator { + + @Override + public V next() { + return nextEntry().getValue(); + } + } + + /** + * Custom Entry class used by EntryIterator.next(), that relays setValue changes to the underlying + * map. + */ + final class WriteThroughEntry implements Entry { + final K key; // non-null + V value; // non-null + + WriteThroughEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public boolean equals(Object object) { + // Cannot use key and value equivalence + if (object instanceof Entry) { + Entry that = (Entry) object; + return key.equals(that.getKey()) && value.equals(that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + // Cannot use key and value equivalence + return key.hashCode() ^ value.hashCode(); + } + + @Override + public V setValue(V newValue) { + V oldValue = put(key, newValue); + value = newValue; // only if put succeeds + return oldValue; + } + + @Override + public String toString() { + return getKey() + "=" + getValue(); + } + } + + final class EntryIterator extends HashIterator> { + + @Override + public Entry next() { + return nextEntry(); + } + } + + abstract class AbstractCacheSet extends AbstractSet { + @Override + public int size() { + return LocalCache.this.size(); + } + + @Override + public boolean isEmpty() { + return LocalCache.this.isEmpty(); + } + + @Override + public void clear() { + LocalCache.this.clear(); + } + + // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. + // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 + + @Override + public Object[] toArray() { + return toArrayList(this).toArray(); + } + + @Override + public E[] toArray(E[] a) { + return toArrayList(this).toArray(a); + } + } + + private static ArrayList toArrayList(Collection c) { + // Avoid calling ArrayList(Collection), which may call back into toArray. + ArrayList result = new ArrayList<>(c.size()); + Iterators.addAll(result, c.iterator()); + return result; + } + + boolean removeIf(BiPredicate filter) { + checkNotNull(filter); + boolean changed = false; + for (K key : keySet()) { + while (true) { + V value = get(key); + if (value == null || !filter.test(key, value)) { + break; + } else if (LocalCache.this.remove(key, value)) { + changed = true; + break; + } + } + } + return changed; + } + + final class KeySet extends AbstractCacheSet { + + @Override + public Iterator iterator() { + return new KeyIterator(); + } + + @Override + public boolean contains(Object o) { + return LocalCache.this.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return LocalCache.this.remove(o) != null; + } + } + + final class Values extends AbstractCollection { + @Override + public int size() { + return LocalCache.this.size(); + } + + @Override + public boolean isEmpty() { + return LocalCache.this.isEmpty(); + } + + @Override + public void clear() { + LocalCache.this.clear(); + } + + @Override + public Iterator iterator() { + return new ValueIterator(); + } + + @Override + public boolean removeIf(Predicate filter) { + checkNotNull(filter); + return LocalCache.this.removeIf((k, v) -> filter.test(v)); + } + + @Override + public boolean contains(Object o) { + return LocalCache.this.containsValue(o); + } + + // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. + // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 + + @Override + public Object[] toArray() { + return toArrayList(this).toArray(); + } + + @Override + public E[] toArray(E[] a) { + return toArrayList(this).toArray(a); + } + } + + final class EntrySet extends AbstractCacheSet> { + + @Override + public Iterator> iterator() { + return new EntryIterator(); + } + + @Override + public boolean removeIf(Predicate> filter) { + checkNotNull(filter); + return LocalCache.this.removeIf((k, v) -> filter.test(Maps.immutableEntry(k, v))); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + if (key == null) { + return false; + } + V v = LocalCache.this.get(key); + + return v != null && valueEquivalence.equivalent(e.getValue(), v); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + return key != null && LocalCache.this.remove(key, e.getValue()); + } + } + + // Serialization Support + + /** + * Serializes the configuration of a LocalCache, reconstituting it as a Cache using CacheBuilder + * upon deserialization. An instance of this class is fit for use by the writeReplace of + * LocalManualCache. + * + *

Unfortunately, readResolve() doesn't get called when a circular dependency is present, so + * the proxy must be able to behave as the cache itself. + */ + static class ManualSerializationProxy extends ForwardingCache + implements Serializable { + private static final long serialVersionUID = 1; + + final Strength keyStrength; + final Strength valueStrength; + final Equivalence keyEquivalence; + final Equivalence valueEquivalence; + final long expireAfterWriteNanos; + final long expireAfterAccessNanos; + final long maxWeight; + final Weigher weigher; + final int concurrencyLevel; + final RemovalListener removalListener; + final Ticker ticker; + final CacheLoader loader; + + transient Cache delegate; + + ManualSerializationProxy(LocalCache cache) { + this( + cache.keyStrength, + cache.valueStrength, + cache.keyEquivalence, + cache.valueEquivalence, + cache.expireAfterWriteNanos, + cache.expireAfterAccessNanos, + cache.maxWeight, + cache.weigher, + cache.concurrencyLevel, + cache.removalListener, + cache.ticker, + cache.defaultLoader); + } + + private ManualSerializationProxy( + Strength keyStrength, + Strength valueStrength, + Equivalence keyEquivalence, + Equivalence valueEquivalence, + long expireAfterWriteNanos, + long expireAfterAccessNanos, + long maxWeight, + Weigher weigher, + int concurrencyLevel, + RemovalListener removalListener, + Ticker ticker, + CacheLoader loader) { + this.keyStrength = keyStrength; + this.valueStrength = valueStrength; + this.keyEquivalence = keyEquivalence; + this.valueEquivalence = valueEquivalence; + this.expireAfterWriteNanos = expireAfterWriteNanos; + this.expireAfterAccessNanos = expireAfterAccessNanos; + this.maxWeight = maxWeight; + this.weigher = weigher; + this.concurrencyLevel = concurrencyLevel; + this.removalListener = removalListener; + this.ticker = (ticker == Ticker.systemTicker() || ticker == NULL_TICKER) ? null : ticker; + this.loader = loader; + } + + CacheBuilder recreateCacheBuilder() { + CacheBuilder builder = + CacheBuilder.newBuilder() + .setKeyStrength(keyStrength) + .setValueStrength(valueStrength) + .keyEquivalence(keyEquivalence) + .valueEquivalence(valueEquivalence) + .concurrencyLevel(concurrencyLevel) + .removalListener(removalListener); + builder.strictParsing = false; + if (expireAfterWriteNanos > 0) { + builder.expireAfterWrite(expireAfterWriteNanos, TimeUnit.NANOSECONDS); + } + if (expireAfterAccessNanos > 0) { + builder.expireAfterAccess(expireAfterAccessNanos, TimeUnit.NANOSECONDS); + } + if (weigher != OneWeigher.INSTANCE) { + Object unused = builder.weigher(weigher); + if (maxWeight != UNSET_INT) { + builder.maximumWeight(maxWeight); + } + } else { + if (maxWeight != UNSET_INT) { + builder.maximumSize(maxWeight); + } + } + if (ticker != null) { + builder.ticker(ticker); + } + return builder; + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + CacheBuilder builder = recreateCacheBuilder(); + this.delegate = builder.build(); + } + + private Object readResolve() { + return delegate; + } + + @Override + protected Cache delegate() { + return delegate; + } + } + + /** + * Serializes the configuration of a LocalCache, reconstituting it as an LoadingCache using + * CacheBuilder upon deserialization. An instance of this class is fit for use by the writeReplace + * of LocalLoadingCache. + * + *

Unfortunately, readResolve() doesn't get called when a circular dependency is present, so + * the proxy must be able to behave as the cache itself. + */ + static final class LoadingSerializationProxy extends ManualSerializationProxy + implements LoadingCache, Serializable { + private static final long serialVersionUID = 1; + + transient LoadingCache autoDelegate; + + LoadingSerializationProxy(LocalCache cache) { + super(cache); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + CacheBuilder builder = recreateCacheBuilder(); + this.autoDelegate = builder.build(loader); + } + + @Override + public V get(K key) throws ExecutionException { + return autoDelegate.get(key); + } + + @Override + public V getUnchecked(K key) { + return autoDelegate.getUnchecked(key); + } + + @Override + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + return autoDelegate.getAll(keys); + } + + @Override + public V apply(K key) { + return autoDelegate.apply(key); + } + + @Override + public void refresh(K key) { + autoDelegate.refresh(key); + } + + private Object readResolve() { + return autoDelegate; + } + } + + static class LocalManualCache implements Cache, Serializable { + final LocalCache localCache; + + LocalManualCache(CacheBuilder builder) { + this(new LocalCache<>(builder, null)); + } + + private LocalManualCache(LocalCache localCache) { + this.localCache = localCache; + } + + // Cache methods + + @Override + public V getIfPresent(Object key) { + return localCache.getIfPresent(key); + } + + @Override + public V get(K key, final Callable valueLoader) throws ExecutionException { + checkNotNull(valueLoader); + return localCache.get( + key, + new CacheLoader() { + @Override + public V load(Object key) throws Exception { + return valueLoader.call(); + } + }); + } + + @Override + public ImmutableMap getAllPresent(Iterable keys) { + return localCache.getAllPresent(keys); + } + + @Override + public void put(K key, V value) { + localCache.put(key, value); + } + + @Override + public void putAll(Map m) { + localCache.putAll(m); + } + + @Override + public void invalidate(Object key) { + checkNotNull(key); + localCache.remove(key); + } + + @Override + public void invalidateAll(Iterable keys) { + localCache.invalidateAll(keys); + } + + @Override + public void invalidateAll() { + localCache.clear(); + } + + @Override + public long size() { + return localCache.longSize(); + } + + @Override + public ConcurrentMap asMap() { + return localCache; + } + + @Override + public CacheStats stats() { + SimpleStatsCounter aggregator = new SimpleStatsCounter(); + aggregator.incrementBy(localCache.globalStatsCounter); + for (Segment segment : localCache.segments) { + aggregator.incrementBy(segment.statsCounter); + } + return aggregator.snapshot(); + } + + @Override + public void cleanUp() { + localCache.cleanUp(); + } + + // Serialization Support + + private static final long serialVersionUID = 1; + + Object writeReplace() { + return new ManualSerializationProxy<>(localCache); + } + + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use ManualSerializationProxy"); + } + } + + static class LocalLoadingCache extends LocalManualCache + implements LoadingCache { + + LocalLoadingCache( + CacheBuilder builder, CacheLoader loader) { + super(new LocalCache<>(builder, checkNotNull(loader))); + } + + // LoadingCache methods + + @Override + public V get(K key) throws ExecutionException { + return localCache.getOrLoad(key); + } + + // TODO(b/27479612): consider removing this + @Override + public V getUnchecked(K key) { + try { + return get(key); + } catch (ExecutionException e) { + throw new UncheckedExecutionException(e.getCause()); + } + } + + @Override + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + return localCache.getAll(keys); + } + + @Override + public void refresh(K key) { + localCache.refresh(key); + } + + @Override + public final V apply(K key) { + return getUnchecked(key); + } + + // Serialization Support + + private static final long serialVersionUID = 1; + + @Override + Object writeReplace() { + return new LoadingSerializationProxy<>(localCache); + } + + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use LoadingSerializationProxy"); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAddable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAddable.java new file mode 100644 index 0000000000000..bbfd0fa519b26 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAddable.java @@ -0,0 +1,37 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Abstract interface for objects that can concurrently add longs. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +interface LongAddable { + void increment(); + + void add(long x); + + long sum(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAddables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAddables.java new file mode 100644 index 0000000000000..0f1d16b196c9f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAddables.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Source of {@link LongAddable} objects that deals with GWT, Unsafe, and all that. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class LongAddables { + private static final Supplier SUPPLIER; + + static { + Supplier supplier; + try { + // trigger static initialization of the LongAdder class, which may fail + LongAdder unused = new LongAdder(); + supplier = + new Supplier() { + @Override + public LongAddable get() { + return new LongAdder(); + } + }; + } catch (Throwable t) { // we really want to catch *everything* + supplier = + new Supplier() { + @Override + public LongAddable get() { + return new PureJavaLongAddable(); + } + }; + } + SUPPLIER = supplier; + } + + public static LongAddable create() { + return SUPPLIER.get(); + } + + private static final class PureJavaLongAddable extends AtomicLong implements LongAddable { + @Override + public void increment() { + getAndIncrement(); + } + + @Override + public void add(long x) { + getAndAdd(x); + } + + @Override + public long sum() { + return get(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAdder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAdder.java new file mode 100644 index 0000000000000..30050955a1bf0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/LongAdder.java @@ -0,0 +1,203 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLong; + +/** + * One or more variables that together maintain an initially zero {@code long} sum. When updates + * (method {@link #add}) are contended across threads, the set of variables may grow dynamically to + * reduce contention. Method {@link #sum} (or, equivalently, {@link #longValue}) returns the current + * total combined across the variables maintaining the sum. + * + *

This class is usually preferable to {@link AtomicLong} when multiple threads update a common + * sum that is used for purposes such as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar characteristics. But under + * high contention, expected throughput of this class is significantly higher, at the expense of + * higher space consumption. + * + *

This class extends {@link Number}, but does not define methods such as {@code + * equals}, {@code hashCode} and {@code compareTo} because instances are expected to be mutated, and + * so are not useful as collection keys. + * + *

jsr166e note: This class is targeted to be placed in java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class LongAdder extends Striped64 implements Serializable, LongAddable { + private static final long serialVersionUID = 7249069246863182397L; + + /** Version of plus for use in retryUpdate */ + @Override + final long fn(long v, long x) { + return v + x; + } + + /** Creates a new adder with initial sum of zero. */ + public LongAdder() {} + + /** + * Adds the given value. + * + * @param x the value to add + */ + @Override + public void add(long x) { + Cell[] as; + long b, v; + int[] hc; + Cell a; + int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + if ((hc = threadHashCode.get()) == null + || as == null + || (n = as.length) < 1 + || (a = as[(n - 1) & hc[0]]) == null + || !(uncontended = a.cas(v = a.value, v + x))) retryUpdate(x, hc, uncontended); + } + } + + /** Equivalent to {@code add(1)}. */ + @Override + public void increment() { + add(1L); + } + + /** Equivalent to {@code add(-1)}. */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an atomic snapshot; invocation in + * the absence of concurrent updates returns an accurate result, but concurrent updates that occur + * while the sum is being calculated might not be incorporated. + * + * @return the sum + */ + @Override + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may be a useful alternative to + * creating a new adder, but is only effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is known that no threads are + * concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link #reset}. This method may apply for + * example during quiescent points between multithreaded computations. If there are updates + * concurrent with this method, the returned value is not guaranteed to be the final + * value occurring before the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * + * @return the String representation of the {@link #sum} + */ + @Override + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + @Override + public long longValue() { + return sum(); + } + + /** Returns the {@link #sum} as an {@code int} after a narrowing primitive conversion. */ + @Override + public int intValue() { + return (int) sum(); + } + + /** Returns the {@link #sum} as a {@code float} after a widening primitive conversion. */ + @Override + public float floatValue() { + return (float) sum(); + } + + /** Returns the {@link #sum} as a {@code double} after a widening primitive conversion. */ + @Override + public double doubleValue() { + return (double) sum(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ParametricNullness.java new file mode 100644 index 0000000000000..d79d179591634 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ReferenceEntry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ReferenceEntry.java new file mode 100644 index 0000000000000..dcfd416d82e99 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/ReferenceEntry.java @@ -0,0 +1,114 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.cache.LocalCache.ValueReference; + +/** + * An entry in a reference map. + * + *

Entries in the map can be in the following states: + * + *

Valid: + * + *

    + *
  • Live: valid key/value are set + *
  • Loading: loading is pending + *
+ * + *

Invalid: + * + *

    + *
  • Expired: time expired (key/value may still be set) + *
  • Collected: key/value was partially collected, but not yet cleaned up + *
  • Unset: marked as unset, awaiting cleanup or reuse + *
+ */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +interface ReferenceEntry { + /** Returns the value reference from this entry. */ + ValueReference getValueReference(); + + /** Sets the value reference for this entry. */ + void setValueReference(ValueReference valueReference); + + /** Returns the next entry in the chain. */ + ReferenceEntry getNext(); + + /** Returns the entry's hash. */ + int getHash(); + + /** Returns the key for this entry. */ + K getKey(); + + /* + * Used by entries that use access order. Access entries are maintained in a doubly-linked list. + * New entries are added at the tail of the list at write time; stale entries are expired from + * the head of the list. + */ + + /** Returns the time that this entry was last accessed, in ns. */ + @SuppressWarnings("GoodTime") + long getAccessTime(); + + /** Sets the entry access time in ns. */ + @SuppressWarnings("GoodTime") // b/122668874 + void setAccessTime(long time); + + /** Returns the next entry in the access queue. */ + ReferenceEntry getNextInAccessQueue(); + + /** Sets the next entry in the access queue. */ + void setNextInAccessQueue(ReferenceEntry next); + + /** Returns the previous entry in the access queue. */ + ReferenceEntry getPreviousInAccessQueue(); + + /** Sets the previous entry in the access queue. */ + void setPreviousInAccessQueue(ReferenceEntry previous); + + /* + * Implemented by entries that use write order. Write entries are maintained in a doubly-linked + * list. New entries are added at the tail of the list at write time and stale entries are + * expired from the head of the list. + */ + + @SuppressWarnings("GoodTime") + /** Returns the time that this entry was last written, in ns. */ + long getWriteTime(); + + /** Sets the entry write time in ns. */ + @SuppressWarnings("GoodTime") // b/122668874 + void setWriteTime(long time); + + /** Returns the next entry in the write queue. */ + ReferenceEntry getNextInWriteQueue(); + + /** Sets the next entry in the write queue. */ + void setNextInWriteQueue(ReferenceEntry next); + + /** Returns the previous entry in the write queue. */ + ReferenceEntry getPreviousInWriteQueue(); + + /** Sets the previous entry in the write queue. */ + void setPreviousInWriteQueue(ReferenceEntry previous); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalCause.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalCause.java new file mode 100644 index 0000000000000..2a5a54c7799c5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalCause.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * The reason why a cached entry was removed. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public enum RemovalCause { + /** + * The entry was manually removed by the user. This can result from the user invoking {@link + * Cache#invalidate}, {@link Cache#invalidateAll(Iterable)}, {@link Cache#invalidateAll()}, {@link + * Map#remove}, {@link ConcurrentMap#remove}, or {@link Iterator#remove}. + */ + EXPLICIT { + @Override + boolean wasEvicted() { + return false; + } + }, + + /** + * The entry itself was not actually removed, but its value was replaced by the user. This can + * result from the user invoking {@link Cache#put}, {@link LoadingCache#refresh}, {@link Map#put}, + * {@link Map#putAll}, {@link ConcurrentMap#replace(Object, Object)}, or {@link + * ConcurrentMap#replace(Object, Object, Object)}. + */ + REPLACED { + @Override + boolean wasEvicted() { + return false; + } + }, + + /** + * The entry was removed automatically because its key or value was garbage-collected. This can + * occur when using {@link CacheBuilder#weakKeys}, {@link CacheBuilder#weakValues}, or {@link + * CacheBuilder#softValues}. + */ + COLLECTED { + @Override + boolean wasEvicted() { + return true; + } + }, + + /** + * The entry's expiration timestamp has passed. This can occur when using {@link + * CacheBuilder#expireAfterWrite} or {@link CacheBuilder#expireAfterAccess}. + */ + EXPIRED { + @Override + boolean wasEvicted() { + return true; + } + }, + + /** + * The entry was evicted due to size constraints. This can occur when using {@link + * CacheBuilder#maximumSize} or {@link CacheBuilder#maximumWeight}. + */ + SIZE { + @Override + boolean wasEvicted() { + return true; + } + }; + + /** + * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither + * {@link #EXPLICIT} nor {@link #REPLACED}). + */ + abstract boolean wasEvicted(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalListener.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalListener.java new file mode 100644 index 0000000000000..357a5b58f462a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalListener.java @@ -0,0 +1,53 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * An object that can receive a notification when an entry is removed from a cache. The removal + * resulting in notification could have occurred to an entry being manually removed or replaced, or + * due to eviction resulting from timed expiration, exceeding a maximum size, or garbage collection. + * + *

An instance may be called concurrently by multiple threads to process different entries. + * Implementations of this interface should avoid performing blocking calls or synchronizing on + * shared resources. + * + * @param the most general type of keys this listener can listen for; for example {@code Object} + * if any key is acceptable + * @param the most general type of values this listener can listen for; for example {@code + * Object} if any key is acceptable + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@FunctionalInterface +@ElementTypesAreNonnullByDefault +public interface RemovalListener { + /** + * Notifies the listener that a removal occurred at some point in the past. + * + *

This does not always signify that the key is now absent from the cache, as it may have + * already been re-added. + */ + // Technically should accept RemovalNotification, but because + // RemovalNotification is guaranteed covariant, let's make users' lives simpler. + void onRemoval(RemovalNotification notification); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalListeners.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalListeners.java new file mode 100644 index 0000000000000..60c92ef2a3d2a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalListeners.java @@ -0,0 +1,54 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.concurrent.Executor; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A collection of common removal listeners. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class RemovalListeners { + + private RemovalListeners() {} + + /** + * Returns a {@code RemovalListener} which processes all eviction notifications using {@code + * executor}. + * + * @param listener the backing listener + * @param executor the executor with which removal notifications are asynchronously executed + */ + public static RemovalListener asynchronous( + RemovalListener listener, Executor executor) { + checkNotNull(listener); + checkNotNull(executor); + return (RemovalNotification notification) -> + executor.execute(() -> listener.onRemoval(notification)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalNotification.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalNotification.java new file mode 100644 index 0000000000000..48cc9af2cccee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/RemovalNotification.java @@ -0,0 +1,74 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.AbstractMap.SimpleImmutableEntry; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A notification of the removal of a single entry. The key and/or value may be null if they were + * already garbage collected. + * + *

Like other {@code Entry} instances associated with {@code CacheBuilder}, this class holds + * strong references to the key and value, regardless of the type of references the cache may be + * using. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class RemovalNotification extends SimpleImmutableEntry { + private final RemovalCause cause; + + /** + * Creates a new {@code RemovalNotification} for the given {@code key}/{@code value} pair, with + * the given {@code cause} for the removal. The {@code key} and/or {@code value} may be {@code + * null} if they were already garbage collected. + * + * @since 19.0 + */ + public static RemovalNotification create(K key, V value, RemovalCause cause) { + return new RemovalNotification<>(key, value, cause); + } + + private RemovalNotification(K key, V value, RemovalCause cause) { + super(key, value); + this.cause = checkNotNull(cause); + } + + /** Returns the cause for which the entry was removed. */ + public RemovalCause getCause() { + return cause; + } + + /** + * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither + * {@link RemovalCause#EXPLICIT} nor {@link RemovalCause#REPLACED}). + */ + public boolean wasEvicted() { + return cause.wasEvicted(); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Striped64.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Striped64.java new file mode 100644 index 0000000000000..67d3b45f7faf8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Striped64.java @@ -0,0 +1,320 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Random; + +/** + * A package-local class holding common representation and mechanics for classes supporting dynamic + * striping on 64bit values. The class extends Number so that concrete subclasses must publicly do + * so. + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock; when the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed + * between pads, hoping that the JVM doesn't reorder them. + * + *

JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were + * provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + Cell(long x) { + value = x; + } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /** + * ThreadLocal holding a single-slot int array holding hash code. Unlike the JDK8 version of this + * class, we use a suboptimal int[] representation to avoid introducing a new type that can impede + * class-unloading when ThreadLocals are not removed. + */ + static final ThreadLocal threadHashCode = new ThreadLocal<>(); + + /** Generator of new random hash codes */ + static final Random rng = new Random(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** Table of cells. When non-null, size is a power of 2. */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as a fallback during table + * initialization races. Updated via CAS. + */ + transient volatile long base; + + /** Spinlock (locked via CAS) used when resizing and/or creating Cells. */ + transient volatile int busy; + + /** Package-private default constructor */ + Striped64() {} + + /** CASes the base field. */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + /** CASes the busy field from 0 to 1 to acquire lock. */ + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses should open-code this update + * function for most uses, but the virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, creating new Cells, and/or + * contention. See above for explanation. This method suffers the usual non-modularity problems of + * optimistic retry code, relying on rechecked sets of reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, int[] hc, boolean wasUncontended) { + int h; + if (hc == null) { + threadHashCode.set(hc = new int[1]); // Initialize randomly + int r = rng.nextInt(); // Avoid zero to allow xorShift rehash + h = hc[0] = (r == 0) ? 1 : r; + } else h = hc[0]; + boolean collide = false; // True if last slot nonempty + for (; ; ) { + Cell[] as; + Cell a; + int n; + long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; + int m, j; + if ((rs = cells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) break; + continue; // Slot is now non-empty + } + } + collide = false; + } else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) break; + else if (n >= NCPU || cells != as) collide = false; // At max size or stale + else if (!collide) collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + hc[0] = h; // Record index for next time + } else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) break; + } else if (casBase(v = base, fn(v, x))) break; // Fall back on using base + } + } + + /** Sets base and all cells to the given value. */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) a.value = initialValue; + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple call + * to Unsafe.getUnsafe when integrating into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException tryReflectionInstead) { + } + try { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction() { + @Override + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", e.getCause()); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Weigher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Weigher.java new file mode 100644 index 0000000000000..744590f21f64c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/Weigher.java @@ -0,0 +1,42 @@ +/* + * 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.iotdb.google.common.cache; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Calculates the weights of cache entries. + * + * @author Charles Fry + * @since 11.0 + */ +@GwtCompatible +@FunctionalInterface +@ElementTypesAreNonnullByDefault +public interface Weigher { + + /** + * Returns the weight of a cache entry. There is no unit for entry weights; rather they are simply + * relative to each other. + * + * @return the weight of the entry; must be non-negative + */ + int weigh(K key, V value); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/package-info.java new file mode 100644 index 0000000000000..3a8306446a9b6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/cache/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.cache; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractBiMap.java new file mode 100644 index 0000000000000..538c65862544b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractBiMap.java @@ -0,0 +1,502 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * A general-purpose bimap implementation using any two backing {@code Map} instances. + * + *

Note that this class contains {@code equals()} calls that keep it from supporting {@code + * IdentityHashMap} backing maps. + * + * @author Kevin Bourrillion + * @author Mike Bostock + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class AbstractBiMap extends ForwardingMap + implements BiMap, Serializable { + + @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (lateinit) + private transient Map delegate; + + @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (lateinit) + transient AbstractBiMap inverse; + + /** Package-private constructor for creating a map-backed bimap. */ + AbstractBiMap(Map forward, Map backward) { + setDelegates(forward, backward); + } + + /** Private constructor for inverse bimap. */ + private AbstractBiMap(Map backward, AbstractBiMap forward) { + delegate = backward; + inverse = forward; + } + + @Override + protected Map delegate() { + return delegate; + } + + /** Returns its input, or throws an exception if this is not a valid key. */ + @ParametricNullness + K checkKey(@ParametricNullness K key) { + return key; + } + + /** Returns its input, or throws an exception if this is not a valid value. */ + @ParametricNullness + V checkValue(@ParametricNullness V value) { + return value; + } + + /** + * Specifies the delegate maps going in each direction. Called by the constructor and by + * subclasses during deserialization. + */ + void setDelegates(Map forward, Map backward) { + checkState(delegate == null); + checkState(inverse == null); + checkArgument(forward.isEmpty()); + checkArgument(backward.isEmpty()); + checkArgument(forward != backward); + delegate = forward; + inverse = makeInverse(backward); + } + + AbstractBiMap makeInverse(Map backward) { + return new Inverse<>(backward, this); + } + + void setInverse(AbstractBiMap inverse) { + this.inverse = inverse; + } + + // Query Operations (optimizations) + + @Override + public boolean containsValue(Object value) { + return inverse.containsKey(value); + } + + // Modification Operations + + @Override + public V put(@ParametricNullness K key, @ParametricNullness V value) { + return putInBothMaps(key, value, false); + } + + @Override + public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + return putInBothMaps(key, value, true); + } + + private V putInBothMaps(@ParametricNullness K key, @ParametricNullness V value, boolean force) { + checkKey(key); + checkValue(value); + boolean containedKey = containsKey(key); + if (containedKey && Objects.equal(value, get(key))) { + return value; + } + if (force) { + inverse().remove(value); + } else { + checkArgument(!containsValue(value), "value already present: %s", value); + } + V oldValue = delegate.put(key, value); + updateInverseMap(key, containedKey, oldValue, value); + return oldValue; + } + + private void updateInverseMap( + @ParametricNullness K key, boolean containedKey, V oldValue, @ParametricNullness V newValue) { + if (containedKey) { + // The cast is safe because of the containedKey check. + removeFromInverseMap(uncheckedCastNullableTToT(oldValue)); + } + inverse.delegate.put(newValue, key); + } + + @Override + public V remove(Object key) { + return containsKey(key) ? removeFromBothMaps(key) : null; + } + + @ParametricNullness + private V removeFromBothMaps(Object key) { + // The cast is safe because the callers of this method first check that the key is present. + V oldValue = uncheckedCastNullableTToT(delegate.remove(key)); + removeFromInverseMap(oldValue); + return oldValue; + } + + private void removeFromInverseMap(@ParametricNullness V oldValue) { + inverse.delegate.remove(oldValue); + } + + // Bulk Operations + + @Override + public void putAll(Map map) { + for (Entry entry : map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void replaceAll(BiFunction function) { + this.delegate.replaceAll(function); + inverse.delegate.clear(); + Entry broken = null; + Iterator> itr = this.delegate.entrySet().iterator(); + while (itr.hasNext()) { + Entry entry = itr.next(); + K k = entry.getKey(); + V v = entry.getValue(); + K conflict = inverse.delegate.putIfAbsent(v, k); + if (conflict != null) { + broken = entry; + // We're definitely going to throw, but we'll try to keep the BiMap in an internally + // consistent state by removing the bad entry. + itr.remove(); + } + } + if (broken != null) { + throw new IllegalArgumentException("value already present: " + broken.getValue()); + } + } + + @Override + public void clear() { + delegate.clear(); + inverse.delegate.clear(); + } + + // Views + + @Override + public BiMap inverse() { + return inverse; + } + + private transient Set keySet; + + @Override + public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = new KeySet() : result; + } + + private class KeySet extends ForwardingSet { + @Override + protected Set delegate() { + return delegate.keySet(); + } + + @Override + public void clear() { + AbstractBiMap.this.clear(); + } + + @Override + public boolean remove(Object key) { + if (!contains(key)) { + return false; + } + removeFromBothMaps(key); + return true; + } + + @Override + public boolean removeAll(Collection keysToRemove) { + return standardRemoveAll(keysToRemove); + } + + @Override + public boolean retainAll(Collection keysToRetain) { + return standardRetainAll(keysToRetain); + } + + @Override + public Iterator iterator() { + return Maps.keyIterator(entrySet().iterator()); + } + } + + private transient Set valueSet; + + @Override + public Set values() { + /* + * We can almost reuse the inverse's keySet, except we have to fix the + * iteration order so that it is consistent with the forward map. + */ + Set result = valueSet; + return (result == null) ? valueSet = new ValueSet() : result; + } + + private class ValueSet extends ForwardingSet { + final Set valuesDelegate = inverse.keySet(); + + @Override + protected Set delegate() { + return valuesDelegate; + } + + @Override + public Iterator iterator() { + return Maps.valueIterator(entrySet().iterator()); + } + + @Override + public Object[] toArray() { + return standardToArray(); + } + + @Override + @SuppressWarnings("nullness") // bug in our checker's handling of toArray signatures + public T[] toArray(T[] array) { + return standardToArray(array); + } + + @Override + public String toString() { + return standardToString(); + } + } + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = new EntrySet() : result; + } + + class BiMapEntry extends ForwardingMapEntry { + private final Entry delegate; + + BiMapEntry(Entry delegate) { + this.delegate = delegate; + } + + @Override + protected Entry delegate() { + return delegate; + } + + @Override + public V setValue(V value) { + checkValue(value); + // Preconditions keep the map and inverse consistent. + checkState(entrySet().contains(this), "entry no longer in map"); + // similar to putInBothMaps, but set via entry + if (Objects.equal(value, getValue())) { + return value; + } + checkArgument(!containsValue(value), "value already present: %s", value); + V oldValue = delegate.setValue(value); + checkState(Objects.equal(value, get(getKey())), "entry no longer in map"); + updateInverseMap(getKey(), true, oldValue, value); + return oldValue; + } + } + + Iterator> entrySetIterator() { + final Iterator> iterator = delegate.entrySet().iterator(); + return new Iterator>() { + Entry entry; + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Entry next() { + entry = iterator.next(); + return new BiMapEntry(entry); + } + + @Override + public void remove() { + if (entry == null) { + throw new IllegalStateException("no calls to next() since the last call to remove()"); + } + V value = entry.getValue(); + iterator.remove(); + removeFromInverseMap(value); + entry = null; + } + }; + } + + private class EntrySet extends ForwardingSet> { + final Set> esDelegate = delegate.entrySet(); + + @Override + protected Set> delegate() { + return esDelegate; + } + + @Override + public void clear() { + AbstractBiMap.this.clear(); + } + + @Override + public boolean remove(Object object) { + /* + * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our + * nullness checker. + */ + if (!esDelegate.contains(object) || !(object instanceof Entry)) { + return false; + } + + Entry entry = (Entry) object; + inverse.delegate.remove(entry.getValue()); + /* + * Remove the mapping in inverse before removing from esDelegate because + * if entry is part of esDelegate, entry might be invalidated after the + * mapping is removed from esDelegate. + */ + esDelegate.remove(entry); + return true; + } + + @Override + public Iterator> iterator() { + return entrySetIterator(); + } + + // See java.util.Collections.CheckedEntrySet for details on attacks. + + @Override + public Object[] toArray() { + return standardToArray(); + } + + @Override + @SuppressWarnings("nullness") // bug in our checker's handling of toArray signatures + public T[] toArray(T[] array) { + return standardToArray(array); + } + + @Override + public boolean contains(Object o) { + return Maps.containsEntryImpl(delegate(), o); + } + + @Override + public boolean containsAll(Collection c) { + return standardContainsAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return standardRemoveAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return standardRetainAll(c); + } + } + + /** The inverse of any other {@code AbstractBiMap} subclass. */ + static class Inverse extends AbstractBiMap { + Inverse(Map backward, AbstractBiMap forward) { + super(backward, forward); + } + + /* + * Serialization stores the forward bimap, the inverse of this inverse. + * Deserialization calls inverse() on the forward bimap and returns that + * inverse. + * + * If a bimap and its inverse are serialized together, the deserialized + * instances have inverse() methods that return the other. + */ + + @Override + @ParametricNullness + K checkKey(@ParametricNullness K key) { + return inverse.checkValue(key); + } + + @Override + @ParametricNullness + V checkValue(@ParametricNullness V value) { + return inverse.checkKey(value); + } + + /** + * @serialData the forward bimap + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(inverse()); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + setInverse((AbstractBiMap) stream.readObject()); + } + + @GwtIncompatible // Not needed in the emulated source. + @J2ktIncompatible + Object readResolve() { + return inverse().inverse(); + } + + @GwtIncompatible // Not needed in emulated source. + @J2ktIncompatible + private static final long serialVersionUID = 0; + } + + @GwtIncompatible // Not needed in emulated source. + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractIndexedListIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractIndexedListIterator.java new file mode 100644 index 0000000000000..a7878d7f2c182 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractIndexedListIterator.java @@ -0,0 +1,110 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; + +/** + * This class provides a skeletal implementation of the {@link ListIterator} interface across a + * fixed number of elements that may be retrieved by position. It does not support {@link #remove}, + * {@link #set}, or {@link #add}. + * + * @author Jared Levy + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractIndexedListIterator extends UnmodifiableListIterator { + private final int size; + private int position; + + /** Returns the element with the specified index. This method is called by {@link #next()}. */ + @ParametricNullness + protected abstract E get(int index); + + /** + * Constructs an iterator across a sequence of the given size whose initial position is 0. That + * is, the first call to {@link #next()} will return the first element (or throw {@link + * NoSuchElementException} if {@code size} is zero). + * + * @throws IllegalArgumentException if {@code size} is negative + */ + protected AbstractIndexedListIterator(int size) { + this(size, 0); + } + + /** + * Constructs an iterator across a sequence of the given size with the given initial position. + * That is, the first call to {@link #nextIndex()} will return {@code position}, and the first + * call to {@link #next()} will return the element at that index, if available. Calls to {@link + * #previous()} can retrieve the preceding {@code position} elements. + * + * @throws IndexOutOfBoundsException if {@code position} is negative or is greater than {@code + * size} + * @throws IllegalArgumentException if {@code size} is negative + */ + protected AbstractIndexedListIterator(int size, int position) { + checkPositionIndex(position, size); + this.size = size; + this.position = position; + } + + @Override + public final boolean hasNext() { + return position < size; + } + + @Override + @ParametricNullness + public final E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return get(position++); + } + + @Override + public final int nextIndex() { + return position; + } + + @Override + public final boolean hasPrevious() { + return position > 0; + } + + @Override + @ParametricNullness + public final E previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + return get(--position); + } + + @Override + public final int previousIndex() { + return position - 1; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractIterator.java new file mode 100644 index 0000000000000..339a29680cb28 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractIterator.java @@ -0,0 +1,180 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.NoSuchElementException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * This class provides a skeletal implementation of the {@code Iterator} interface, to make this + * interface easier to implement for certain types of data sources. + * + *

{@code Iterator} requires its implementations to support querying the end-of-data status + * without changing the iterator's state, using the {@link #hasNext} method. But many data sources, + * such as {@link java.io.Reader#read()}, do not expose this information; the only way to discover + * whether there is any data left is by trying to retrieve it. These types of data sources are + * ordinarily difficult to write iterators for. But using this class, one must implement only the + * {@link #computeNext} method, and invoke the {@link #endOfData} method when appropriate. + * + *

Another example is an iterator that skips over null elements in a backing iterator. This could + * be implemented as: + * + *

{@code
+ * public static Iterator skipNulls(final Iterator in) {
+ *   return new AbstractIterator() {
+ *     protected String computeNext() {
+ *       while (in.hasNext()) {
+ *         String s = in.next();
+ *         if (s != null) {
+ *           return s;
+ *         }
+ *       }
+ *       return endOfData();
+ *     }
+ *   };
+ * }
+ * }
+ * + *

This class supports iterators that include null elements. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +// When making changes to this class, please also update the copy at +// org.apache.iotdb.google.common.base.AbstractIterator +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractIterator extends UnmodifiableIterator { + private State state = State.NOT_READY; + + /** Constructor for use by subclasses. */ + protected AbstractIterator() {} + + private enum State { + /** We have computed the next element and haven't returned it yet. */ + READY, + + /** We haven't yet computed or have already returned the element. */ + NOT_READY, + + /** We have reached the end of the data and are finished. */ + DONE, + + /** We've suffered an exception and are kaput. */ + FAILED, + } + + private T next; + + /** + * Returns the next element. Note: the implementation must call {@link #endOfData()} when + * there are no elements left in the iteration. Failure to do so could result in an infinite loop. + * + *

The initial invocation of {@link #hasNext()} or {@link #next()} calls this method, as does + * the first invocation of {@code hasNext} or {@code next} following each successful call to + * {@code next}. Once the implementation either invokes {@code endOfData} or throws an exception, + * {@code computeNext} is guaranteed to never be called again. + * + *

If this method throws an exception, it will propagate outward to the {@code hasNext} or + * {@code next} invocation that invoked this method. Any further attempts to use the iterator will + * result in an {@link IllegalStateException}. + * + *

The implementation of this method may not invoke the {@code hasNext}, {@code next}, or + * {@link #peek()} methods on this instance; if it does, an {@code IllegalStateException} will + * result. + * + * @return the next element if there was one. If {@code endOfData} was called during execution, + * the return value will be ignored. + * @throws RuntimeException if any unrecoverable error happens. This exception will propagate + * outward to the {@code hasNext()}, {@code next()}, or {@code peek()} invocation that invoked + * this method. Any further attempts to use the iterator will result in an {@link + * IllegalStateException}. + */ + protected abstract T computeNext(); + + /** + * Implementations of {@link #computeNext} must invoke this method when there are no + * elements left in the iteration. + * + * @return {@code null}; a convenience so your {@code computeNext} implementation can use the + * simple statement {@code return endOfData();} + */ + protected final T endOfData() { + state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + checkState(state != State.FAILED); + switch (state) { + case DONE: + return false; + case READY: + return true; + default: + } + return tryToComputeNext(); + } + + private boolean tryToComputeNext() { + state = State.FAILED; // temporary pessimism + next = computeNext(); + if (state != State.DONE) { + state = State.READY; + return true; + } + return false; + } + + // TODO(kak): Should we remove this? + @Override + @ParametricNullness + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + state = State.NOT_READY; + // Safe because hasNext() ensures that tryToComputeNext() has put a T into `next`. + T result = uncheckedCastNullableTToT(next); + next = null; + return result; + } + + /** + * Returns the next element in the iteration without advancing the iteration, according to the + * contract of {@link PeekingIterator#peek()}. + * + *

Implementations of {@code AbstractIterator} that wish to expose this functionality should + * implement {@code PeekingIterator}. + */ + @ParametricNullness + public final T peek() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + // Safe because hasNext() ensures that tryToComputeNext() has put a T into `next`. + return uncheckedCastNullableTToT(next); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractListMultimap.java new file mode 100644 index 0000000000000..334ee8915c1f9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractListMultimap.java @@ -0,0 +1,141 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Basic implementation of the {@link ListMultimap} interface. It's a wrapper around {@link + * AbstractMapBasedMultimap} that converts the returned collections into {@code Lists}. The {@link + * #createCollection} method must return a {@code List}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractListMultimap + extends AbstractMapBasedMultimap implements ListMultimap { + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding values + */ + protected AbstractListMultimap(Map> map) { + super(map); + } + + @Override + abstract List createCollection(); + + @Override + List createUnmodifiableEmptyCollection() { + return Collections.emptyList(); + } + + @Override + Collection unmodifiableCollectionSubclass(Collection collection) { + return Collections.unmodifiableList((List) collection); + } + + @Override + Collection wrapCollection(@ParametricNullness K key, Collection collection) { + return wrapList(key, (List) collection, null); + } + + // Following Javadoc copied from ListMultimap. + + /** + * {@inheritDoc} + * + *

Because the values for a given key may have duplicates and follow the insertion ordering, + * this method returns a {@link List}, instead of the {@link Collection} specified in the {@link + * Multimap} interface. + */ + @Override + public List get(@ParametricNullness K key) { + return (List) super.get(key); + } + + /** + * {@inheritDoc} + * + *

Because the values for a given key may have duplicates and follow the insertion ordering, + * this method returns a {@link List}, instead of the {@link Collection} specified in the {@link + * Multimap} interface. + */ + @Override + public List removeAll(Object key) { + return (List) super.removeAll(key); + } + + /** + * {@inheritDoc} + * + *

Because the values for a given key may have duplicates and follow the insertion ordering, + * this method returns a {@link List}, instead of the {@link Collection} specified in the {@link + * Multimap} interface. + */ + @Override + public List replaceValues(@ParametricNullness K key, Iterable values) { + return (List) super.replaceValues(key, values); + } + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} always + */ + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + return super.put(key, value); + } + + /** + * {@inheritDoc} + * + *

Though the method signature doesn't say so explicitly, the returned map has {@link List} + * values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * Compares the specified object to this multimap for equality. + * + *

Two {@code ListMultimap} instances are equal if, for each key, they contain the same values + * in the same order. If the value orderings disagree, the multimaps will not be considered equal. + */ + @Override + public boolean equals(Object object) { + return super.equals(object); + } + + private static final long serialVersionUID = 6588350623831699109L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapBasedMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapBasedMultimap.java new file mode 100644 index 0000000000000..d77d701b88851 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapBasedMultimap.java @@ -0,0 +1,1667 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.Maps.ViewCachingAbstractMap; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.function.BiConsumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * Basic implementation of the {@link Multimap} interface. This class represents a multimap as a map + * that associates each key with a collection of values. All methods of {@link Multimap} are + * supported, including those specified as optional in the interface. + * + *

To implement a multimap, a subclass must define the method {@link #createCollection()}, which + * creates an empty collection of values for a key. + * + *

The multimap constructor takes a map that has a single entry for each distinct key. When you + * insert a key-value pair with a key that isn't already in the multimap, {@code + * AbstractMapBasedMultimap} calls {@link #createCollection()} to create the collection of values + * for that key. The subclass should not call {@link #createCollection()} directly, and a new + * instance should be created every time the method is called. + * + *

For example, the subclass could pass a {@link java.util.TreeMap} during construction, and + * {@link #createCollection()} could return a {@link java.util.TreeSet}, in which case the + * multimap's iterators would propagate through the keys and values in sorted order. + * + *

Keys and values may be null, as long as the underlying collection classes support null + * elements. + * + *

The collections created by {@link #createCollection()} may or may not allow duplicates. If the + * collection, such as a {@link Set}, does not support duplicates, an added key-value pair will + * replace an existing pair with the same key and value, if such a pair is present. With collections + * like {@link List} that allow duplicates, the collection will keep the existing key-value pairs + * while adding a new pair. + * + *

This class is not threadsafe when any concurrent operations update the multimap, even if the + * underlying map and {@link #createCollection()} method return threadsafe classes. Concurrent read + * operations will work correctly. To allow concurrent update operations, wrap your multimap with a + * call to {@link Multimaps#synchronizedMultimap}. + * + *

For serialization to work, the subclass must specify explicit {@code readObject} and {@code + * writeObject} methods. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractMapBasedMultimap + extends AbstractMultimap implements Serializable { + /* + * Here's an outline of the overall design. + * + * The map variable contains the collection of values associated with each + * key. When a key-value pair is added to a multimap that didn't previously + * contain any values for that key, a new collection generated by + * createCollection is added to the map. That same collection instance + * remains in the map as long as the multimap has any values for the key. If + * all values for the key are removed, the key and collection are removed + * from the map. + * + * The get method returns a WrappedCollection, which decorates the collection + * in the map (if the key is present) or an empty collection (if the key is + * not present). When the collection delegate in the WrappedCollection is + * empty, the multimap may contain subsequently added values for that key. To + * handle that situation, the WrappedCollection checks whether map contains + * an entry for the provided key, and if so replaces the delegate. + */ + + private transient Map> map; + private transient int totalSize; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding values + * @throws IllegalArgumentException if {@code map} is not empty + */ + protected AbstractMapBasedMultimap(Map> map) { + checkArgument(map.isEmpty()); + this.map = map; + } + + /** Used during deserialization only. */ + final void setMap(Map> map) { + this.map = map; + totalSize = 0; + for (Collection values : map.values()) { + checkArgument(!values.isEmpty()); + totalSize += values.size(); + } + } + + /** + * Creates an unmodifiable, empty collection of values. + * + *

This is used in {@link #removeAll} on an empty key. + */ + Collection createUnmodifiableEmptyCollection() { + return unmodifiableCollectionSubclass(createCollection()); + } + + /** + * Creates the collection of values for a single key. + * + *

Collections with weak, soft, or phantom references are not supported. Each call to {@code + * createCollection} should create a new instance. + * + *

The returned collection class determines whether duplicate key-value pairs are allowed. + * + * @return an empty collection of values + */ + abstract Collection createCollection(); + + /** + * Creates the collection of values for an explicitly provided key. By default, it simply calls + * {@link #createCollection()}, which is the correct behavior for most implementations. The {@link + * LinkedHashMultimap} class overrides it. + * + * @param key key to associate with values in the collection + * @return an empty collection of values + */ + Collection createCollection(@ParametricNullness K key) { + return createCollection(); + } + + Map> backingMap() { + return map; + } + + // Query Operations + + @Override + public int size() { + return totalSize; + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + // Modification Operations + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + if (collection.add(value)) { + totalSize++; + map.put(key, collection); + return true; + } else { + throw new AssertionError("New Collection violated the Collection spec"); + } + } else if (collection.add(value)) { + totalSize++; + return true; + } else { + return false; + } + } + + private Collection getOrCreateCollection(@ParametricNullness K key) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + map.put(key, collection); + } + return collection; + } + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

The returned collection is immutable. + */ + @Override + public Collection replaceValues(@ParametricNullness K key, Iterable values) { + Iterator iterator = values.iterator(); + if (!iterator.hasNext()) { + return removeAll(key); + } + + // TODO(lowasser): investigate atomic failure? + Collection collection = getOrCreateCollection(key); + Collection oldValues = createCollection(); + oldValues.addAll(collection); + + totalSize -= collection.size(); + collection.clear(); + + while (iterator.hasNext()) { + if (collection.add(iterator.next())) { + totalSize++; + } + } + + return unmodifiableCollectionSubclass(oldValues); + } + + /** + * {@inheritDoc} + * + *

The returned collection is immutable. + */ + @Override + public Collection removeAll(Object key) { + Collection collection = map.remove(key); + + if (collection == null) { + return createUnmodifiableEmptyCollection(); + } + + Collection output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + + return unmodifiableCollectionSubclass(output); + } + + Collection unmodifiableCollectionSubclass(Collection collection) { + return Collections.unmodifiableCollection(collection); + } + + @Override + public void clear() { + // Clear each collection, to make previously returned collections empty. + for (Collection collection : map.values()) { + collection.clear(); + } + map.clear(); + totalSize = 0; + } + + // Views + + /** + * {@inheritDoc} + * + *

The returned collection is not serializable. + */ + @Override + public Collection get(@ParametricNullness K key) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + } + return wrapCollection(key, collection); + } + + /** + * Generates a decorated collection that remains consistent with the values in the multimap for + * the provided key. Changes to the multimap may alter the returned collection, and vice versa. + */ + Collection wrapCollection(@ParametricNullness K key, Collection collection) { + return new WrappedCollection(key, collection, null); + } + + final List wrapList(@ParametricNullness K key, List list, WrappedCollection ancestor) { + return (list instanceof RandomAccess) + ? new RandomAccessWrappedList(key, list, ancestor) + : new WrappedList(key, list, ancestor); + } + + /** + * Collection decorator that stays in sync with the multimap values for a key. There are two kinds + * of wrapped collections: full and subcollections. Both have a delegate pointing to the + * underlying collection class. + * + *

Full collections, identified by a null ancestor field, contain all multimap values for a + * given key. Its delegate is a value in {@link AbstractMapBasedMultimap#map} whenever the + * delegate is non-empty. The {@code refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} + * methods ensure that the {@code WrappedCollection} and map remain consistent. + * + *

A subcollection, such as a sublist, contains some of the values for a given key. Its + * ancestor field points to the full wrapped collection with all values for the key. The + * subcollection {@code refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods call + * the corresponding methods of the full wrapped collection. + */ + class WrappedCollection extends AbstractCollection { + @ParametricNullness final K key; + Collection delegate; + final WrappedCollection ancestor; + final Collection ancestorDelegate; + + WrappedCollection( + @ParametricNullness K key, Collection delegate, WrappedCollection ancestor) { + this.key = key; + this.delegate = delegate; + this.ancestor = ancestor; + this.ancestorDelegate = (ancestor == null) ? null : ancestor.getDelegate(); + } + + /** + * If the delegate collection is empty, but the multimap has values for the key, replace the + * delegate with the new collection for the key. + * + *

For a subcollection, refresh its ancestor and validate that the ancestor delegate hasn't + * changed. + */ + void refreshIfEmpty() { + if (ancestor != null) { + ancestor.refreshIfEmpty(); + if (ancestor.getDelegate() != ancestorDelegate) { + throw new ConcurrentModificationException(); + } + } else if (delegate.isEmpty()) { + Collection newDelegate = map.get(key); + if (newDelegate != null) { + delegate = newDelegate; + } + } + } + + /** + * If collection is empty, remove it from {@code AbstractMapBasedMultimap.this.map}. For + * subcollections, check whether the ancestor collection is empty. + */ + void removeIfEmpty() { + if (ancestor != null) { + ancestor.removeIfEmpty(); + } else if (delegate.isEmpty()) { + map.remove(key); + } + } + + @ParametricNullness + K getKey() { + return key; + } + + /** + * Add the delegate to the map. Other {@code WrappedCollection} methods should call this method + * after adding elements to a previously empty collection. + * + *

Subcollection add the ancestor's delegate instead. + */ + void addToMap() { + if (ancestor != null) { + ancestor.addToMap(); + } else { + map.put(key, delegate); + } + } + + @Override + public int size() { + refreshIfEmpty(); + return delegate.size(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + refreshIfEmpty(); + return delegate.equals(object); + } + + @Override + public int hashCode() { + refreshIfEmpty(); + return delegate.hashCode(); + } + + @Override + public String toString() { + refreshIfEmpty(); + return delegate.toString(); + } + + Collection getDelegate() { + return delegate; + } + + @Override + public Iterator iterator() { + refreshIfEmpty(); + return new WrappedIterator(); + } + + @Override + public Spliterator spliterator() { + refreshIfEmpty(); + return delegate.spliterator(); + } + + /** Collection iterator for {@code WrappedCollection}. */ + class WrappedIterator implements Iterator { + final Iterator delegateIterator; + final Collection originalDelegate = delegate; + + WrappedIterator() { + delegateIterator = iteratorOrListIterator(delegate); + } + + WrappedIterator(Iterator delegateIterator) { + this.delegateIterator = delegateIterator; + } + + /** + * If the delegate changed since the iterator was created, the iterator is no longer valid. + */ + void validateIterator() { + refreshIfEmpty(); + if (delegate != originalDelegate) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + validateIterator(); + return delegateIterator.hasNext(); + } + + @Override + @ParametricNullness + public V next() { + validateIterator(); + return delegateIterator.next(); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize--; + removeIfEmpty(); + } + + Iterator getDelegateIterator() { + validateIterator(); + return delegateIterator; + } + } + + @Override + public boolean add(@ParametricNullness V value) { + refreshIfEmpty(); + boolean wasEmpty = delegate.isEmpty(); + boolean changed = delegate.add(value); + if (changed) { + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + return changed; + } + + WrappedCollection getAncestor() { + return ancestor; + } + + // The following methods are provided for better performance. + + @Override + public boolean addAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.addAll(collection); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override + public boolean contains(Object o) { + refreshIfEmpty(); + return delegate.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + refreshIfEmpty(); + return delegate.containsAll(c); + } + + @Override + public void clear() { + int oldSize = size(); // calls refreshIfEmpty + if (oldSize == 0) { + return; + } + delegate.clear(); + totalSize -= oldSize; + removeIfEmpty(); // maybe shouldn't be removed if this is a sublist + } + + @Override + public boolean remove(Object o) { + refreshIfEmpty(); + boolean changed = delegate.remove(o); + if (changed) { + totalSize--; + removeIfEmpty(); + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.removeAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.retainAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + } + + private static Iterator iteratorOrListIterator(Collection collection) { + return (collection instanceof List) + ? ((List) collection).listIterator() + : collection.iterator(); + } + + /** Set decorator that stays in sync with the multimap values for a key. */ + class WrappedSet extends WrappedCollection implements Set { + WrappedSet(@ParametricNullness K key, Set delegate) { + super(key, delegate, null); + } + + @Override + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + + // Guava issue 1013: AbstractSet and most JDK set implementations are + // susceptible to quadratic removeAll performance on lists; + // use a slightly smarter implementation here + boolean changed = Sets.removeAllImpl((Set) delegate, c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + } + + /** SortedSet decorator that stays in sync with the multimap values for a key. */ + class WrappedSortedSet extends WrappedCollection implements SortedSet { + WrappedSortedSet(@ParametricNullness K key, SortedSet delegate, WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + SortedSet getSortedSetDelegate() { + return (SortedSet) getDelegate(); + } + + @Override + public Comparator comparator() { + return getSortedSetDelegate().comparator(); + } + + @Override + @ParametricNullness + public V first() { + refreshIfEmpty(); + return getSortedSetDelegate().first(); + } + + @Override + @ParametricNullness + public V last() { + refreshIfEmpty(); + return getSortedSetDelegate().last(); + } + + @Override + public SortedSet headSet(@ParametricNullness V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), + getSortedSetDelegate().headSet(toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet subSet(@ParametricNullness V fromElement, @ParametricNullness V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), + getSortedSetDelegate().subSet(fromElement, toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet tailSet(@ParametricNullness V fromElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), + getSortedSetDelegate().tailSet(fromElement), + (getAncestor() == null) ? this : getAncestor()); + } + } + + class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet { + WrappedNavigableSet( + @ParametricNullness K key, NavigableSet delegate, WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + @Override + NavigableSet getSortedSetDelegate() { + return (NavigableSet) super.getSortedSetDelegate(); + } + + @Override + public V lower(@ParametricNullness V v) { + return getSortedSetDelegate().lower(v); + } + + @Override + public V floor(@ParametricNullness V v) { + return getSortedSetDelegate().floor(v); + } + + @Override + public V ceiling(@ParametricNullness V v) { + return getSortedSetDelegate().ceiling(v); + } + + @Override + public V higher(@ParametricNullness V v) { + return getSortedSetDelegate().higher(v); + } + + @Override + public V pollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public V pollLast() { + return Iterators.pollNext(descendingIterator()); + } + + private NavigableSet wrap(NavigableSet wrapped) { + return new WrappedNavigableSet(key, wrapped, (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public NavigableSet descendingSet() { + return wrap(getSortedSetDelegate().descendingSet()); + } + + @Override + public Iterator descendingIterator() { + return new WrappedIterator(getSortedSetDelegate().descendingIterator()); + } + + @Override + public NavigableSet subSet( + @ParametricNullness V fromElement, + boolean fromInclusive, + @ParametricNullness V toElement, + boolean toInclusive) { + return wrap( + getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet headSet(@ParametricNullness V toElement, boolean inclusive) { + return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); + } + + @Override + public NavigableSet tailSet(@ParametricNullness V fromElement, boolean inclusive) { + return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); + } + } + + /** List decorator that stays in sync with the multimap values for a key. */ + class WrappedList extends WrappedCollection implements List { + WrappedList(@ParametricNullness K key, List delegate, WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + List getListDelegate() { + return (List) getDelegate(); + } + + @Override + public boolean addAll(int index, Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = getListDelegate().addAll(index, c); + if (changed) { + int newSize = getDelegate().size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override + @ParametricNullness + public V get(int index) { + refreshIfEmpty(); + return getListDelegate().get(index); + } + + @Override + @ParametricNullness + public V set(int index, @ParametricNullness V element) { + refreshIfEmpty(); + return getListDelegate().set(index, element); + } + + @Override + public void add(int index, @ParametricNullness V element) { + refreshIfEmpty(); + boolean wasEmpty = getDelegate().isEmpty(); + getListDelegate().add(index, element); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + + @Override + @ParametricNullness + public V remove(int index) { + refreshIfEmpty(); + V value = getListDelegate().remove(index); + totalSize--; + removeIfEmpty(); + return value; + } + + @Override + public int indexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + refreshIfEmpty(); + return new WrappedListIterator(); + } + + @Override + public ListIterator listIterator(int index) { + refreshIfEmpty(); + return new WrappedListIterator(index); + } + + @Override + public List subList(int fromIndex, int toIndex) { + refreshIfEmpty(); + return wrapList( + getKey(), + getListDelegate().subList(fromIndex, toIndex), + (getAncestor() == null) ? this : getAncestor()); + } + + /** ListIterator decorator. */ + private class WrappedListIterator extends WrappedIterator implements ListIterator { + WrappedListIterator() {} + + public WrappedListIterator(int index) { + super(getListDelegate().listIterator(index)); + } + + private ListIterator getDelegateListIterator() { + return (ListIterator) getDelegateIterator(); + } + + @Override + public boolean hasPrevious() { + return getDelegateListIterator().hasPrevious(); + } + + @Override + @ParametricNullness + public V previous() { + return getDelegateListIterator().previous(); + } + + @Override + public int nextIndex() { + return getDelegateListIterator().nextIndex(); + } + + @Override + public int previousIndex() { + return getDelegateListIterator().previousIndex(); + } + + @Override + public void set(@ParametricNullness V value) { + getDelegateListIterator().set(value); + } + + @Override + public void add(@ParametricNullness V value) { + boolean wasEmpty = isEmpty(); + getDelegateListIterator().add(value); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + } + } + + /** + * List decorator that stays in sync with the multimap values for a key and supports rapid random + * access. + */ + private class RandomAccessWrappedList extends WrappedList implements RandomAccess { + RandomAccessWrappedList( + @ParametricNullness K key, List delegate, WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + } + + @Override + Set createKeySet() { + return new KeySet(map); + } + + final Set createMaybeNavigableKeySet() { + if (map instanceof NavigableMap) { + return new NavigableKeySet((NavigableMap>) map); + } else if (map instanceof SortedMap) { + return new SortedKeySet((SortedMap>) map); + } else { + return new KeySet(map); + } + } + + private class KeySet extends Maps.KeySet> { + KeySet(final Map> subMap) { + super(subMap); + } + + @Override + public Iterator iterator() { + final Iterator>> entryIterator = map().entrySet().iterator(); + return new Iterator() { + Entry> entry; + + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + @ParametricNullness + public K next() { + entry = entryIterator.next(); + return entry.getKey(); + } + + @Override + public void remove() { + checkState(entry != null, "no calls to next() since the last call to remove()"); + Collection collection = entry.getValue(); + entryIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + entry = null; + } + }; + } + + // The following methods are included for better performance. + + @Override + public Spliterator spliterator() { + return map().keySet().spliterator(); + } + + @Override + public boolean remove(Object key) { + int count = 0; + Collection collection = map().remove(key); + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count > 0; + } + + @Override + public void clear() { + Iterators.clear(iterator()); + } + + @Override + public boolean containsAll(Collection c) { + return map().keySet().containsAll(c); + } + + @Override + public boolean equals(Object object) { + return this == object || this.map().keySet().equals(object); + } + + @Override + public int hashCode() { + return map().keySet().hashCode(); + } + } + + private class SortedKeySet extends KeySet implements SortedSet { + + SortedKeySet(SortedMap> subMap) { + super(subMap); + } + + SortedMap> sortedMap() { + return (SortedMap>) super.map(); + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + @ParametricNullness + public K first() { + return sortedMap().firstKey(); + } + + @Override + public SortedSet headSet(@ParametricNullness K toElement) { + return new SortedKeySet(sortedMap().headMap(toElement)); + } + + @Override + @ParametricNullness + public K last() { + return sortedMap().lastKey(); + } + + @Override + public SortedSet subSet(@ParametricNullness K fromElement, @ParametricNullness K toElement) { + return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); + } + + @Override + public SortedSet tailSet(@ParametricNullness K fromElement) { + return new SortedKeySet(sortedMap().tailMap(fromElement)); + } + } + + class NavigableKeySet extends SortedKeySet implements NavigableSet { + NavigableKeySet(NavigableMap> subMap) { + super(subMap); + } + + @Override + NavigableMap> sortedMap() { + return (NavigableMap>) super.sortedMap(); + } + + @Override + public K lower(@ParametricNullness K k) { + return sortedMap().lowerKey(k); + } + + @Override + public K floor(@ParametricNullness K k) { + return sortedMap().floorKey(k); + } + + @Override + public K ceiling(@ParametricNullness K k) { + return sortedMap().ceilingKey(k); + } + + @Override + public K higher(@ParametricNullness K k) { + return sortedMap().higherKey(k); + } + + @Override + public K pollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public K pollLast() { + return Iterators.pollNext(descendingIterator()); + } + + @Override + public NavigableSet descendingSet() { + return new NavigableKeySet(sortedMap().descendingMap()); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public NavigableSet headSet(@ParametricNullness K toElement) { + return headSet(toElement, false); + } + + @Override + public NavigableSet headSet(@ParametricNullness K toElement, boolean inclusive) { + return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); + } + + @Override + public NavigableSet subSet( + @ParametricNullness K fromElement, @ParametricNullness K toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet subSet( + @ParametricNullness K fromElement, + boolean fromInclusive, + @ParametricNullness K toElement, + boolean toInclusive) { + return new NavigableKeySet( + sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet tailSet(@ParametricNullness K fromElement) { + return tailSet(fromElement, true); + } + + @Override + public NavigableSet tailSet(@ParametricNullness K fromElement, boolean inclusive) { + return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); + } + } + + /** Removes all values for the provided key. */ + private void removeValuesForKey(Object key) { + Collection collection = Maps.safeRemove(map, key); + + if (collection != null) { + int count = collection.size(); + collection.clear(); + totalSize -= count; + } + } + + private abstract class Itr implements Iterator { + final Iterator>> keyIterator; + K key; + Collection collection; + Iterator valueIterator; + + Itr() { + keyIterator = map.entrySet().iterator(); + key = null; + collection = null; + valueIterator = Iterators.emptyModifiableIterator(); + } + + abstract T output(@ParametricNullness K key, @ParametricNullness V value); + + @Override + public boolean hasNext() { + return keyIterator.hasNext() || valueIterator.hasNext(); + } + + @Override + @ParametricNullness + public T next() { + if (!valueIterator.hasNext()) { + Entry> mapEntry = keyIterator.next(); + key = mapEntry.getKey(); + collection = mapEntry.getValue(); + valueIterator = collection.iterator(); + } + /* + * uncheckedCastNullableTToT is safe: The first call to this method always enters the !hasNext() case and + * populates key, after which it's never cleared. + */ + return output(uncheckedCastNullableTToT(key), valueIterator.next()); + } + + @Override + public void remove() { + valueIterator.remove(); + /* + * requireNonNull is safe because we've already initialized `collection`. If we hadn't, then + * valueIterator.remove() would have failed. + */ + if (requireNonNull(collection).isEmpty()) { + keyIterator.remove(); + } + totalSize--; + } + } + + /** + * {@inheritDoc} + * + *

The iterator generated by the returned collection traverses the values for one key, followed + * by the values of a second key, and so on. + */ + @Override + public Collection values() { + return super.values(); + } + + @Override + Collection createValues() { + return new Values(); + } + + @Override + Iterator valueIterator() { + return new Itr() { + @Override + @ParametricNullness + V output(@ParametricNullness K key, @ParametricNullness V value) { + return value; + } + }; + } + + @Override + Spliterator valueSpliterator() { + return CollectSpliterators.flatMap( + map.values().spliterator(), Collection::spliterator, Spliterator.SIZED, size()); + } + + /* + * TODO(kevinb): should we copy this javadoc to each concrete class, so that + * classes like LinkedHashMultimap that need to say something different are + * still able to {@inheritDoc} all the way from Multimap? + */ + + @Override + Multiset createKeys() { + return new Multimaps.Keys(this); + } + + /** + * {@inheritDoc} + * + *

The iterator generated by the returned collection traverses the values for one key, followed + * by the values of a second key, and so on. + * + *

Each entry is an immutable snapshot of a key-value mapping in the multimap, taken at the + * time the entry is returned by a method call to the collection or its iterator. + */ + @Override + public Collection> entries() { + return super.entries(); + } + + @Override + Collection> createEntries() { + if (this instanceof SetMultimap) { + return new EntrySet(); + } else { + return new Entries(); + } + } + + /** + * Returns an iterator across all key-value map entries, used by {@code entries().iterator()} and + * {@code values().iterator()}. The default behavior, which traverses the values for one key, the + * values for a second key, and so on, suffices for most {@code AbstractMapBasedMultimap} + * implementations. + * + * @return an iterator across map entries + */ + @Override + Iterator> entryIterator() { + return new Itr>() { + @Override + Entry output(@ParametricNullness K key, @ParametricNullness V value) { + return Maps.immutableEntry(key, value); + } + }; + } + + @Override + Spliterator> entrySpliterator() { + return CollectSpliterators.flatMap( + map.entrySet().spliterator(), + keyToValueCollectionEntry -> { + K key = keyToValueCollectionEntry.getKey(); + Collection valueCollection = keyToValueCollectionEntry.getValue(); + return CollectSpliterators.map( + valueCollection.spliterator(), (V value) -> Maps.immutableEntry(key, value)); + }, + Spliterator.SIZED, + size()); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + map.forEach( + (key, valueCollection) -> valueCollection.forEach(value -> action.accept(key, value))); + } + + @Override + Map> createAsMap() { + return new AsMap(map); + } + + final Map> createMaybeNavigableAsMap() { + if (map instanceof NavigableMap) { + return new NavigableAsMap((NavigableMap>) map); + } else if (map instanceof SortedMap) { + return new SortedAsMap((SortedMap>) map); + } else { + return new AsMap(map); + } + } + + private class AsMap extends ViewCachingAbstractMap> { + /** + * Usually the same as map, but smaller for the headMap(), tailMap(), or subMap() of a + * SortedAsMap. + */ + final transient Map> submap; + + AsMap(Map> submap) { + this.submap = submap; + } + + @Override + protected Set>> createEntrySet() { + return new AsMapEntries(); + } + + // The following methods are included for performance. + + @Override + public boolean containsKey(Object key) { + return Maps.safeContainsKey(submap, key); + } + + @Override + public Collection get(Object key) { + Collection collection = Maps.safeGet(submap, key); + if (collection == null) { + return null; + } + @SuppressWarnings("unchecked") + K k = (K) key; + return wrapCollection(k, collection); + } + + @Override + public Set keySet() { + return AbstractMapBasedMultimap.this.keySet(); + } + + @Override + public int size() { + return submap.size(); + } + + @Override + public Collection remove(Object key) { + Collection collection = submap.remove(key); + if (collection == null) { + return null; + } + + Collection output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + return output; + } + + @Override + public boolean equals(Object object) { + return this == object || submap.equals(object); + } + + @Override + public int hashCode() { + return submap.hashCode(); + } + + @Override + public String toString() { + return submap.toString(); + } + + @Override + public void clear() { + if (submap == map) { + AbstractMapBasedMultimap.this.clear(); + } else { + Iterators.clear(new AsMapIterator()); + } + } + + Entry> wrapEntry(Entry> entry) { + K key = entry.getKey(); + return Maps.immutableEntry(key, wrapCollection(key, entry.getValue())); + } + + class AsMapEntries extends Maps.EntrySet> { + @Override + Map> map() { + return AsMap.this; + } + + @Override + public Iterator>> iterator() { + return new AsMapIterator(); + } + + @Override + public Spliterator>> spliterator() { + return CollectSpliterators.map(submap.entrySet().spliterator(), AsMap.this::wrapEntry); + } + + // The following methods are included for performance. + + @Override + public boolean contains(Object o) { + return Collections2.safeContains(submap.entrySet(), o); + } + + @Override + public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + // requireNonNull is safe because of the contains check. + Entry entry = requireNonNull((Entry) o); + removeValuesForKey(entry.getKey()); + return true; + } + } + + /** Iterator across all keys and value collections. */ + class AsMapIterator implements Iterator>> { + final Iterator>> delegateIterator = submap.entrySet().iterator(); + Collection collection; + + @Override + public boolean hasNext() { + return delegateIterator.hasNext(); + } + + @Override + public Entry> next() { + Entry> entry = delegateIterator.next(); + collection = entry.getValue(); + return wrapEntry(entry); + } + + @Override + public void remove() { + checkState(collection != null, "no calls to next() since the last call to remove()"); + delegateIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + collection = null; + } + } + } + + private class SortedAsMap extends AsMap implements SortedMap> { + SortedAsMap(SortedMap> submap) { + super(submap); + } + + SortedMap> sortedMap() { + return (SortedMap>) submap; + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + @ParametricNullness + public K firstKey() { + return sortedMap().firstKey(); + } + + @Override + @ParametricNullness + public K lastKey() { + return sortedMap().lastKey(); + } + + @Override + public SortedMap> headMap(@ParametricNullness K toKey) { + return new SortedAsMap(sortedMap().headMap(toKey)); + } + + @Override + public SortedMap> subMap( + @ParametricNullness K fromKey, @ParametricNullness K toKey) { + return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); + } + + @Override + public SortedMap> tailMap(@ParametricNullness K fromKey) { + return new SortedAsMap(sortedMap().tailMap(fromKey)); + } + + SortedSet sortedKeySet; + + // returns a SortedSet, even though returning a Set would be sufficient to + // satisfy the SortedMap.keySet() interface + @Override + public SortedSet keySet() { + SortedSet result = sortedKeySet; + return (result == null) ? sortedKeySet = createKeySet() : result; + } + + @Override + SortedSet createKeySet() { + return new SortedKeySet(sortedMap()); + } + } + + class NavigableAsMap extends SortedAsMap implements NavigableMap> { + + NavigableAsMap(NavigableMap> submap) { + super(submap); + } + + @Override + NavigableMap> sortedMap() { + return (NavigableMap>) super.sortedMap(); + } + + @Override + public Entry> lowerEntry(@ParametricNullness K key) { + Entry> entry = sortedMap().lowerEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K lowerKey(@ParametricNullness K key) { + return sortedMap().lowerKey(key); + } + + @Override + public Entry> floorEntry(@ParametricNullness K key) { + Entry> entry = sortedMap().floorEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K floorKey(@ParametricNullness K key) { + return sortedMap().floorKey(key); + } + + @Override + public Entry> ceilingEntry(@ParametricNullness K key) { + Entry> entry = sortedMap().ceilingEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K ceilingKey(@ParametricNullness K key) { + return sortedMap().ceilingKey(key); + } + + @Override + public Entry> higherEntry(@ParametricNullness K key) { + Entry> entry = sortedMap().higherEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K higherKey(@ParametricNullness K key) { + return sortedMap().higherKey(key); + } + + @Override + public Entry> firstEntry() { + Entry> entry = sortedMap().firstEntry(); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public Entry> lastEntry() { + Entry> entry = sortedMap().lastEntry(); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public Entry> pollFirstEntry() { + return pollAsMapEntry(entrySet().iterator()); + } + + @Override + public Entry> pollLastEntry() { + return pollAsMapEntry(descendingMap().entrySet().iterator()); + } + + Entry> pollAsMapEntry(Iterator>> entryIterator) { + if (!entryIterator.hasNext()) { + return null; + } + Entry> entry = entryIterator.next(); + Collection output = createCollection(); + output.addAll(entry.getValue()); + entryIterator.remove(); + return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); + } + + @Override + public NavigableMap> descendingMap() { + return new NavigableAsMap(sortedMap().descendingMap()); + } + + @Override + public NavigableSet keySet() { + return (NavigableSet) super.keySet(); + } + + @Override + NavigableSet createKeySet() { + return new NavigableKeySet(sortedMap()); + } + + @Override + public NavigableSet navigableKeySet() { + return keySet(); + } + + @Override + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + @Override + public NavigableMap> subMap( + @ParametricNullness K fromKey, @ParametricNullness K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap> subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + @Override + public NavigableMap> headMap(@ParametricNullness K toKey) { + return headMap(toKey, false); + } + + @Override + public NavigableMap> headMap(@ParametricNullness K toKey, boolean inclusive) { + return new NavigableAsMap(sortedMap().headMap(toKey, inclusive)); + } + + @Override + public NavigableMap> tailMap(@ParametricNullness K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public NavigableMap> tailMap( + @ParametricNullness K fromKey, boolean inclusive) { + return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); + } + } + + private static final long serialVersionUID = 2447537837011683357L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapBasedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapBasedMultiset.java new file mode 100644 index 0000000000000..d231cd6cf686d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapBasedMultiset.java @@ -0,0 +1,354 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.ObjIntConsumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * Basic implementation of {@code Multiset} backed by an instance of {@code Map}. + * + *

For serialization to work, the subclass must specify explicit {@code readObject} and {@code + * writeObject} methods. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class AbstractMapBasedMultiset extends AbstractMultiset + implements Serializable { + // TODO(lowasser): consider overhauling this back to Map + private transient Map backingMap; + + /* + * Cache the size for efficiency. Using a long lets us avoid the need for + * overflow checking and ensures that size() will function correctly even if + * the multiset had once been larger than Integer.MAX_VALUE. + */ + private transient long size; + + /** Standard constructor. */ + protected AbstractMapBasedMultiset(Map backingMap) { + checkArgument(backingMap.isEmpty()); + this.backingMap = backingMap; + } + + /** Used during deserialization only. The backing map must be empty. */ + void setBackingMap(Map backingMap) { + this.backingMap = backingMap; + } + + // Required Implementations + + /** + * {@inheritDoc} + * + *

Invoking {@link Multiset.Entry#getCount} on an entry in the returned set always returns the + * current count of that element in the multiset, as opposed to the count at the time the entry + * was retrieved. + */ + @Override + public Set> entrySet() { + return super.entrySet(); + } + + @Override + Iterator elementIterator() { + final Iterator> backingEntries = backingMap.entrySet().iterator(); + return new Iterator() { + Map.Entry toRemove; + + @Override + public boolean hasNext() { + return backingEntries.hasNext(); + } + + @Override + @ParametricNullness + public E next() { + final Map.Entry mapEntry = backingEntries.next(); + toRemove = mapEntry; + return mapEntry.getKey(); + } + + @Override + public void remove() { + checkState(toRemove != null, "no calls to next() since the last call to remove()"); + size -= toRemove.getValue().getAndSet(0); + backingEntries.remove(); + toRemove = null; + } + }; + } + + @Override + Iterator> entryIterator() { + final Iterator> backingEntries = backingMap.entrySet().iterator(); + return new Iterator>() { + Map.Entry toRemove; + + @Override + public boolean hasNext() { + return backingEntries.hasNext(); + } + + @Override + public Multiset.Entry next() { + final Map.Entry mapEntry = backingEntries.next(); + toRemove = mapEntry; + return new Multisets.AbstractEntry() { + @Override + @ParametricNullness + public E getElement() { + return mapEntry.getKey(); + } + + @Override + public int getCount() { + Count count = mapEntry.getValue(); + if (count == null || count.get() == 0) { + Count frequency = backingMap.get(getElement()); + if (frequency != null) { + return frequency.get(); + } + } + return (count == null) ? 0 : count.get(); + } + }; + } + + @Override + public void remove() { + checkState(toRemove != null, "no calls to next() since the last call to remove()"); + size -= toRemove.getValue().getAndSet(0); + backingEntries.remove(); + toRemove = null; + } + }; + } + + @Override + public void forEachEntry(ObjIntConsumer action) { + checkNotNull(action); + backingMap.forEach((element, count) -> action.accept(element, count.get())); + } + + @Override + public void clear() { + for (Count frequency : backingMap.values()) { + frequency.set(0); + } + backingMap.clear(); + size = 0L; + } + + @Override + int distinctElements() { + return backingMap.size(); + } + + // Optimizations - Query Operations + + @Override + public int size() { + return Ints.saturatedCast(size); + } + + @Override + public Iterator iterator() { + return new MapBasedMultisetIterator(); + } + + /* + * Not subclassing AbstractMultiset$MultisetIterator because next() needs to + * retrieve the Map.Entry entry, which can then be used for + * a more efficient remove() call. + */ + private class MapBasedMultisetIterator implements Iterator { + final Iterator> entryIterator; + Map.Entry currentEntry; + int occurrencesLeft; + boolean canRemove; + + MapBasedMultisetIterator() { + this.entryIterator = backingMap.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return occurrencesLeft > 0 || entryIterator.hasNext(); + } + + @Override + @ParametricNullness + public E next() { + if (occurrencesLeft == 0) { + currentEntry = entryIterator.next(); + occurrencesLeft = currentEntry.getValue().get(); + } + occurrencesLeft--; + canRemove = true; + /* + * requireNonNull is safe because occurrencesLeft starts at 0, forcing us to initialize + * currentEntry above. After that, we never clear it. + */ + return requireNonNull(currentEntry).getKey(); + } + + @Override + public void remove() { + checkRemove(canRemove); + /* + * requireNonNull is safe because canRemove is set to true only after we initialize + * currentEntry (which we never subsequently clear). + */ + int frequency = requireNonNull(currentEntry).getValue().get(); + if (frequency <= 0) { + throw new ConcurrentModificationException(); + } + if (currentEntry.getValue().addAndGet(-1) == 0) { + entryIterator.remove(); + } + size--; + canRemove = false; + } + } + + @Override + public int count(Object element) { + Count frequency = Maps.safeGet(backingMap, element); + return (frequency == null) ? 0 : frequency.get(); + } + + // Optional Operations - Modification Operations + + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if the call would result in more than {@link + * Integer#MAX_VALUE} occurrences of {@code element} in this multiset. + */ + @Override + public int add(@ParametricNullness E element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); + Count frequency = backingMap.get(element); + int oldCount; + if (frequency == null) { + oldCount = 0; + backingMap.put(element, new Count(occurrences)); + } else { + oldCount = frequency.get(); + long newCount = (long) oldCount + (long) occurrences; + checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount); + frequency.add(occurrences); + } + size += occurrences; + return oldCount; + } + + @Override + public int remove(Object element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); + Count frequency = backingMap.get(element); + if (frequency == null) { + return 0; + } + + int oldCount = frequency.get(); + + int numberRemoved; + if (oldCount > occurrences) { + numberRemoved = occurrences; + } else { + numberRemoved = oldCount; + backingMap.remove(element); + } + + frequency.add(-numberRemoved); + size -= numberRemoved; + return oldCount; + } + + // Roughly a 33% performance improvement over AbstractMultiset.setCount(). + @Override + public int setCount(@ParametricNullness E element, int count) { + checkNonnegative(count, "count"); + + Count existingCounter; + int oldCount; + if (count == 0) { + existingCounter = backingMap.remove(element); + oldCount = getAndSet(existingCounter, count); + } else { + existingCounter = backingMap.get(element); + oldCount = getAndSet(existingCounter, count); + + if (existingCounter == null) { + backingMap.put(element, new Count(count)); + } + } + + size += (count - oldCount); + return oldCount; + } + + private static int getAndSet(Count i, int count) { + if (i == null) { + return 0; + } + + return i.getAndSet(count); + } + + // Don't allow default serialization. + @GwtIncompatible // java.io.ObjectStreamException + @J2ktIncompatible + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Stream data required"); + } + + @GwtIncompatible // not needed in emulated source. + @J2ktIncompatible + private static final long serialVersionUID = -2250766705698539974L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapEntry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapEntry.java new file mode 100644 index 0000000000000..f9392852ef6d7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMapEntry.java @@ -0,0 +1,73 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.Map.Entry; + +/** + * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} methods of {@code + * Entry}. + * + * @author Jared Levy + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractMapEntry implements Entry { + + @Override + @ParametricNullness + public abstract K getKey(); + + @Override + @ParametricNullness + public abstract V getValue(); + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equal(this.getKey(), that.getKey()) + && Objects.equal(this.getValue(), that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + K k = getKey(); + V v = getValue(); + return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); + } + + /** Returns a string representation of the form {@code {key}={value}}. */ + @Override + public String toString() { + return getKey() + "=" + getValue(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMultimap.java new file mode 100644 index 0000000000000..e808da28298a0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMultimap.java @@ -0,0 +1,258 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractMultimap implements Multimap { + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsValue(Object value) { + for (Collection collection : asMap().values()) { + if (collection.contains(value)) { + return true; + } + } + + return false; + } + + @Override + public boolean containsEntry(Object key, Object value) { + Collection collection = asMap().get(key); + return collection != null && collection.contains(value); + } + + @Override + public boolean remove(Object key, Object value) { + Collection collection = asMap().get(key); + return collection != null && collection.remove(value); + } + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + return get(key).add(value); + } + + @Override + public boolean putAll(@ParametricNullness K key, Iterable values) { + checkNotNull(values); + // make sure we only call values.iterator() once + // and we only call get(key) if values is nonempty + if (values instanceof Collection) { + Collection valueCollection = (Collection) values; + return !valueCollection.isEmpty() && get(key).addAll(valueCollection); + } else { + Iterator valueItr = values.iterator(); + return valueItr.hasNext() && Iterators.addAll(get(key), valueItr); + } + } + + @Override + public boolean putAll(Multimap multimap) { + boolean changed = false; + for (Entry entry : multimap.entries()) { + changed |= put(entry.getKey(), entry.getValue()); + } + return changed; + } + + @Override + public Collection replaceValues(@ParametricNullness K key, Iterable values) { + checkNotNull(values); + Collection result = removeAll(key); + putAll(key, values); + return result; + } + + private transient Collection> entries; + + @Override + public Collection> entries() { + Collection> result = entries; + return (result == null) ? entries = createEntries() : result; + } + + abstract Collection> createEntries(); + + class Entries extends Multimaps.Entries { + @Override + Multimap multimap() { + return AbstractMultimap.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public Spliterator> spliterator() { + return entrySpliterator(); + } + } + + class EntrySet extends Entries implements Set> { + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(Object obj) { + return Sets.equalsImpl(this, obj); + } + } + + abstract Iterator> entryIterator(); + + Spliterator> entrySpliterator() { + return Spliterators.spliterator( + entryIterator(), size(), (this instanceof SetMultimap) ? Spliterator.DISTINCT : 0); + } + + private transient Set keySet; + + @Override + public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + abstract Set createKeySet(); + + private transient Multiset keys; + + @Override + public Multiset keys() { + Multiset result = keys; + return (result == null) ? keys = createKeys() : result; + } + + abstract Multiset createKeys(); + + private transient Collection values; + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + abstract Collection createValues(); + + class Values extends AbstractCollection { + @Override + public Iterator iterator() { + return valueIterator(); + } + + @Override + public Spliterator spliterator() { + return valueSpliterator(); + } + + @Override + public int size() { + return AbstractMultimap.this.size(); + } + + @Override + public boolean contains(Object o) { + return AbstractMultimap.this.containsValue(o); + } + + @Override + public void clear() { + AbstractMultimap.this.clear(); + } + } + + Iterator valueIterator() { + return Maps.valueIterator(entries().iterator()); + } + + Spliterator valueSpliterator() { + return Spliterators.spliterator(valueIterator(), size(), 0); + } + + private transient Map> asMap; + + @Override + public Map> asMap() { + Map> result = asMap; + return (result == null) ? asMap = createAsMap() : result; + } + + abstract Map> createAsMap(); + + // Comparison and hashing + + @Override + public boolean equals(Object object) { + return Multimaps.equalsImpl(this, object); + } + + /** + * Returns the hash code for this multimap. + * + *

The hash code of a multimap is defined as the hash code of the map view, as returned by + * {@link Multimap#asMap}. + * + * @see Map#hashCode + */ + @Override + public int hashCode() { + return asMap().hashCode(); + } + + /** + * Returns a string representation of the multimap, generated by calling {@code toString} on the + * map returned by {@link Multimap#asMap}. + * + * @return a string representation of the multimap + */ + @Override + public String toString() { + return asMap().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMultiset.java new file mode 100644 index 0000000000000..9f953c88a2c78 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractMultiset.java @@ -0,0 +1,222 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import static org.apache.iotdb.google.common.collect.Multisets.setCountImpl; + +/** + * This class provides a skeletal implementation of the {@link Multiset} interface. A new multiset + * implementation can be created easily by extending this class and implementing the {@link + * Multiset#entrySet()} method, plus optionally overriding {@link #add(Object, int)} and {@link + * #remove(Object, int)} to enable modifications to the multiset. + * + *

The {@link #count} and {@link #size} implementations all iterate across the set returned by + * {@link Multiset#entrySet()}, as do many methods acting on the set returned by {@link + * #elementSet()}. Override those methods for better performance. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractMultiset extends AbstractCollection + implements Multiset { + // Query Operations + + @Override + public boolean isEmpty() { + return entrySet().isEmpty(); + } + + @Override + public boolean contains(Object element) { + return count(element) > 0; + } + + // Modification Operations + @Override + public final boolean add(@ParametricNullness E element) { + add(element, 1); + return true; + } + + @Override + public int add(@ParametricNullness E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public final boolean remove(Object element) { + return remove(element, 1) > 0; + } + + @Override + public int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public int setCount(@ParametricNullness E element, int count) { + return setCountImpl(this, element, count); + } + + @Override + public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { + return setCountImpl(this, element, oldCount, newCount); + } + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

This implementation is highly efficient when {@code elementsToAdd} is itself a {@link + * Multiset}. + */ + @Override + public final boolean addAll(Collection elementsToAdd) { + return Multisets.addAllImpl(this, elementsToAdd); + } + + @Override + public final boolean removeAll(Collection elementsToRemove) { + return Multisets.removeAllImpl(this, elementsToRemove); + } + + @Override + public final boolean retainAll(Collection elementsToRetain) { + return Multisets.retainAllImpl(this, elementsToRetain); + } + + @Override + public abstract void clear(); + + // Views + + private transient Set elementSet; + + @Override + public Set elementSet() { + Set result = elementSet; + if (result == null) { + elementSet = result = createElementSet(); + } + return result; + } + + /** + * Creates a new instance of this multiset's element set, which will be returned by {@link + * #elementSet()}. + */ + Set createElementSet() { + return new ElementSet(); + } + + class ElementSet extends Multisets.ElementSet { + @Override + Multiset multiset() { + return AbstractMultiset.this; + } + + @Override + public Iterator iterator() { + return elementIterator(); + } + } + + abstract Iterator elementIterator(); + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + if (result == null) { + entrySet = result = createEntrySet(); + } + return result; + } + + class EntrySet extends Multisets.EntrySet { + @Override + Multiset multiset() { + return AbstractMultiset.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public int size() { + return distinctElements(); + } + } + + Set> createEntrySet() { + return new EntrySet(); + } + + abstract Iterator> entryIterator(); + + abstract int distinctElements(); + + // Object methods + + /** + * {@inheritDoc} + * + *

This implementation returns {@code true} if {@code object} is a multiset of the same size + * and if, for each element, the two multisets have the same count. + */ + @Override + public final boolean equals(Object object) { + return Multisets.equalsImpl(this, object); + } + + /** + * {@inheritDoc} + * + *

This implementation returns the hash code of {@link Multiset#entrySet()}. + */ + @Override + public final int hashCode() { + return entrySet().hashCode(); + } + + /** + * {@inheritDoc} + * + *

This implementation returns the result of invoking {@code toString} on {@link + * Multiset#entrySet()}. + */ + @Override + public final String toString() { + return entrySet().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractNavigableMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractNavigableMap.java new file mode 100644 index 0000000000000..5ae97c006d2b3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractNavigableMap.java @@ -0,0 +1,175 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.collect.Maps.IteratorBasedAbstractMap; + +import java.util.Iterator; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedMap; + +/** + * Skeletal implementation of {@link NavigableMap}. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractNavigableMap + extends IteratorBasedAbstractMap implements NavigableMap { + + @Override + public abstract V get(Object key); + + @Override + public Entry firstEntry() { + return Iterators.>getNext(entryIterator(), null); + } + + @Override + public Entry lastEntry() { + return Iterators.>getNext(descendingEntryIterator(), null); + } + + @Override + public Entry pollFirstEntry() { + return Iterators.pollNext(entryIterator()); + } + + @Override + public Entry pollLastEntry() { + return Iterators.pollNext(descendingEntryIterator()); + } + + @Override + @ParametricNullness + public K firstKey() { + Entry entry = firstEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + @ParametricNullness + public K lastKey() { + Entry entry = lastEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + public Entry lowerEntry(@ParametricNullness K key) { + return headMap(key, false).lastEntry(); + } + + @Override + public Entry floorEntry(@ParametricNullness K key) { + return headMap(key, true).lastEntry(); + } + + @Override + public Entry ceilingEntry(@ParametricNullness K key) { + return tailMap(key, true).firstEntry(); + } + + @Override + public Entry higherEntry(@ParametricNullness K key) { + return tailMap(key, false).firstEntry(); + } + + @Override + public K lowerKey(@ParametricNullness K key) { + return Maps.keyOrNull(lowerEntry(key)); + } + + @Override + public K floorKey(@ParametricNullness K key) { + return Maps.keyOrNull(floorEntry(key)); + } + + @Override + public K ceilingKey(@ParametricNullness K key) { + return Maps.keyOrNull(ceilingEntry(key)); + } + + @Override + public K higherKey(@ParametricNullness K key) { + return Maps.keyOrNull(higherEntry(key)); + } + + abstract Iterator> descendingEntryIterator(); + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return headMap(toKey, false); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public NavigableSet navigableKeySet() { + return new Maps.NavigableKeySet<>(this); + } + + @Override + public Set keySet() { + return navigableKeySet(); + } + + @Override + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + @Override + public NavigableMap descendingMap() { + return new DescendingMap(); + } + + private final class DescendingMap extends Maps.DescendingMap { + @Override + NavigableMap forward() { + return AbstractNavigableMap.this; + } + + @Override + Iterator> entryIterator() { + return descendingEntryIterator(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractRangeSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractRangeSet.java new file mode 100644 index 0000000000000..b260ae83492f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractRangeSet.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +/** + * A skeletal implementation of {@code RangeSet}. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractRangeSet implements RangeSet { + AbstractRangeSet() {} + + @Override + public boolean contains(C value) { + return rangeContaining(value) != null; + } + + @Override + public abstract Range rangeContaining(C value); + + @Override + public boolean isEmpty() { + return asRanges().isEmpty(); + } + + @Override + public void add(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + remove(Range.all()); + } + + @Override + public boolean enclosesAll(RangeSet other) { + return enclosesAll(other.asRanges()); + } + + @Override + public void addAll(RangeSet other) { + addAll(other.asRanges()); + } + + @Override + public void removeAll(RangeSet other) { + removeAll(other.asRanges()); + } + + @Override + public boolean intersects(Range otherRange) { + return !subRangeSet(otherRange).isEmpty(); + } + + @Override + public abstract boolean encloses(Range otherRange); + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof RangeSet) { + RangeSet other = (RangeSet) obj; + return this.asRanges().equals(other.asRanges()); + } + return false; + } + + @Override + public final int hashCode() { + return asRanges().hashCode(); + } + + @Override + public final String toString() { + return asRanges().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSequentialIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSequentialIterator.java new file mode 100644 index 0000000000000..e1ce1efb1537e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSequentialIterator.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.NoSuchElementException; + +/** + * This class provides a skeletal implementation of the {@code Iterator} interface for sequences + * whose next element can always be derived from the previous element. Null elements are not + * supported, nor is the {@link #remove()} method. + * + *

Example: + * + *

{@code
+ * Iterator powersOfTwo =
+ *     new AbstractSequentialIterator(1) {
+ *       protected Integer computeNext(Integer previous) {
+ *         return (previous == 1 << 30) ? null : previous * 2;
+ *       }
+ *     };
+ * }
+ * + * @author Chris Povirk + * @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0) + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractSequentialIterator extends UnmodifiableIterator { + private T nextOrNull; + + /** + * Creates a new iterator with the given first element, or, if {@code firstOrNull} is null, + * creates a new empty iterator. + */ + protected AbstractSequentialIterator(T firstOrNull) { + this.nextOrNull = firstOrNull; + } + + /** + * Returns the element that follows {@code previous}, or returns {@code null} if no elements + * remain. This method is invoked during each call to {@link #next()} in order to compute the + * result of a future call to {@code next()}. + */ + protected abstract T computeNext(T previous); + + @Override + public final boolean hasNext() { + return nextOrNull != null; + } + + @Override + public final T next() { + if (nextOrNull == null) { + throw new NoSuchElementException(); + } + T oldNext = nextOrNull; + nextOrNull = computeNext(oldNext); + return oldNext; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSetMultimap.java new file mode 100644 index 0000000000000..c941dc60fbb51 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSetMultimap.java @@ -0,0 +1,152 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Basic implementation of the {@link SetMultimap} interface. It's a wrapper around {@link + * AbstractMapBasedMultimap} that converts the returned collections into {@code Sets}. The {@link + * #createCollection} method must return a {@code Set}. + * + * @author Jared Levy + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractSetMultimap + extends AbstractMapBasedMultimap implements SetMultimap { + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding values + */ + protected AbstractSetMultimap(Map> map) { + super(map); + } + + @Override + abstract Set createCollection(); + + @Override + Set createUnmodifiableEmptyCollection() { + return Collections.emptySet(); + } + + @Override + Collection unmodifiableCollectionSubclass(Collection collection) { + return Collections.unmodifiableSet((Set) collection); + } + + @Override + Collection wrapCollection(@ParametricNullness K key, Collection collection) { + return new WrappedSet(key, (Set) collection); + } + + // Following Javadoc copied from SetMultimap. + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public Set get(@ParametricNullness K key) { + return (Set) super.get(key); + } + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public Set> entries() { + return (Set>) super.entries(); + } + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. + * + *

Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + + /** + * {@inheritDoc} + * + *

Though the method signature doesn't say so explicitly, the returned map has {@link Set} + * values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} if the method increased the size of the multimap, or {@code false} if the + * multimap already contained the key-value pair + */ + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + return super.put(key, value); + } + + /** + * Compares the specified object to this multimap for equality. + * + *

Two {@code SetMultimap} instances are equal if, for each key, they contain the same values. + * Equality does not depend on the ordering of keys or values. + */ + @Override + public boolean equals(Object object) { + return super.equals(object); + } + + private static final long serialVersionUID = 7431625294878419160L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedKeySortedSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedKeySortedSetMultimap.java new file mode 100644 index 0000000000000..1ce317e6222e7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedKeySortedSetMultimap.java @@ -0,0 +1,65 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +/** + * Basic implementation of a {@link SortedSetMultimap} with a sorted key set. + * + *

This superclass allows {@code TreeMultimap} to override methods to return navigable set and + * map types in non-GWT only, while GWT code will inherit the SortedMap/SortedSet overrides. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractSortedKeySortedSetMultimap + extends AbstractSortedSetMultimap { + + AbstractSortedKeySortedSetMultimap(SortedMap> map) { + super(map); + } + + @Override + public SortedMap> asMap() { + return (SortedMap>) super.asMap(); + } + + @Override + SortedMap> backingMap() { + return (SortedMap>) super.backingMap(); + } + + @Override + public SortedSet keySet() { + return (SortedSet) super.keySet(); + } + + @Override + Set createKeySet() { + return createMaybeNavigableKeySet(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedMultiset.java new file mode 100644 index 0000000000000..5d39b48a5e81c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedMultiset.java @@ -0,0 +1,151 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * This class provides a skeletal implementation of the {@link SortedMultiset} interface. + * + *

The {@link #count} and {@link #size} implementations all iterate across the set returned by + * {@link Multiset#entrySet()}, as do many methods acting on the set returned by {@link + * #elementSet()}. Override those methods for better performance. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class AbstractSortedMultiset extends AbstractMultiset + implements SortedMultiset { + @GwtTransient final Comparator comparator; + + // needed for serialization + @SuppressWarnings("unchecked") + AbstractSortedMultiset() { + this((Comparator) Ordering.natural()); + } + + AbstractSortedMultiset(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public NavigableSet elementSet() { + return (NavigableSet) super.elementSet(); + } + + @Override + NavigableSet createElementSet() { + return new SortedMultisets.NavigableElementSet<>(this); + } + + @Override + public Comparator comparator() { + return comparator; + } + + @Override + public Entry firstEntry() { + Iterator> entryIterator = entryIterator(); + return entryIterator.hasNext() ? entryIterator.next() : null; + } + + @Override + public Entry lastEntry() { + Iterator> entryIterator = descendingEntryIterator(); + return entryIterator.hasNext() ? entryIterator.next() : null; + } + + @Override + public Entry pollFirstEntry() { + Iterator> entryIterator = entryIterator(); + if (entryIterator.hasNext()) { + Entry result = entryIterator.next(); + result = Multisets.immutableEntry(result.getElement(), result.getCount()); + entryIterator.remove(); + return result; + } + return null; + } + + @Override + public Entry pollLastEntry() { + Iterator> entryIterator = descendingEntryIterator(); + if (entryIterator.hasNext()) { + Entry result = entryIterator.next(); + result = Multisets.immutableEntry(result.getElement(), result.getCount()); + entryIterator.remove(); + return result; + } + return null; + } + + @Override + public SortedMultiset subMultiset( + @ParametricNullness E fromElement, + BoundType fromBoundType, + @ParametricNullness E toElement, + BoundType toBoundType) { + // These are checked elsewhere, but NullPointerTester wants them checked eagerly. + checkNotNull(fromBoundType); + checkNotNull(toBoundType); + return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType); + } + + abstract Iterator> descendingEntryIterator(); + + Iterator descendingIterator() { + return Multisets.iteratorImpl(descendingMultiset()); + } + + private transient SortedMultiset descendingMultiset; + + @Override + public SortedMultiset descendingMultiset() { + SortedMultiset result = descendingMultiset; + return (result == null) ? descendingMultiset = createDescendingMultiset() : result; + } + + SortedMultiset createDescendingMultiset() { + class DescendingMultisetImpl extends DescendingMultiset { + @Override + SortedMultiset forwardMultiset() { + return AbstractSortedMultiset.this; + } + + @Override + Iterator> entryIterator() { + return descendingEntryIterator(); + } + + @Override + public Iterator iterator() { + return descendingIterator(); + } + } + return new DescendingMultisetImpl(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedSetMultimap.java new file mode 100644 index 0000000000000..1deedc5c4e84b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractSortedSetMultimap.java @@ -0,0 +1,149 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.NavigableSet; +import java.util.SortedSet; + +/** + * Basic implementation of the {@link SortedSetMultimap} interface. It's a wrapper around {@link + * AbstractMapBasedMultimap} that converts the returned collections into sorted sets. The {@link + * #createCollection} method must return a {@code SortedSet}. + * + * @author Jared Levy + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractSortedSetMultimap + extends AbstractSetMultimap implements SortedSetMultimap { + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding values + */ + protected AbstractSortedSetMultimap(Map> map) { + super(map); + } + + @Override + abstract SortedSet createCollection(); + + @Override + SortedSet createUnmodifiableEmptyCollection() { + return unmodifiableCollectionSubclass(createCollection()); + } + + @Override + SortedSet unmodifiableCollectionSubclass(Collection collection) { + if (collection instanceof NavigableSet) { + return Sets.unmodifiableNavigableSet((NavigableSet) collection); + } else { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } + } + + @Override + Collection wrapCollection(@ParametricNullness K key, Collection collection) { + if (collection instanceof NavigableSet) { + return new WrappedNavigableSet(key, (NavigableSet) collection, null); + } else { + return new WrappedSortedSet(key, (SortedSet) collection, null); + } + } + + // Following Javadoc copied from Multimap and SortedSetMultimap. + + /** + * Returns a collection view of all values associated with a key. If no mappings in the multimap + * have the provided key, an empty collection is returned. + * + *

Changes to the returned collection will update the underlying multimap, and vice versa. + * + *

Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method + * returns a {@link SortedSet}, instead of the {@link Collection} specified in the {@link + * Multimap} interface. + */ + @Override + public SortedSet get(@ParametricNullness K key) { + return (SortedSet) super.get(key); + } + + /** + * Removes all values associated with a given key. The returned collection is immutable. + * + *

Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method + * returns a {@link SortedSet}, instead of the {@link Collection} specified in the {@link + * Multimap} interface. + */ + @Override + public SortedSet removeAll(Object key) { + return (SortedSet) super.removeAll(key); + } + + /** + * Stores a collection of values with the same key, replacing any existing values for that key. + * The returned collection is immutable. + * + *

Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method + * returns a {@link SortedSet}, instead of the {@link Collection} specified in the {@link + * Multimap} interface. + * + *

Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + public SortedSet replaceValues(@ParametricNullness K key, Iterable values) { + return (SortedSet) super.replaceValues(key, values); + } + + /** + * Returns a map view that associates each key with the corresponding values in the multimap. + * Changes to the returned map, such as element removal, will update the underlying multimap. The + * map does not support {@code setValue} on its entries, {@code put}, or {@code putAll}. + * + *

When passed a key that is present in the map, {@code asMap().get(Object)} has the same + * behavior as {@link #get}, returning a live collection. When passed a key that is not present, + * however, {@code asMap().get(Object)} returns {@code null} instead of an empty collection. + * + *

Though the method signature doesn't say so explicitly, the returned map has {@link + * SortedSet} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * {@inheritDoc} + * + *

Consequently, the values do not follow their natural ordering or the ordering of the value + * comparator. + */ + @Override + public Collection values() { + return super.values(); + } + + private static final long serialVersionUID = 430848587173315748L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractTable.java new file mode 100644 index 0000000000000..cadaab636828d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AbstractTable.java @@ -0,0 +1,243 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Spliterator; + +/** + * Skeletal, implementation-agnostic implementation of the {@link Table} interface. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AbstractTable + implements Table { + + @Override + public boolean containsRow(Object rowKey) { + return Maps.safeContainsKey(rowMap(), rowKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return Maps.safeContainsKey(columnMap(), columnKey); + } + + @Override + public Set rowKeySet() { + return rowMap().keySet(); + } + + @Override + public Set columnKeySet() { + return columnMap().keySet(); + } + + @Override + public boolean containsValue(Object value) { + for (Map row : rowMap().values()) { + if (row.containsValue(value)) { + return true; + } + } + return false; + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return row != null && Maps.safeContainsKey(row, columnKey); + } + + @Override + public V get(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return (row == null) ? null : Maps.safeGet(row, columnKey); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public void clear() { + Iterators.clear(cellSet().iterator()); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return (row == null) ? null : Maps.safeRemove(row, columnKey); + } + + @Override + public V put( + @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { + return row(rowKey).put(columnKey, value); + } + + @Override + public void putAll(Table table) { + for (Table.Cell cell : table.cellSet()) { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + } + + private transient Set> cellSet; + + @Override + public Set> cellSet() { + Set> result = cellSet; + return (result == null) ? cellSet = createCellSet() : result; + } + + Set> createCellSet() { + return new CellSet(); + } + + abstract Iterator> cellIterator(); + + abstract Spliterator> cellSpliterator(); + + class CellSet extends AbstractSet> { + @Override + public boolean contains(Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null + && Collections2.safeContains( + row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null + && Collections2.safeRemove( + row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public void clear() { + AbstractTable.this.clear(); + } + + @Override + public Iterator> iterator() { + return cellIterator(); + } + + @Override + public Spliterator> spliterator() { + return cellSpliterator(); + } + + @Override + public int size() { + return AbstractTable.this.size(); + } + } + + private transient Collection values; + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + Collection createValues() { + return new Values(); + } + + Iterator valuesIterator() { + return new TransformedIterator, V>(cellSet().iterator()) { + @Override + @ParametricNullness + V transform(Cell cell) { + return cell.getValue(); + } + }; + } + + Spliterator valuesSpliterator() { + return CollectSpliterators.map(cellSpliterator(), Table.Cell::getValue); + } + + class Values extends AbstractCollection { + @Override + public Iterator iterator() { + return valuesIterator(); + } + + @Override + public Spliterator spliterator() { + return valuesSpliterator(); + } + + @Override + public boolean contains(Object o) { + return containsValue(o); + } + + @Override + public void clear() { + AbstractTable.this.clear(); + } + + @Override + public int size() { + return AbstractTable.this.size(); + } + } + + @Override + public boolean equals(Object obj) { + return Tables.equalsImpl(this, obj); + } + + @Override + public int hashCode() { + return cellSet().hashCode(); + } + + /** Returns the string representation {@code rowMap().toString()}. */ + @Override + public String toString() { + return rowMap().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AllEqualOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AllEqualOrdering.java new file mode 100644 index 0000000000000..20de8f077db23 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/AllEqualOrdering.java @@ -0,0 +1,68 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.List; + +/** + * An ordering that treats all references as equals, even nulls. + * + * @author Emily Soldal + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class AllEqualOrdering extends Ordering implements Serializable { + static final AllEqualOrdering INSTANCE = new AllEqualOrdering(); + + @Override + public int compare(Object left, Object right) { + return 0; + } + + @Override + public List sortedCopy(Iterable iterable) { + return Lists.newArrayList(iterable); + } + + @Override + public ImmutableList immutableSortedCopy(Iterable iterable) { + return ImmutableList.copyOf(iterable); + } + + @SuppressWarnings("unchecked") + @Override + public Ordering reverse() { + return (Ordering) this; + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.allEqual()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayListMultimap.java new file mode 100644 index 0000000000000..89a44c20ffac9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayListMultimap.java @@ -0,0 +1,181 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Implementation of {@code Multimap} that uses an {@code ArrayList} to store the values for a given + * key. A {@link HashMap} associates each key with an {@link ArrayList} of values. + * + *

When iterating through the collections supplied by this class, the ordering of values for a + * given key agrees with the order in which the values were added. + * + *

This multimap allows duplicate key-value pairs. After adding a new key-value pair equal to an + * existing key-value pair, the {@code ArrayListMultimap} will contain entries for both the new + * value and the old value. + * + *

Keys and values may be null. All optional multimap methods are supported, and all returned + * views are modifiable. + * + *

The lists returned by {@link #get}, {@link #removeAll}, and {@link #replaceValues} all + * implement {@link java.util.RandomAccess}. + * + *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent + * read operations will work correctly. To allow concurrent update operations, wrap your multimap + * with a call to {@link Multimaps#synchronizedListMultimap}. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public final class ArrayListMultimap + extends ArrayListMultimapGwtSerializationDependencies { + // Default from ArrayList + private static final int DEFAULT_VALUES_PER_KEY = 3; + + @VisibleForTesting transient int expectedValuesPerKey; + + /** + * Creates a new, empty {@code ArrayListMultimap} with the default initial capacities. + * + *

This method will soon be deprecated in favor of {@code + * MultimapBuilder.hashKeys().arrayListValues().build()}. + */ + public static ArrayListMultimap create() { + return new ArrayListMultimap<>(); + } + + /** + * Constructs an empty {@code ArrayListMultimap} with enough capacity to hold the specified + * numbers of keys and values without resizing. + * + *

This method will soon be deprecated in favor of {@code + * MultimapBuilder.hashKeys(expectedKeys).arrayListValues(expectedValuesPerKey).build()}. + * + * @param expectedKeys the expected number of distinct keys + * @param expectedValuesPerKey the expected average number of values per key + * @throws IllegalArgumentException if {@code expectedKeys} or {@code expectedValuesPerKey} is + * negative + */ + public static ArrayListMultimap create( + int expectedKeys, int expectedValuesPerKey) { + return new ArrayListMultimap<>(expectedKeys, expectedValuesPerKey); + } + + /** + * Constructs an {@code ArrayListMultimap} with the same mappings as the specified multimap. + * + *

This method will soon be deprecated in favor of {@code + * MultimapBuilder.hashKeys().arrayListValues().build(multimap)}. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static ArrayListMultimap create( + Multimap multimap) { + return new ArrayListMultimap<>(multimap); + } + + private ArrayListMultimap() { + this(12, DEFAULT_VALUES_PER_KEY); + } + + private ArrayListMultimap(int expectedKeys, int expectedValuesPerKey) { + super(Platform.>newHashMapWithExpectedSize(expectedKeys)); + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + this.expectedValuesPerKey = expectedValuesPerKey; + } + + private ArrayListMultimap(Multimap multimap) { + this( + multimap.keySet().size(), + (multimap instanceof ArrayListMultimap) + ? ((ArrayListMultimap) multimap).expectedValuesPerKey + : DEFAULT_VALUES_PER_KEY); + putAll(multimap); + } + + /** + * Creates a new, empty {@code ArrayList} to hold the collection of values for an arbitrary key. + */ + @Override + List createCollection() { + return new ArrayList(expectedValuesPerKey); + } + + /** + * Reduces the memory used by this {@code ArrayListMultimap}, if feasible. + * + * @deprecated For a {@link ListMultimap} that automatically trims to size, use {@link + * ImmutableListMultimap}. If you need a mutable collection, remove the {@code trimToSize} + * call, or switch to a {@code HashMap>}. + */ + @Deprecated + public void trimToSize() { + for (Collection collection : backingMap().values()) { + ArrayList arrayList = (ArrayList) collection; + arrayList.trimToSize(); + } + } + + /** + * @serialData expectedValuesPerKey, number of distinct keys, and then for each distinct key: the + * key, number of values for that key, and the key's values + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + int distinctKeys = Serialization.readCount(stream); + Map> map = Maps.newHashMap(); + setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } + + @GwtIncompatible // Not needed in emulated source. + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java new file mode 100644 index 0000000000000..40fd3e1079dac --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayListMultimapGwtSerializationDependencies.java @@ -0,0 +1,43 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Map; + +/** + * A dummy superclass to support GWT serialization of the element types of an {@link + * ArrayListMultimap}. The GWT supersource for this class contains a field for each type. + * + *

For details about this hack, see {@code GwtSerializationDependencies}, which takes the same + * approach but with a subclass rather than a superclass. + * + *

TODO(cpovirk): Consider applying this subclass approach to our other types. + */ +@GwtCompatible(emulated = true) +abstract class ArrayListMultimapGwtSerializationDependencies + extends AbstractListMultimap { + ArrayListMultimapGwtSerializationDependencies(Map> map) { + super(map); + } + // TODO(cpovirk): Maybe I should have just one shared superclass for AbstractMultimap itself? +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayTable.java new file mode 100644 index 0000000000000..f839b28accaf7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ArrayTable.java @@ -0,0 +1,794 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Maps.IteratorBasedAbstractMap; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Spliterator; + +import static java.util.Collections.emptyMap; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Fixed-size {@link Table} implementation backed by a two-dimensional array. + * + *

Warning: {@code ArrayTable} is rarely the {@link Table} implementation you want. First, + * it requires that the complete universe of rows and columns be specified at construction time. + * Second, it is always backed by an array large enough to hold a value for every possible + * combination of row and column keys. (This is rarely optimal unless the table is extremely dense.) + * Finally, every possible combination of row and column keys is always considered to have a value + * associated with it: It is not possible to "remove" a value, only to replace it with {@code null}, + * which will still appear when iterating over the table's contents in a foreach loop or a call to a + * null-hostile method like {@link ImmutableTable#copyOf}. For alternatives, please see the wiki. + * + *

The allowed row and column keys must be supplied when the table is created. The table always + * contains a mapping for every row key / column pair. The value corresponding to a given row and + * column is null unless another value is provided. + * + *

The table's size is constant: the product of the number of supplied row keys and the number of + * supplied column keys. The {@code remove} and {@code clear} methods are not supported by the table + * or its views. The {@link #erase} and {@link #eraseAll} methods may be used instead. + * + *

The ordering of the row and column keys provided when the table is constructed determines the + * iteration ordering across rows and columns in the table's views. None of the view iterators + * support {@link Iterator#remove}. If the table is modified after an iterator is created, the + * iterator remains valid. + * + *

This class requires less memory than the {@link HashBasedTable} and {@link TreeBasedTable} + * implementations, except when the table is sparse. + * + *

Null row keys or column keys are not permitted. + * + *

This class provides methods involving the underlying array structure, where the array indices + * correspond to the position of a row or column in the lists of allowed keys and values. See the + * {@link #at}, {@link #set}, {@link #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} + * methods for more details. + * + *

Note that this implementation is not synchronized. If multiple threads access the same cell of + * an {@code ArrayTable} concurrently and one of the threads modifies its value, there is no + * guarantee that the new value will be fully visible to the other threads. To guarantee that + * modifications are visible, synchronize access to the table. Unlike other {@code Table} + * implementations, synchronization is unnecessary between a thread that writes to one cell and a + * thread that reads from another. + * + *

See the Guava User Guide article on {@code Table}. + * + * @author Jared Levy + * @since 10.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class ArrayTable extends AbstractTable implements Serializable { + + /** + * Creates an {@code ArrayTable} filled with {@code null}. + * + * @param rowKeys row keys that may be stored in the generated table + * @param columnKeys column keys that may be stored in the generated table + * @throws NullPointerException if any of the provided keys is null + * @throws IllegalArgumentException if {@code rowKeys} or {@code columnKeys} contains duplicates + * or if exactly one of {@code rowKeys} or {@code columnKeys} is empty. + */ + public static ArrayTable create( + Iterable rowKeys, Iterable columnKeys) { + return new ArrayTable<>(rowKeys, columnKeys); + } + + /* + * TODO(jlevy): Add factory methods taking an Enum class, instead of an + * iterable, to specify the allowed row keys and/or column keys. Note that + * custom serialization logic is needed to support different enum sizes during + * serialization and deserialization. + */ + + /** + * Creates an {@code ArrayTable} with the mappings in the provided table. + * + *

If {@code table} includes a mapping with row key {@code r} and a separate mapping with + * column key {@code c}, the returned table contains a mapping with row key {@code r} and column + * key {@code c}. If that row key / column key pair in not in {@code table}, the pair maps to + * {@code null} in the generated table. + * + *

The returned table allows subsequent {@code put} calls with the row keys in {@code + * table.rowKeySet()} and the column keys in {@code table.columnKeySet()}. Calling {@link #put} + * with other keys leads to an {@code IllegalArgumentException}. + * + *

The ordering of {@code table.rowKeySet()} and {@code table.columnKeySet()} determines the + * row and column iteration ordering of the returned table. + * + * @throws NullPointerException if {@code table} has a null key + */ + public static ArrayTable create(Table table) { + return (table instanceof ArrayTable) + ? new ArrayTable((ArrayTable) table) + : new ArrayTable(table); + } + + private final ImmutableList rowList; + private final ImmutableList columnList; + + // TODO(jlevy): Add getters returning rowKeyToIndex and columnKeyToIndex? + private final ImmutableMap rowKeyToIndex; + private final ImmutableMap columnKeyToIndex; + private final V[][] array; + + private ArrayTable(Iterable rowKeys, Iterable columnKeys) { + this.rowList = ImmutableList.copyOf(rowKeys); + this.columnList = ImmutableList.copyOf(columnKeys); + checkArgument(rowList.isEmpty() == columnList.isEmpty()); + + /* + * TODO(jlevy): Support only one of rowKey / columnKey being empty? If we + * do, when columnKeys is empty but rowKeys isn't, rowKeyList() can contain + * elements but rowKeySet() will be empty and containsRow() won't + * acknowledge them. + */ + rowKeyToIndex = Maps.indexMap(rowList); + columnKeyToIndex = Maps.indexMap(columnList); + + @SuppressWarnings("unchecked") + V[][] tmpArray = (V[][]) new Object[rowList.size()][columnList.size()]; + array = tmpArray; + // Necessary because in GWT the arrays are initialized with "undefined" instead of null. + eraseAll(); + } + + private ArrayTable(Table table) { + this(table.rowKeySet(), table.columnKeySet()); + putAll(table); + } + + private ArrayTable(ArrayTable table) { + rowList = table.rowList; + columnList = table.columnList; + rowKeyToIndex = table.rowKeyToIndex; + columnKeyToIndex = table.columnKeyToIndex; + @SuppressWarnings("unchecked") + V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()]; + array = copy; + for (int i = 0; i < rowList.size(); i++) { + System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); + } + } + + private abstract static class ArrayMap + extends IteratorBasedAbstractMap { + private final ImmutableMap keyIndex; + + private ArrayMap(ImmutableMap keyIndex) { + this.keyIndex = keyIndex; + } + + @Override + public Set keySet() { + return keyIndex.keySet(); + } + + K getKey(int index) { + return keyIndex.keySet().asList().get(index); + } + + abstract String getKeyRole(); + + @ParametricNullness + abstract V getValue(int index); + + @ParametricNullness + abstract V setValue(int index, @ParametricNullness V newValue); + + @Override + public int size() { + return keyIndex.size(); + } + + @Override + public boolean isEmpty() { + return keyIndex.isEmpty(); + } + + Entry getEntry(final int index) { + checkElementIndex(index, size()); + return new AbstractMapEntry() { + @Override + public K getKey() { + return ArrayMap.this.getKey(index); + } + + @Override + @ParametricNullness + public V getValue() { + return ArrayMap.this.getValue(index); + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V value) { + return ArrayMap.this.setValue(index, value); + } + }; + } + + @Override + Iterator> entryIterator() { + return new AbstractIndexedListIterator>(size()) { + @Override + protected Entry get(final int index) { + return getEntry(index); + } + }; + } + + @Override + Spliterator> entrySpliterator() { + return CollectSpliterators.indexed(size(), Spliterator.ORDERED, this::getEntry); + } + + // TODO(lowasser): consider an optimized values() implementation + + @Override + public boolean containsKey(Object key) { + return keyIndex.containsKey(key); + } + + @Override + public V get(Object key) { + Integer index = keyIndex.get(key); + if (index == null) { + return null; + } else { + return getValue(index); + } + } + + @Override + public V put(K key, @ParametricNullness V value) { + Integer index = keyIndex.get(key); + if (index == null) { + throw new IllegalArgumentException( + getKeyRole() + " " + key + " not in " + keyIndex.keySet()); + } + return setValue(index, value); + } + + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns, as an immutable list, the row keys provided when the table was constructed, including + * those that are mapped to null values only. + */ + public ImmutableList rowKeyList() { + return rowList; + } + + /** + * Returns, as an immutable list, the column keys provided when the table was constructed, + * including those that are mapped to null values only. + */ + public ImmutableList columnKeyList() { + return columnList; + } + + /** + * Returns the value corresponding to the specified row and column indices. The same value is + * returned by {@code get(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex))}, but this + * method runs more quickly. + * + * @param rowIndex position of the row key in {@link #rowKeyList()} + * @param columnIndex position of the row key in {@link #columnKeyList()} + * @return the value with the specified row and column + * @throws IndexOutOfBoundsException if either index is negative, {@code rowIndex} is greater than + * or equal to the number of allowed row keys, or {@code columnIndex} is greater than or equal + * to the number of allowed column keys + */ + public V at(int rowIndex, int columnIndex) { + // In GWT array access never throws IndexOutOfBoundsException. + checkElementIndex(rowIndex, rowList.size()); + checkElementIndex(columnIndex, columnList.size()); + return array[rowIndex][columnIndex]; + } + + /** + * Associates {@code value} with the specified row and column indices. The logic {@code + * put(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex), value)} has the same + * behavior, but this method runs more quickly. + * + * @param rowIndex position of the row key in {@link #rowKeyList()} + * @param columnIndex position of the row key in {@link #columnKeyList()} + * @param value value to store in the table + * @return the previous value with the specified row and column + * @throws IndexOutOfBoundsException if either index is negative, {@code rowIndex} is greater than + * or equal to the number of allowed row keys, or {@code columnIndex} is greater than or equal + * to the number of allowed column keys + */ + public V set(int rowIndex, int columnIndex, V value) { + // In GWT array access never throws IndexOutOfBoundsException. + checkElementIndex(rowIndex, rowList.size()); + checkElementIndex(columnIndex, columnList.size()); + V oldValue = array[rowIndex][columnIndex]; + array[rowIndex][columnIndex] = value; + return oldValue; + } + + /** + * Returns a two-dimensional array with the table contents. The row and column indices correspond + * to the positions of the row and column in the iterables provided during table construction. If + * the table lacks a mapping for a given row and column, the corresponding array element is null. + * + *

Subsequent table changes will not modify the array, and vice versa. + * + * @param valueClass class of values stored in the returned array + */ + @GwtIncompatible // reflection + public V[][] toArray(Class valueClass) { + @SuppressWarnings("unchecked") // TODO: safe? + V[][] copy = (V[][]) Array.newInstance(valueClass, rowList.size(), columnList.size()); + for (int i = 0; i < rowList.size(); i++) { + System.arraycopy(array[i], 0, copy[i], 0, array[i].length); + } + return copy; + } + + /** + * Not supported. Use {@link #eraseAll} instead. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link #eraseAll} + */ + @Override + @Deprecated + public void clear() { + throw new UnsupportedOperationException(); + } + + /** Associates the value {@code null} with every pair of allowed row and column keys. */ + public void eraseAll() { + for (V[] row : array) { + Arrays.fill(row, null); + } + } + + /** + * Returns {@code true} if the provided keys are among the keys provided when the table was + * constructed. + */ + @Override + public boolean contains(Object rowKey, Object columnKey) { + return containsRow(rowKey) && containsColumn(columnKey); + } + + /** + * Returns {@code true} if the provided column key is among the column keys provided when the + * table was constructed. + */ + @Override + public boolean containsColumn(Object columnKey) { + return columnKeyToIndex.containsKey(columnKey); + } + + /** + * Returns {@code true} if the provided row key is among the row keys provided when the table was + * constructed. + */ + @Override + public boolean containsRow(Object rowKey) { + return rowKeyToIndex.containsKey(rowKey); + } + + @Override + public boolean containsValue(Object value) { + for (V[] row : array) { + for (V element : row) { + if (Objects.equal(value, element)) { + return true; + } + } + } + return false; + } + + @Override + public V get(Object rowKey, Object columnKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + return (rowIndex == null || columnIndex == null) ? null : at(rowIndex, columnIndex); + } + + /** + * Returns {@code true} if {@code rowKeyList().size == 0} or {@code columnKeyList().size() == 0}. + */ + @Override + public boolean isEmpty() { + return rowList.isEmpty() || columnList.isEmpty(); + } + + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if {@code rowKey} is not in {@link #rowKeySet()} or {@code + * columnKey} is not in {@link #columnKeySet()}. + */ + @Override + public V put(R rowKey, C columnKey, V value) { + checkNotNull(rowKey); + checkNotNull(columnKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList); + Integer columnIndex = columnKeyToIndex.get(columnKey); + checkArgument(columnIndex != null, "Column %s not in %s", columnKey, columnList); + return set(rowIndex, columnIndex, value); + } + + /* + * TODO(jlevy): Consider creating a merge() method, similar to putAll() but + * copying non-null values only. + */ + + /** + * {@inheritDoc} + * + *

If {@code table} is an {@code ArrayTable}, its null values will be stored in this table, + * possibly replacing values that were previously non-null. + * + * @throws NullPointerException if {@code table} has a null key + * @throws IllegalArgumentException if any of the provided table's row keys or column keys is not + * in {@link #rowKeySet()} or {@link #columnKeySet()} + */ + @Override + public void putAll(Table table) { + super.putAll(table); + } + + /** + * Not supported. Use {@link #erase} instead. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link #erase} + */ + @Override + @Deprecated + public V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } + + /** + * Associates the value {@code null} with the specified keys, assuming both keys are valid. If + * either key is null or isn't among the keys provided during construction, this method has no + * effect. + * + *

This method is equivalent to {@code put(rowKey, columnKey, null)} when both provided keys + * are valid. + * + * @param rowKey row key of mapping to be erased + * @param columnKey column key of mapping to be erased + * @return the value previously associated with the keys, or {@code null} if no mapping existed + * for the keys + */ + public V erase(Object rowKey, Object columnKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + if (rowIndex == null || columnIndex == null) { + return null; + } + return set(rowIndex, columnIndex, null); + } + + // TODO(jlevy): Add eraseRow and eraseColumn methods? + + @Override + public int size() { + return rowList.size() * columnList.size(); + } + + /** + * Returns an unmodifiable set of all row key / column key / value triplets. Changes to the table + * will update the returned set. + * + *

The returned set's iterator traverses the mappings with the first row key, the mappings with + * the second row key, and so on. + * + *

The value in the returned cells may change if the table subsequently changes. + * + * @return set of table cells consisting of row key / column key / value triplets + */ + @Override + public Set> cellSet() { + return super.cellSet(); + } + + @Override + Iterator> cellIterator() { + return new AbstractIndexedListIterator>(size()) { + @Override + protected Cell get(final int index) { + return getCell(index); + } + }; + } + + @Override + Spliterator> cellSpliterator() { + return CollectSpliterators.>indexed( + size(), Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.DISTINCT, this::getCell); + } + + private Cell getCell(final int index) { + return new Tables.AbstractCell() { + final int rowIndex = index / columnList.size(); + final int columnIndex = index % columnList.size(); + + @Override + public R getRowKey() { + return rowList.get(rowIndex); + } + + @Override + public C getColumnKey() { + return columnList.get(columnIndex); + } + + @Override + public V getValue() { + return at(rowIndex, columnIndex); + } + }; + } + + private V getValue(int index) { + int rowIndex = index / columnList.size(); + int columnIndex = index % columnList.size(); + return at(rowIndex, columnIndex); + } + + /** + * Returns a view of all mappings that have the given column key. If the column key isn't in + * {@link #columnKeySet()}, an empty immutable map is returned. + * + *

Otherwise, for each row key in {@link #rowKeySet()}, the returned map associates the row key + * with the corresponding value in the table. Changes to the returned map will update the + * underlying table, and vice versa. + * + * @param columnKey key of column to search for in the table + * @return the corresponding map from row keys to values + */ + @Override + public Map column(C columnKey) { + checkNotNull(columnKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + if (columnIndex == null) { + return emptyMap(); + } else { + return new Column(columnIndex); + } + } + + private class Column extends ArrayMap { + final int columnIndex; + + Column(int columnIndex) { + super(rowKeyToIndex); + this.columnIndex = columnIndex; + } + + @Override + String getKeyRole() { + return "Row"; + } + + @Override + V getValue(int index) { + return at(index, columnIndex); + } + + @Override + V setValue(int index, V newValue) { + return set(index, columnIndex, newValue); + } + } + + /** + * Returns an immutable set of the valid column keys, including those that are associated with + * null values only. + * + * @return immutable set of column keys + */ + @Override + public ImmutableSet columnKeySet() { + return columnKeyToIndex.keySet(); + } + + private transient ColumnMap columnMap; + + @Override + public Map> columnMap() { + ColumnMap map = columnMap; + return (map == null) ? columnMap = new ColumnMap() : map; + } + + private class ColumnMap extends ArrayMap> { + private ColumnMap() { + super(columnKeyToIndex); + } + + @Override + String getKeyRole() { + return "Column"; + } + + @Override + Map getValue(int index) { + return new Column(index); + } + + @Override + Map setValue(int index, Map newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public Map put(C key, Map value) { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns a view of all mappings that have the given row key. If the row key isn't in {@link + * #rowKeySet()}, an empty immutable map is returned. + * + *

Otherwise, for each column key in {@link #columnKeySet()}, the returned map associates the + * column key with the corresponding value in the table. Changes to the returned map will update + * the underlying table, and vice versa. + * + * @param rowKey key of row to search for in the table + * @return the corresponding map from column keys to values + */ + @Override + public Map row(R rowKey) { + checkNotNull(rowKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + if (rowIndex == null) { + return emptyMap(); + } else { + return new Row(rowIndex); + } + } + + private class Row extends ArrayMap { + final int rowIndex; + + Row(int rowIndex) { + super(columnKeyToIndex); + this.rowIndex = rowIndex; + } + + @Override + String getKeyRole() { + return "Column"; + } + + @Override + V getValue(int index) { + return at(rowIndex, index); + } + + @Override + V setValue(int index, V newValue) { + return set(rowIndex, index, newValue); + } + } + + /** + * Returns an immutable set of the valid row keys, including those that are associated with null + * values only. + * + * @return immutable set of row keys + */ + @Override + public ImmutableSet rowKeySet() { + return rowKeyToIndex.keySet(); + } + + private transient RowMap rowMap; + + @Override + public Map> rowMap() { + RowMap map = rowMap; + return (map == null) ? rowMap = new RowMap() : map; + } + + private class RowMap extends ArrayMap> { + private RowMap() { + super(rowKeyToIndex); + } + + @Override + String getKeyRole() { + return "Row"; + } + + @Override + Map getValue(int index) { + return new Row(index); + } + + @Override + Map setValue(int index, Map newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public Map put(R key, Map value) { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns an unmodifiable collection of all values, which may contain duplicates. Changes to the + * table will update the returned collection. + * + *

The returned collection's iterator traverses the values of the first row key, the values of + * the second row key, and so on. + * + * @return collection of values + */ + @Override + public Collection values() { + return super.values(); + } + + @Override + Iterator valuesIterator() { + return new AbstractIndexedListIterator(size()) { + @Override + protected V get(int index) { + return getValue(index); + } + }; + } + + @Override + Spliterator valuesSpliterator() { + return CollectSpliterators.indexed(size(), Spliterator.ORDERED, this::getValue); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BaseImmutableMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BaseImmutableMultimap.java new file mode 100644 index 0000000000000..8b832226349c9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BaseImmutableMultimap.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * A dummy superclass of {@link ImmutableMultimap} that can be instanceof'd without ProGuard + * retaining additional implementation details of {@link ImmutableMultimap}. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class BaseImmutableMultimap extends AbstractMultimap {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BiMap.java new file mode 100644 index 0000000000000..cacdbf8206edb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BiMap.java @@ -0,0 +1,118 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; +import java.util.Set; + +/** + * A bimap (or "bidirectional map") is a map that preserves the uniqueness of its values as well as + * that of its keys. This constraint enables bimaps to support an "inverse view", which is another + * bimap containing the same entries as this bimap but with reversed keys and values. + * + *

Implementations

+ * + *
    + *
  • {@link ImmutableBiMap} + *
  • {@link HashBiMap} + *
  • {@link EnumBiMap} + *
  • {@link EnumHashBiMap} + *
+ * + *

See the Guava User Guide article on {@code BiMap}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface BiMap extends Map { + // Modification Operations + + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if the given value is already bound to a different key in this + * bimap. The bimap will remain unmodified in this event. To avoid this exception, call {@link + * #forcePut} instead. + */ + @Override + V put(@ParametricNullness K key, @ParametricNullness V value); + + /** + * An alternate form of {@code put} that silently removes any existing entry with the value {@code + * value} before proceeding with the {@link #put} operation. If the bimap previously contained the + * provided key-value mapping, this method has no effect. + * + *

Note that a successful call to this method could cause the size of the bimap to increase by + * one, stay the same, or even decrease by one. + * + *

Warning: If an existing entry with this value is removed, the key for that entry is + * discarded and not returned. + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key + * @return the value that was previously associated with the key, or {@code null} if there was no + * previous entry. (If the bimap contains null values, then {@code forcePut}, like {@code + * put}, returns {@code null} both if the key is absent and if it is present with a null + * value.) + */ + V forcePut(@ParametricNullness K key, @ParametricNullness V value); + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

Warning: the results of calling this method may vary depending on the iteration order + * of {@code map}. + * + * @throws IllegalArgumentException if an attempt to {@code put} any entry fails. Note that some + * map entries may have been added to the bimap before the exception was thrown. + */ + @Override + void putAll(Map map); + + // Views + + /** + * {@inheritDoc} + * + *

Because a bimap has unique values, this method returns a {@link Set}, instead of the {@link + * java.util.Collection} specified in the {@link Map} interface. + */ + @Override + Set values(); + + /** + * Returns the inverse view of this bimap, which maps each of this bimap's values to its + * associated key. The two bimaps are backed by the same data; any changes to one will appear in + * the other. + * + *

Note:There is no guaranteed correspondence between the iteration order of a bimap and + * that of its inverse. + * + * @return the inverse view of this bimap + */ + BiMap inverse(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BoundType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BoundType.java new file mode 100644 index 0000000000000..1d2ad54be16bc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/BoundType.java @@ -0,0 +1,48 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Indicates whether an endpoint of some range is contained in the range itself ("closed") or not + * ("open"). If a range is unbounded on a side, it is neither open nor closed on that side; the + * bound simply does not exist. + * + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public enum BoundType { + /** The endpoint value is not considered part of the set ("exclusive"). */ + OPEN(false), + CLOSED(true); + + final boolean inclusive; + + BoundType(boolean inclusive) { + this.inclusive = inclusive; + } + + /** Returns the bound type corresponding to a boolean value for inclusivity. */ + static BoundType forBoolean(boolean inclusive) { + return inclusive ? CLOSED : OPEN; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ByFunctionOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ByFunctionOrdering.java new file mode 100644 index 0000000000000..6dec9c2ef1576 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ByFunctionOrdering.java @@ -0,0 +1,74 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Objects; + +import java.io.Serializable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An ordering that orders elements by applying an order to the result of a function on those + * elements. + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class ByFunctionOrdering extends Ordering + implements Serializable { + final Function function; + final Ordering ordering; + + ByFunctionOrdering(Function function, Ordering ordering) { + this.function = checkNotNull(function); + this.ordering = checkNotNull(ordering); + } + + @Override + public int compare(@ParametricNullness F left, @ParametricNullness F right) { + return ordering.compare(function.apply(left), function.apply(right)); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ByFunctionOrdering) { + ByFunctionOrdering that = (ByFunctionOrdering) object; + return this.function.equals(that.function) && this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(function, ordering); + } + + @Override + public String toString() { + return ordering + ".onResultOf(" + function + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CartesianList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CartesianList.java new file mode 100644 index 0000000000000..0169fede59049 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CartesianList.java @@ -0,0 +1,166 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.math.IntMath; + +import java.util.AbstractList; +import java.util.List; +import java.util.ListIterator; +import java.util.RandomAccess; + +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; + +/** + * Implementation of {@link Lists#cartesianProduct(List)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class CartesianList extends AbstractList> implements RandomAccess { + + private final transient ImmutableList> axes; + private final transient int[] axesSizeProduct; + + static List> create(List> lists) { + ImmutableList.Builder> axesBuilder = new ImmutableList.Builder<>(lists.size()); + for (List list : lists) { + List copy = ImmutableList.copyOf(list); + if (copy.isEmpty()) { + return ImmutableList.of(); + } + axesBuilder.add(copy); + } + return new CartesianList<>(axesBuilder.build()); + } + + CartesianList(ImmutableList> axes) { + this.axes = axes; + int[] axesSizeProduct = new int[axes.size() + 1]; + axesSizeProduct[axes.size()] = 1; + try { + for (int i = axes.size() - 1; i >= 0; i--) { + axesSizeProduct[i] = IntMath.checkedMultiply(axesSizeProduct[i + 1], axes.get(i).size()); + } + } catch (ArithmeticException e) { + throw new IllegalArgumentException( + "Cartesian product too large; must have size at most Integer.MAX_VALUE"); + } + this.axesSizeProduct = axesSizeProduct; + } + + private int getAxisIndexForProductIndex(int index, int axis) { + return (index / axesSizeProduct[axis + 1]) % axes.get(axis).size(); + } + + @Override + public int indexOf(Object o) { + if (!(o instanceof List)) { + return -1; + } + List list = (List) o; + if (list.size() != axes.size()) { + return -1; + } + ListIterator itr = list.listIterator(); + int computedIndex = 0; + while (itr.hasNext()) { + int axisIndex = itr.nextIndex(); + int elemIndex = axes.get(axisIndex).indexOf(itr.next()); + if (elemIndex == -1) { + return -1; + } + computedIndex += elemIndex * axesSizeProduct[axisIndex + 1]; + } + return computedIndex; + } + + @Override + public int lastIndexOf(Object o) { + if (!(o instanceof List)) { + return -1; + } + List list = (List) o; + if (list.size() != axes.size()) { + return -1; + } + ListIterator itr = list.listIterator(); + int computedIndex = 0; + while (itr.hasNext()) { + int axisIndex = itr.nextIndex(); + int elemIndex = axes.get(axisIndex).lastIndexOf(itr.next()); + if (elemIndex == -1) { + return -1; + } + computedIndex += elemIndex * axesSizeProduct[axisIndex + 1]; + } + return computedIndex; + } + + @Override + public ImmutableList get(int index) { + checkElementIndex(index, size()); + return new ImmutableList() { + + @Override + public int size() { + return axes.size(); + } + + @Override + public E get(int axis) { + checkElementIndex(axis, size()); + int axisIndex = getAxisIndexForProductIndex(index, axis); + return axes.get(axis).get(axisIndex); + } + + @Override + boolean isPartialView() { + return true; + } + }; + } + + @Override + public int size() { + return axesSizeProduct[0]; + } + + @Override + public boolean contains(Object object) { + if (!(object instanceof List)) { + return false; + } + List list = (List) object; + if (list.size() != axes.size()) { + return false; + } + int i = 0; + for (Object o : list) { + if (!axes.get(i).contains(o)) { + return false; + } + i++; + } + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ClassToInstanceMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ClassToInstanceMap.java new file mode 100644 index 0000000000000..91c941550bc7a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ClassToInstanceMap.java @@ -0,0 +1,70 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; + +/** + * A map, each entry of which maps a Java raw type to an + * instance of that type. In addition to implementing {@code Map}, the additional type-safe + * operations {@link #putInstance} and {@link #getInstance} are available. + * + *

Like any other {@code Map}, this map may contain entries for primitive types, + * and a primitive type and its corresponding wrapper type may map to different values. + * + *

Implementations

+ * + *
    + *
  • {@link ImmutableClassToInstanceMap} + *
  • {@link MutableClassToInstanceMap} + *
+ * + *

To map a generic type to an instance of that type, use {@link + * org.apache.iotdb.google.common.reflect.TypeToInstanceMap} instead. + * + *

See the Guava User Guide article on {@code + * ClassToInstanceMap}. + * + * @param the common supertype that all values will share. When in doubt, just use {@link + * Object}, or use {@code @Nullable Object} to allow null values. + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface ClassToInstanceMap extends Map, B> { + /** + * Returns the value the specified class is mapped to, or {@code null} if no entry for this class + * is present. This will only return a value that was bound to this specific class, not a value + * that may have been bound to a subtype. + */ + T getInstance(Class type); + + /** + * Maps the specified class to the specified value. Does not associate this value with any + * of the class's supertypes. + * + * @return the value previously associated with this class (possibly {@code null}), or {@code + * null} if there was no previous entry. + */ + T putInstance(Class type, @ParametricNullness T value); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectCollectors.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectCollectors.java new file mode 100644 index 0000000000000..4618e1e3580b8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectCollectors.java @@ -0,0 +1,457 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.TreeMap; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.collectingAndThen; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** Collectors utilities for {@code common.collect} internals. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class CollectCollectors { + + private static final Collector> TO_IMMUTABLE_LIST = + Collector.of( + ImmutableList::builder, + ImmutableList.Builder::add, + ImmutableList.Builder::combine, + ImmutableList.Builder::build); + + private static final Collector> TO_IMMUTABLE_SET = + Collector.of( + ImmutableSet::builder, + ImmutableSet.Builder::add, + ImmutableSet.Builder::combine, + ImmutableSet.Builder::build); + + @GwtIncompatible + private static final Collector>, ?, ImmutableRangeSet>> + TO_IMMUTABLE_RANGE_SET = + Collector.of( + ImmutableRangeSet::builder, + ImmutableRangeSet.Builder::add, + ImmutableRangeSet.Builder::combine, + ImmutableRangeSet.Builder::build); + + // Lists + + @SuppressWarnings({"rawtypes", "unchecked"}) + static Collector> toImmutableList() { + return (Collector) TO_IMMUTABLE_LIST; + } + + // Sets + + @SuppressWarnings({"rawtypes", "unchecked"}) + static Collector> toImmutableSet() { + return (Collector) TO_IMMUTABLE_SET; + } + + static Collector> toImmutableSortedSet( + Comparator comparator) { + checkNotNull(comparator); + return Collector.of( + () -> new ImmutableSortedSet.Builder(comparator), + ImmutableSortedSet.Builder::add, + ImmutableSortedSet.Builder::combine, + ImmutableSortedSet.Builder::build); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + static > Collector> toImmutableEnumSet() { + return (Collector) EnumSetAccumulator.TO_IMMUTABLE_ENUM_SET; + } + + private static > + Collector, ImmutableSet> toImmutableEnumSetGeneric() { + return Collector.of( + EnumSetAccumulator::new, + EnumSetAccumulator::add, + EnumSetAccumulator::combine, + EnumSetAccumulator::toImmutableSet, + Collector.Characteristics.UNORDERED); + } + + private static final class EnumSetAccumulator> { + @SuppressWarnings({"rawtypes", "unchecked"}) + static final Collector, ?, ImmutableSet>> TO_IMMUTABLE_ENUM_SET = + (Collector) toImmutableEnumSetGeneric(); + + private EnumSet set; + + void add(E e) { + if (set == null) { + set = EnumSet.of(e); + } else { + set.add(e); + } + } + + EnumSetAccumulator combine(EnumSetAccumulator other) { + if (this.set == null) { + return other; + } else if (other.set == null) { + return this; + } else { + this.set.addAll(other.set); + return this; + } + } + + ImmutableSet toImmutableSet() { + if (set == null) { + return ImmutableSet.of(); + } + ImmutableSet ret = ImmutableEnumSet.asImmutable(set); + set = null; // subsequent manual manipulation of the accumulator mustn't affect ret + return ret; + } + } + + @GwtIncompatible + @SuppressWarnings({"rawtypes", "unchecked"}) + static > + Collector, ?, ImmutableRangeSet> toImmutableRangeSet() { + return (Collector) TO_IMMUTABLE_RANGE_SET; + } + + // Multisets + + static Collector> toImmutableMultiset( + Function elementFunction, ToIntFunction countFunction) { + checkNotNull(elementFunction); + checkNotNull(countFunction); + return Collector.of( + LinkedHashMultiset::create, + (multiset, t) -> + multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)), + (multiset1, multiset2) -> { + multiset1.addAll(multiset2); + return multiset1; + }, + (Multiset multiset) -> ImmutableMultiset.copyFromEntries(multiset.entrySet())); + } + + static > Collector toMultiset( + Function elementFunction, + ToIntFunction countFunction, + Supplier multisetSupplier) { + checkNotNull(elementFunction); + checkNotNull(countFunction); + checkNotNull(multisetSupplier); + return Collector.of( + multisetSupplier, + (ms, t) -> ms.add(elementFunction.apply(t), countFunction.applyAsInt(t)), + (ms1, ms2) -> { + ms1.addAll(ms2); + return ms1; + }); + } + + // Maps + + static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableMap.Builder::new, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableMap.Builder::combine, + ImmutableMap.Builder::build); + } + + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + return collectingAndThen( + Collectors.toMap(keyFunction, valueFunction, mergeFunction, LinkedHashMap::new), + ImmutableMap::copyOf); + } + + static Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction) { + checkNotNull(comparator); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + /* + * We will always fail if there are duplicate keys, and the keys are always sorted by + * the Comparator, so the entries can come in an arbitrary order -- so we report UNORDERED. + */ + return Collector.of( + () -> new ImmutableSortedMap.Builder(comparator), + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableSortedMap.Builder::combine, + ImmutableSortedMap.Builder::build, + Collector.Characteristics.UNORDERED); + } + + static Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + checkNotNull(comparator); + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + return collectingAndThen( + Collectors.toMap( + keyFunction, valueFunction, mergeFunction, () -> new TreeMap(comparator)), + ImmutableSortedMap::copyOfSorted); + } + + static Collector> toImmutableBiMap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableBiMap.Builder::new, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableBiMap.Builder::combine, + ImmutableBiMap.Builder::build, + new Collector.Characteristics[0]); + } + + @J2ktIncompatible + static , V> + Collector> toImmutableEnumMap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + () -> + new EnumMapAccumulator( + (v1, v2) -> { + throw new IllegalArgumentException("Multiple values for key: " + v1 + ", " + v2); + }), + (accum, t) -> { + /* + * We assign these to variables before calling checkNotNull to work around a bug in our + * nullness checker. + */ + K key = keyFunction.apply(t); + V newValue = valueFunction.apply(t); + accum.put( + checkNotNull(key, "Null key for input %s", t), + checkNotNull(newValue, "Null value for input %s", t)); + }, + EnumMapAccumulator::combine, + EnumMapAccumulator::toImmutableMap, + Collector.Characteristics.UNORDERED); + } + + @J2ktIncompatible + static , V> + Collector> toImmutableEnumMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + // not UNORDERED because we don't know if mergeFunction is commutative + return Collector.of( + () -> new EnumMapAccumulator(mergeFunction), + (accum, t) -> { + /* + * We assign these to variables before calling checkNotNull to work around a bug in our + * nullness checker. + */ + K key = keyFunction.apply(t); + V newValue = valueFunction.apply(t); + accum.put( + checkNotNull(key, "Null key for input %s", t), + checkNotNull(newValue, "Null value for input %s", t)); + }, + EnumMapAccumulator::combine, + EnumMapAccumulator::toImmutableMap); + } + + @J2ktIncompatible + private static class EnumMapAccumulator, V> { + private final BinaryOperator mergeFunction; + private EnumMap map = null; + + EnumMapAccumulator(BinaryOperator mergeFunction) { + this.mergeFunction = mergeFunction; + } + + void put(K key, V value) { + if (map == null) { + map = new EnumMap<>(singletonMap(key, value)); + } else { + map.merge(key, value, mergeFunction); + } + } + + EnumMapAccumulator combine(EnumMapAccumulator other) { + if (this.map == null) { + return other; + } else if (other.map == null) { + return this; + } else { + other.map.forEach(this::put); + return this; + } + } + + ImmutableMap toImmutableMap() { + return (map == null) ? ImmutableMap.of() : ImmutableEnumMap.asImmutable(map); + } + } + + @GwtIncompatible + static , V> + Collector> toImmutableRangeMap( + Function> keyFunction, + Function valueFunction) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + return Collector.of( + ImmutableRangeMap::builder, + (builder, input) -> builder.put(keyFunction.apply(input), valueFunction.apply(input)), + ImmutableRangeMap.Builder::combine, + ImmutableRangeMap.Builder::build); + } + + // Multimaps + + static + Collector> toImmutableListMultimap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction, "keyFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + ImmutableListMultimap::builder, + (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), + ImmutableListMultimap.Builder::combine, + ImmutableListMultimap.Builder::build); + } + + static + Collector> flatteningToImmutableListMultimap( + Function keyFunction, + Function> valuesFunction) { + checkNotNull(keyFunction); + checkNotNull(valuesFunction); + return collectingAndThen( + flatteningToMultimap( + input -> checkNotNull(keyFunction.apply(input)), + input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull), + MultimapBuilder.linkedHashKeys().arrayListValues()::build), + ImmutableListMultimap::copyOf); + } + + static + Collector> toImmutableSetMultimap( + Function keyFunction, + Function valueFunction) { + checkNotNull(keyFunction, "keyFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + ImmutableSetMultimap::builder, + (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), + ImmutableSetMultimap.Builder::combine, + ImmutableSetMultimap.Builder::build); + } + + static + Collector> flatteningToImmutableSetMultimap( + Function keyFunction, + Function> valuesFunction) { + checkNotNull(keyFunction); + checkNotNull(valuesFunction); + return collectingAndThen( + flatteningToMultimap( + input -> checkNotNull(keyFunction.apply(input)), + input -> valuesFunction.apply(input).peek(Preconditions::checkNotNull), + MultimapBuilder.linkedHashKeys().linkedHashSetValues()::build), + ImmutableSetMultimap::copyOf); + } + + static > + Collector toMultimap( + Function keyFunction, + Function valueFunction, + Supplier multimapSupplier) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(multimapSupplier); + return Collector.of( + multimapSupplier, + (multimap, input) -> multimap.put(keyFunction.apply(input), valueFunction.apply(input)), + (multimap1, multimap2) -> { + multimap1.putAll(multimap2); + return multimap1; + }); + } + + static > + Collector flatteningToMultimap( + Function keyFunction, + Function> valueFunction, + Supplier multimapSupplier) { + checkNotNull(keyFunction); + checkNotNull(valueFunction); + checkNotNull(multimapSupplier); + return Collector.of( + multimapSupplier, + (multimap, input) -> { + K key = keyFunction.apply(input); + Collection valuesForKey = multimap.get(key); + valueFunction.apply(input).forEachOrdered(valuesForKey::add); + }, + (multimap1, multimap2) -> { + multimap1.putAll(multimap2); + return multimap1; + }); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectPreconditions.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectPreconditions.java new file mode 100644 index 0000000000000..78782889464c4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectPreconditions.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** Precondition checks useful in collection implementations. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class CollectPreconditions { + + static void checkEntryNotNull(Object key, Object value) { + if (key == null) { + throw new NullPointerException("null key in entry: null=" + value); + } else if (value == null) { + throw new NullPointerException("null value in entry: " + key + "=null"); + } + } + + static int checkNonnegative(int value, String name) { + if (value < 0) { + throw new IllegalArgumentException(name + " cannot be negative but was: " + value); + } + return value; + } + + static long checkNonnegative(long value, String name) { + if (value < 0) { + throw new IllegalArgumentException(name + " cannot be negative but was: " + value); + } + return value; + } + + static void checkPositive(int value, String name) { + if (value <= 0) { + throw new IllegalArgumentException(name + " must be positive but was: " + value); + } + } + + /** + * Precondition tester for {@code Iterator.remove()} that throws an exception with a consistent + * error message. + */ + static void checkRemove(boolean canRemove) { + checkState(canRemove, "no calls to next() since the last call to remove()"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectSpliterators.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectSpliterators.java new file mode 100644 index 0000000000000..3d91f6a6b6d77 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CollectSpliterators.java @@ -0,0 +1,546 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.Function; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.LongConsumer; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static java.lang.Math.max; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** Spliterator utilities for {@code common.collect} internals. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class CollectSpliterators { + private CollectSpliterators() {} + + static Spliterator indexed( + int size, int extraCharacteristics, IntFunction function) { + return indexed(size, extraCharacteristics, function, null); + } + + static Spliterator indexed( + int size, + int extraCharacteristics, + IntFunction function, + Comparator comparator) { + if (comparator != null) { + checkArgument((extraCharacteristics & Spliterator.SORTED) != 0); + } + class WithCharacteristics implements Spliterator { + private final Spliterator.OfInt delegate; + + WithCharacteristics(Spliterator.OfInt delegate) { + this.delegate = delegate; + } + + @Override + public boolean tryAdvance(Consumer action) { + return delegate.tryAdvance((IntConsumer) i -> action.accept(function.apply(i))); + } + + @Override + public void forEachRemaining(Consumer action) { + delegate.forEachRemaining((IntConsumer) i -> action.accept(function.apply(i))); + } + + @Override + public Spliterator trySplit() { + Spliterator.OfInt split = delegate.trySplit(); + return (split == null) ? null : new WithCharacteristics(split); + } + + @Override + public long estimateSize() { + return delegate.estimateSize(); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED + | Spliterator.SIZED + | Spliterator.SUBSIZED + | extraCharacteristics; + } + + @Override + public Comparator getComparator() { + if (hasCharacteristics(Spliterator.SORTED)) { + return comparator; + } else { + throw new IllegalStateException(); + } + } + } + return new WithCharacteristics(IntStream.range(0, size).spliterator()); + } + + /** + * Returns a {@code Spliterator} over the elements of {@code fromSpliterator} mapped by {@code + * function}. + */ + static Spliterator map( + Spliterator fromSpliterator, + Function function) { + checkNotNull(fromSpliterator); + checkNotNull(function); + return new Spliterator() { + + @Override + public boolean tryAdvance(Consumer action) { + return fromSpliterator.tryAdvance( + fromElement -> action.accept(function.apply(fromElement))); + } + + @Override + public void forEachRemaining(Consumer action) { + fromSpliterator.forEachRemaining(fromElement -> action.accept(function.apply(fromElement))); + } + + @Override + public Spliterator trySplit() { + Spliterator fromSplit = fromSpliterator.trySplit(); + return (fromSplit != null) ? map(fromSplit, function) : null; + } + + @Override + public long estimateSize() { + return fromSpliterator.estimateSize(); + } + + @Override + public int characteristics() { + return fromSpliterator.characteristics() + & ~(Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SORTED); + } + }; + } + + /** Returns a {@code Spliterator} filtered by the specified predicate. */ + static Spliterator filter( + Spliterator fromSpliterator, Predicate predicate) { + checkNotNull(fromSpliterator); + checkNotNull(predicate); + class Splitr implements Spliterator, Consumer { + T holder = null; + + @Override + public void accept(@ParametricNullness T t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + while (fromSpliterator.tryAdvance(this)) { + try { + // The cast is safe because tryAdvance puts a T into `holder`. + T next = uncheckedCastNullableTToT(holder); + if (predicate.test(next)) { + action.accept(next); + return true; + } + } finally { + holder = null; + } + } + return false; + } + + @Override + public Spliterator trySplit() { + Spliterator fromSplit = fromSpliterator.trySplit(); + return (fromSplit == null) ? null : filter(fromSplit, predicate); + } + + @Override + public long estimateSize() { + return fromSpliterator.estimateSize() / 2; + } + + @Override + public Comparator getComparator() { + return fromSpliterator.getComparator(); + } + + @Override + public int characteristics() { + return fromSpliterator.characteristics() + & (Spliterator.DISTINCT + | Spliterator.NONNULL + | Spliterator.ORDERED + | Spliterator.SORTED); + } + } + return new Splitr(); + } + + /** + * Returns a {@code Spliterator} that iterates over the elements of the spliterators generated by + * applying {@code function} to the elements of {@code fromSpliterator}. + */ + static Spliterator flatMap( + Spliterator fromSpliterator, + Function> function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfObject<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Returns a {@code Spliterator.OfInt} that iterates over the elements of the spliterators + * generated by applying {@code function} to the elements of {@code fromSpliterator}. (If {@code + * function} returns {@code null} for an input, it is replaced with an empty stream.) + */ + static Spliterator.OfInt flatMapToInt( + Spliterator fromSpliterator, + Function function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfInt<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Returns a {@code Spliterator.OfLong} that iterates over the elements of the spliterators + * generated by applying {@code function} to the elements of {@code fromSpliterator}. (If {@code + * function} returns {@code null} for an input, it is replaced with an empty stream.) + */ + static Spliterator.OfLong flatMapToLong( + Spliterator fromSpliterator, + Function function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfLong<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Returns a {@code Spliterator.OfDouble} that iterates over the elements of the spliterators + * generated by applying {@code function} to the elements of {@code fromSpliterator}. (If {@code + * function} returns {@code null} for an input, it is replaced with an empty stream.) + */ + static Spliterator.OfDouble flatMapToDouble( + Spliterator fromSpliterator, + Function function, + int topCharacteristics, + long topSize) { + checkArgument( + (topCharacteristics & Spliterator.SUBSIZED) == 0, + "flatMap does not support SUBSIZED characteristic"); + checkArgument( + (topCharacteristics & Spliterator.SORTED) == 0, + "flatMap does not support SORTED characteristic"); + checkNotNull(fromSpliterator); + checkNotNull(function); + return new FlatMapSpliteratorOfDouble<>( + null, fromSpliterator, function, topCharacteristics, topSize); + } + + /** + * Implements the {@link Stream#flatMap} operation on spliterators. + * + * @param the element type of the input spliterator + * @param the element type of the output spliterators + * @param the type of the output spliterators + */ + abstract static class FlatMapSpliterator< + InElementT extends Object, + OutElementT extends Object, + OutSpliteratorT extends Spliterator> + implements Spliterator { + /** Factory for constructing {@link FlatMapSpliterator} instances. */ + @FunctionalInterface + interface Factory> { + OutSpliteratorT newFlatMapSpliterator( + OutSpliteratorT prefix, + Spliterator fromSplit, + Function function, + int splitCharacteristics, + long estSplitSize); + } + + OutSpliteratorT prefix; + final Spliterator from; + final Function function; + final Factory factory; + int characteristics; + long estimatedSize; + + FlatMapSpliterator( + OutSpliteratorT prefix, + Spliterator from, + Function function, + Factory factory, + int characteristics, + long estimatedSize) { + this.prefix = prefix; + this.from = from; + this.function = function; + this.factory = factory; + this.characteristics = characteristics; + this.estimatedSize = estimatedSize; + } + + /* + * The tryAdvance and forEachRemaining in FlatMapSpliteratorOfPrimitive are overloads of these + * methods, not overrides. They are annotated @Override because they implement methods from + * Spliterator.OfPrimitive (and override default implementations from Spliterator.OfPrimitive or + * a subtype like Spliterator.OfInt). + */ + + @Override + public final boolean tryAdvance(Consumer action) { + while (true) { + if (prefix != null && prefix.tryAdvance(action)) { + if (estimatedSize != Long.MAX_VALUE) { + estimatedSize--; + } + return true; + } else { + prefix = null; + } + if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) { + return false; + } + } + } + + @Override + public final void forEachRemaining(Consumer action) { + if (prefix != null) { + prefix.forEachRemaining(action); + prefix = null; + } + from.forEachRemaining( + fromElement -> { + Spliterator elements = function.apply(fromElement); + if (elements != null) { + elements.forEachRemaining(action); + } + }); + estimatedSize = 0; + } + + @Override + public final OutSpliteratorT trySplit() { + Spliterator fromSplit = from.trySplit(); + if (fromSplit != null) { + int splitCharacteristics = characteristics & ~Spliterator.SIZED; + long estSplitSize = estimateSize(); + if (estSplitSize < Long.MAX_VALUE) { + estSplitSize /= 2; + this.estimatedSize -= estSplitSize; + this.characteristics = splitCharacteristics; + } + OutSpliteratorT result = + factory.newFlatMapSpliterator( + this.prefix, fromSplit, function, splitCharacteristics, estSplitSize); + this.prefix = null; + return result; + } else if (prefix != null) { + OutSpliteratorT result = prefix; + this.prefix = null; + return result; + } else { + return null; + } + } + + @Override + public final long estimateSize() { + if (prefix != null) { + estimatedSize = max(estimatedSize, prefix.estimateSize()); + } + return max(estimatedSize, 0); + } + + @Override + public final int characteristics() { + return characteristics; + } + } + + /** + * Implementation of {@link Stream#flatMap} with an object spliterator output type. + * + *

To avoid having this type, we could use {@code FlatMapSpliterator} directly. The main + * advantages to having the type are the ability to use its constructor reference below and the + * parallelism with the primitive version. In short, it makes its caller ({@code flatMap}) + * simpler. + * + * @param the element type of the input spliterator + * @param the element type of the output spliterators + */ + static final class FlatMapSpliteratorOfObject< + InElementT extends Object, OutElementT extends Object> + extends FlatMapSpliterator> { + FlatMapSpliteratorOfObject( + Spliterator prefix, + Spliterator from, + Function> function, + int characteristics, + long estimatedSize) { + super( + prefix, from, function, FlatMapSpliteratorOfObject::new, characteristics, estimatedSize); + } + } + + /** + * Implementation of {@link Stream#flatMap} with a primitive spliterator output type. + * + * @param the element type of the input spliterator + * @param the (boxed) element type of the output spliterators + * @param the specialized consumer type for the primitive output type + * @param the primitive spliterator type associated with {@code OutElementT} + */ + abstract static class FlatMapSpliteratorOfPrimitive< + InElementT extends Object, + OutElementT extends Object, + OutConsumerT, + OutSpliteratorT extends + Spliterator.OfPrimitive> + extends FlatMapSpliterator + implements Spliterator.OfPrimitive { + + FlatMapSpliteratorOfPrimitive( + OutSpliteratorT prefix, + Spliterator from, + Function function, + Factory factory, + int characteristics, + long estimatedSize) { + super(prefix, from, function, factory, characteristics, estimatedSize); + } + + @Override + public final boolean tryAdvance(OutConsumerT action) { + while (true) { + if (prefix != null && prefix.tryAdvance(action)) { + if (estimatedSize != Long.MAX_VALUE) { + estimatedSize--; + } + return true; + } else { + prefix = null; + } + if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) { + return false; + } + } + } + + @Override + public final void forEachRemaining(OutConsumerT action) { + if (prefix != null) { + prefix.forEachRemaining(action); + prefix = null; + } + from.forEachRemaining( + fromElement -> { + OutSpliteratorT elements = function.apply(fromElement); + if (elements != null) { + elements.forEachRemaining(action); + } + }); + estimatedSize = 0; + } + } + + /** Implementation of {@link #flatMapToInt}. */ + static final class FlatMapSpliteratorOfInt + extends FlatMapSpliteratorOfPrimitive + implements Spliterator.OfInt { + FlatMapSpliteratorOfInt( + Spliterator.OfInt prefix, + Spliterator from, + Function function, + int characteristics, + long estimatedSize) { + super(prefix, from, function, FlatMapSpliteratorOfInt::new, characteristics, estimatedSize); + } + } + + /** Implementation of {@link #flatMapToLong}. */ + static final class FlatMapSpliteratorOfLong + extends FlatMapSpliteratorOfPrimitive + implements Spliterator.OfLong { + FlatMapSpliteratorOfLong( + Spliterator.OfLong prefix, + Spliterator from, + Function function, + int characteristics, + long estimatedSize) { + super(prefix, from, function, FlatMapSpliteratorOfLong::new, characteristics, estimatedSize); + } + } + + /** Implementation of {@link #flatMapToDouble}. */ + static final class FlatMapSpliteratorOfDouble + extends FlatMapSpliteratorOfPrimitive< + InElementT, Double, DoubleConsumer, Spliterator.OfDouble> + implements Spliterator.OfDouble { + FlatMapSpliteratorOfDouble( + Spliterator.OfDouble prefix, + Spliterator from, + Function function, + int characteristics, + long estimatedSize) { + super( + prefix, from, function, FlatMapSpliteratorOfDouble::new, characteristics, estimatedSize); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Collections2.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Collections2.java new file mode 100644 index 0000000000000..018755f07ef6b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Collections2.java @@ -0,0 +1,708 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Provides static methods for working with {@code Collection} instances. + * + *

Java 8 users: several common uses for this class are now more comprehensively addressed + * by the new {@link java.util.stream.Stream} library. Read the method documentation below for + * comparisons. These methods are not being deprecated, but we gently encourage you to migrate to + * streams. + * + * @author Chris Povirk + * @author Mike Bostock + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Collections2 { + private Collections2() {} + + /** + * Returns the elements of {@code unfiltered} that satisfy a predicate. The returned collection is + * a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting collection's iterator does not support {@code remove()}, but all other + * collection methods are supported. When given an element that doesn't satisfy the predicate, the + * collection's {@code add()} and {@code addAll()} methods throw an {@link + * IllegalArgumentException}. When methods such as {@code removeAll()} and {@code clear()} are + * called on the filtered collection, only elements that satisfy the filter will be removed from + * the underlying collection. + * + *

The returned collection isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered collection's methods, such as {@code size()}, iterate across every + * element in the underlying collection and determine which elements satisfy the filter. When a + * live view is not needed, it may be faster to copy {@code Iterables.filter(unfiltered, + * predicate)} and use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link + * Iterables#filter(Iterable, Class)} for related functionality.) + * + *

{@code Stream} equivalent: {@link java.util.stream.Stream#filter Stream.filter}. + */ + // TODO(kevinb): how can we omit that Iterables link when building gwt + // javadoc? + public static Collection filter( + Collection unfiltered, Predicate predicate) { + if (unfiltered instanceof FilteredCollection) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + return ((FilteredCollection) unfiltered).createCombined(predicate); + } + + return new FilteredCollection(checkNotNull(unfiltered), checkNotNull(predicate)); + } + + /** + * Delegates to {@link Collection#contains}. Returns {@code false} if the {@code contains} method + * throws a {@code ClassCastException} or {@code NullPointerException}. + */ + static boolean safeContains(Collection collection, Object object) { + checkNotNull(collection); + try { + return collection.contains(object); + } catch (ClassCastException | NullPointerException e) { + return false; + } + } + + /** + * Delegates to {@link Collection#remove}. Returns {@code false} if the {@code remove} method + * throws a {@code ClassCastException} or {@code NullPointerException}. + */ + static boolean safeRemove(Collection collection, Object object) { + checkNotNull(collection); + try { + return collection.remove(object); + } catch (ClassCastException | NullPointerException e) { + return false; + } + } + + static class FilteredCollection extends AbstractCollection { + final Collection unfiltered; + final Predicate predicate; + + FilteredCollection(Collection unfiltered, Predicate predicate) { + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + FilteredCollection createCombined(Predicate newPredicate) { + return new FilteredCollection(unfiltered, Predicates.and(predicate, newPredicate)); + // . above needed to compile in JDK 5 + } + + @Override + public boolean add(@ParametricNullness E element) { + checkArgument(predicate.apply(element)); + return unfiltered.add(element); + } + + @Override + public boolean addAll(Collection collection) { + for (E element : collection) { + checkArgument(predicate.apply(element)); + } + return unfiltered.addAll(collection); + } + + @Override + public void clear() { + Iterables.removeIf(unfiltered, predicate); + } + + @Override + public boolean contains(Object element) { + if (safeContains(unfiltered, element)) { + @SuppressWarnings("unchecked") // element is in unfiltered, so it must be an E + E e = (E) element; + return predicate.apply(e); + } + return false; + } + + @Override + public boolean containsAll(Collection collection) { + return containsAllImpl(this, collection); + } + + @Override + public boolean isEmpty() { + return !Iterables.any(unfiltered, predicate); + } + + @Override + public Iterator iterator() { + return Iterators.filter(unfiltered.iterator(), predicate); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.filter(unfiltered.spliterator(), predicate); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + unfiltered.forEach( + (E e) -> { + if (predicate.test(e)) { + action.accept(e); + } + }); + } + + @Override + public boolean remove(Object element) { + return contains(element) && unfiltered.remove(element); + } + + @Override + public boolean removeAll(final Collection collection) { + return removeIf(collection::contains); + } + + @Override + public boolean retainAll(final Collection collection) { + return removeIf(element -> !collection.contains(element)); + } + + @Override + public boolean removeIf(java.util.function.Predicate filter) { + checkNotNull(filter); + return unfiltered.removeIf(element -> predicate.apply(element) && filter.test(element)); + } + + @Override + public int size() { + int size = 0; + for (E e : unfiltered) { + if (predicate.apply(e)) { + size++; + } + } + return size; + } + + @Override + public Object[] toArray() { + // creating an ArrayList so filtering happens once + return Lists.newArrayList(iterator()).toArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return Lists.newArrayList(iterator()).toArray(array); + } + } + + /** + * Returns a collection that applies {@code function} to each element of {@code fromCollection}. + * The returned collection is a live view of {@code fromCollection}; changes to one affect the + * other. + * + *

The returned collection's {@code add()} and {@code addAll()} methods throw an {@link + * UnsupportedOperationException}. All other collection methods are supported, as long as {@code + * fromCollection} supports them. + * + *

The returned collection isn't threadsafe or serializable, even if {@code fromCollection} is. + * + *

When a live view is not needed, it may be faster to copy the transformed collection + * and use the copy. + * + *

If the input {@code Collection} is known to be a {@code List}, consider {@link + * Lists#transform}. If only an {@code Iterable} is available, use {@link Iterables#transform}. + * + *

{@code Stream} equivalent: {@link java.util.stream.Stream#map Stream.map}. + */ + public static Collection transform( + Collection fromCollection, Function function) { + return new TransformedCollection<>(fromCollection, function); + } + + static class TransformedCollection + extends AbstractCollection { + final Collection fromCollection; + final Function function; + + TransformedCollection(Collection fromCollection, Function function) { + this.fromCollection = checkNotNull(fromCollection); + this.function = checkNotNull(function); + } + + @Override + public void clear() { + fromCollection.clear(); + } + + @Override + public boolean isEmpty() { + return fromCollection.isEmpty(); + } + + @Override + public Iterator iterator() { + return Iterators.transform(fromCollection.iterator(), function); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.map(fromCollection.spliterator(), function); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + fromCollection.forEach((F f) -> action.accept(function.apply(f))); + } + + @Override + public boolean removeIf(java.util.function.Predicate filter) { + checkNotNull(filter); + return fromCollection.removeIf(element -> filter.test(function.apply(element))); + } + + @Override + public int size() { + return fromCollection.size(); + } + } + + /** + * Returns {@code true} if the collection {@code self} contains all of the elements in the + * collection {@code c}. + * + *

This method iterates over the specified collection {@code c}, checking each element returned + * by the iterator in turn to see if it is contained in the specified collection {@code self}. If + * all elements are so contained, {@code true} is returned, otherwise {@code false}. + * + * @param self a collection which might contain all elements in {@code c} + * @param c a collection whose elements might be contained by {@code self} + */ + static boolean containsAllImpl(Collection self, Collection c) { + for (Object o : c) { + if (!self.contains(o)) { + return false; + } + } + return true; + } + + /** An implementation of {@link Collection#toString()}. */ + static String toStringImpl(final Collection collection) { + StringBuilder sb = newStringBuilderForCollection(collection.size()).append('['); + boolean first = true; + for (Object o : collection) { + if (!first) { + sb.append(", "); + } + first = false; + if (o == collection) { + sb.append("(this Collection)"); + } else { + sb.append(o); + } + } + return sb.append(']').toString(); + } + + /** Returns best-effort-sized StringBuilder based on the given collection size. */ + static StringBuilder newStringBuilderForCollection(int size) { + checkNonnegative(size, "size"); + return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO)); + } + + /** + * Returns a {@link Collection} of all the permutations of the specified {@link Iterable}. + * + *

Notes: This is an implementation of the algorithm for Lexicographical Permutations + * Generation, described in Knuth's "The Art of Computer Programming", Volume 4, Chapter 7, + * Section 7.2.1.2. The iteration order follows the lexicographical order. This means that the + * first permutation will be in ascending order, and the last will be in descending order. + * + *

Duplicate elements are considered equal. For example, the list [1, 1] will have only one + * permutation, instead of two. This is why the elements have to implement {@link Comparable}. + * + *

An empty iterable has only one permutation, which is an empty list. + * + *

This method is equivalent to {@code Collections2.orderedPermutations(list, + * Ordering.natural())}. + * + * @param elements the original iterable whose elements have to be permuted. + * @return an immutable {@link Collection} containing all the different permutations of the + * original iterable. + * @throws NullPointerException if the specified iterable is null or has any null elements. + * @since 12.0 + */ + public static > Collection> orderedPermutations( + Iterable elements) { + return orderedPermutations(elements, Ordering.natural()); + } + + /** + * Returns a {@link Collection} of all the permutations of the specified {@link Iterable} using + * the specified {@link Comparator} for establishing the lexicographical ordering. + * + *

Examples: + * + *

{@code
+   * for (List perm : orderedPermutations(asList("b", "c", "a"))) {
+   *   println(perm);
+   * }
+   * // -> ["a", "b", "c"]
+   * // -> ["a", "c", "b"]
+   * // -> ["b", "a", "c"]
+   * // -> ["b", "c", "a"]
+   * // -> ["c", "a", "b"]
+   * // -> ["c", "b", "a"]
+   *
+   * for (List perm : orderedPermutations(asList(1, 2, 2, 1))) {
+   *   println(perm);
+   * }
+   * // -> [1, 1, 2, 2]
+   * // -> [1, 2, 1, 2]
+   * // -> [1, 2, 2, 1]
+   * // -> [2, 1, 1, 2]
+   * // -> [2, 1, 2, 1]
+   * // -> [2, 2, 1, 1]
+   * }
+ * + *

Notes: This is an implementation of the algorithm for Lexicographical Permutations + * Generation, described in Knuth's "The Art of Computer Programming", Volume 4, Chapter 7, + * Section 7.2.1.2. The iteration order follows the lexicographical order. This means that the + * first permutation will be in ascending order, and the last will be in descending order. + * + *

Elements that compare equal are considered equal and no new permutations are created by + * swapping them. + * + *

An empty iterable has only one permutation, which is an empty list. + * + * @param elements the original iterable whose elements have to be permuted. + * @param comparator a comparator for the iterable's elements. + * @return an immutable {@link Collection} containing all the different permutations of the + * original iterable. + * @throws NullPointerException If the specified iterable is null, has any null elements, or if + * the specified comparator is null. + * @since 12.0 + */ + public static Collection> orderedPermutations( + Iterable elements, Comparator comparator) { + return new OrderedPermutationCollection(elements, comparator); + } + + private static final class OrderedPermutationCollection extends AbstractCollection> { + final ImmutableList inputList; + final Comparator comparator; + final int size; + + OrderedPermutationCollection(Iterable input, Comparator comparator) { + this.inputList = ImmutableList.sortedCopyOf(comparator, input); + this.comparator = comparator; + this.size = calculateSize(inputList, comparator); + } + + /** + * The number of permutations with repeated elements is calculated as follows: + * + *

    + *
  • For an empty list, it is 1 (base case). + *
  • When r numbers are added to a list of n-r elements, the number of permutations is + * increased by a factor of (n choose r). + *
+ */ + private static int calculateSize( + List sortedInputList, Comparator comparator) { + int permutations = 1; + int n = 1; + int r = 1; + while (n < sortedInputList.size()) { + int comparison = comparator.compare(sortedInputList.get(n - 1), sortedInputList.get(n)); + if (comparison < 0) { + // We move to the next non-repeated element. + permutations = IntMath.saturatedMultiply(permutations, IntMath.binomial(n, r)); + r = 0; + if (permutations == Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + } + n++; + r++; + } + return IntMath.saturatedMultiply(permutations, IntMath.binomial(n, r)); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Iterator> iterator() { + return new OrderedPermutationIterator(inputList, comparator); + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof List) { + List list = (List) obj; + return isPermutation(inputList, list); + } + return false; + } + + @Override + public String toString() { + return "orderedPermutationCollection(" + inputList + ")"; + } + } + + private static final class OrderedPermutationIterator extends AbstractIterator> { + List nextPermutation; + final Comparator comparator; + + OrderedPermutationIterator(List list, Comparator comparator) { + this.nextPermutation = Lists.newArrayList(list); + this.comparator = comparator; + } + + @Override + protected List computeNext() { + if (nextPermutation == null) { + return endOfData(); + } + ImmutableList next = ImmutableList.copyOf(nextPermutation); + calculateNextPermutation(); + return next; + } + + void calculateNextPermutation() { + int j = findNextJ(); + if (j == -1) { + nextPermutation = null; + return; + } + /* + * requireNonNull is safe because we don't clear nextPermutation until we're done calling this + * method. + */ + requireNonNull(nextPermutation); + + int l = findNextL(j); + Collections.swap(nextPermutation, j, l); + int n = nextPermutation.size(); + Collections.reverse(nextPermutation.subList(j + 1, n)); + } + + int findNextJ() { + /* + * requireNonNull is safe because we don't clear nextPermutation until we're done calling this + * method. + */ + requireNonNull(nextPermutation); + for (int k = nextPermutation.size() - 2; k >= 0; k--) { + if (comparator.compare(nextPermutation.get(k), nextPermutation.get(k + 1)) < 0) { + return k; + } + } + return -1; + } + + int findNextL(int j) { + /* + * requireNonNull is safe because we don't clear nextPermutation until we're done calling this + * method. + */ + requireNonNull(nextPermutation); + E ak = nextPermutation.get(j); + for (int l = nextPermutation.size() - 1; l > j; l--) { + if (comparator.compare(ak, nextPermutation.get(l)) < 0) { + return l; + } + } + throw new AssertionError("this statement should be unreachable"); + } + } + + /** + * Returns a {@link Collection} of all the permutations of the specified {@link Collection}. + * + *

Notes: This is an implementation of the Plain Changes algorithm for permutations + * generation, described in Knuth's "The Art of Computer Programming", Volume 4, Chapter 7, + * Section 7.2.1.2. + * + *

If the input list contains equal elements, some of the generated permutations will be equal. + * + *

An empty collection has only one permutation, which is an empty list. + * + * @param elements the original collection whose elements have to be permuted. + * @return an immutable {@link Collection} containing all the different permutations of the + * original collection. + * @throws NullPointerException if the specified collection is null or has any null elements. + * @since 12.0 + */ + public static Collection> permutations(Collection elements) { + return new PermutationCollection(ImmutableList.copyOf(elements)); + } + + private static final class PermutationCollection extends AbstractCollection> { + final ImmutableList inputList; + + PermutationCollection(ImmutableList input) { + this.inputList = input; + } + + @Override + public int size() { + return IntMath.factorial(inputList.size()); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Iterator> iterator() { + return new PermutationIterator(inputList); + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof List) { + List list = (List) obj; + return isPermutation(inputList, list); + } + return false; + } + + @Override + public String toString() { + return "permutations(" + inputList + ")"; + } + } + + private static class PermutationIterator extends AbstractIterator> { + final List list; + final int[] c; + final int[] o; + int j; + + PermutationIterator(List list) { + this.list = new ArrayList(list); + int n = list.size(); + c = new int[n]; + o = new int[n]; + Arrays.fill(c, 0); + Arrays.fill(o, 1); + j = Integer.MAX_VALUE; + } + + @Override + protected List computeNext() { + if (j <= 0) { + return endOfData(); + } + ImmutableList next = ImmutableList.copyOf(list); + calculateNextPermutation(); + return next; + } + + void calculateNextPermutation() { + j = list.size() - 1; + int s = 0; + + // Handle the special case of an empty list. Skip the calculation of the + // next permutation. + if (j == -1) { + return; + } + + while (true) { + int q = c[j] + o[j]; + if (q < 0) { + switchDirection(); + continue; + } + if (q == j + 1) { + if (j == 0) { + break; + } + s++; + switchDirection(); + continue; + } + + Collections.swap(list, j - c[j] + s, j - q + s); + c[j] = q; + break; + } + } + + void switchDirection() { + o[j] = -o[j]; + j--; + } + } + + /** Returns {@code true} if the second list is a permutation of the first. */ + private static boolean isPermutation(List first, List second) { + if (first.size() != second.size()) { + return false; + } + Multiset firstMultiset = HashMultiset.create(first); + Multiset secondMultiset = HashMultiset.create(second); + return firstMultiset.equals(secondMultiset); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashMap.java new file mode 100644 index 0000000000000..5a9e99da69b7d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashMap.java @@ -0,0 +1,1194 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; +import static org.apache.iotdb.google.common.collect.CompactHashing.UNSET; +import static org.apache.iotdb.google.common.collect.Hashing.smearedHash; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.collect.NullnessCasts.unsafeNull; + +/** + * CompactHashMap is an implementation of a Map. All optional operations (put and remove) are + * supported. Null keys and values are supported. + * + *

{@code containsKey(k)}, {@code put(k, v)} and {@code remove(k)} are all (expected and + * amortized) constant time operations. Expected in the hashtable sense (depends on the hash + * function doing a good job of distributing the elements to the buckets to a distribution not far + * from uniform), and amortized since some operations can trigger a hash table resize. + * + *

Unlike {@code java.util.HashMap}, iteration is only proportional to the actual {@code size()}, + * which is optimal, and not the size of the internal hashtable, which could be much larger + * than {@code size()}. Furthermore, this structure places significantly reduced load on the garbage + * collector by only using a constant number of internal objects. + * + *

If there are no removals, then iteration order for the {@link #entrySet}, {@link #keySet}, and + * {@link #values} views is the same as insertion order. Any removal invalidates any ordering + * guarantees. + * + *

This class should not be assumed to be universally superior to {@code java.util.HashMap}. + * Generally speaking, this class reduces object allocation and memory consumption at the price of + * moderately increased constant factors of CPU. Only use this class when there is a specific reason + * to prioritize memory over CPU. + * + * @author Louis Wasserman + * @author Jon Noack + */ +@GwtIncompatible // not worth using in GWT for now +@ElementTypesAreNonnullByDefault +class CompactHashMap extends AbstractMap + implements Serializable { + /* + * TODO: Make this a drop-in replacement for j.u. versions, actually drop them in, and test the + * world. Figure out what sort of space-time tradeoff we're actually going to get here with the + * *Map variants. This class is particularly hard to benchmark, because the benefit is not only in + * less allocation, but also having the GC do less work to scan the heap because of fewer + * references, which is particularly hard to quantify. + */ + + /** Creates an empty {@code CompactHashMap} instance. */ + public static CompactHashMap create() { + return new CompactHashMap<>(); + } + + /** + * Creates a {@code CompactHashMap} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without growth. + * + * @param expectedSize the number of elements you expect to add to the returned set + * @return a new, empty {@code CompactHashMap} with enough capacity to hold {@code expectedSize} + * elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static CompactHashMap createWithExpectedSize( + int expectedSize) { + return new CompactHashMap<>(expectedSize); + } + + private static final Object NOT_FOUND = new Object(); + + /** + * Maximum allowed false positive probability of detecting a hash flooding attack given random + * input. + */ + @VisibleForTesting() static final double HASH_FLOODING_FPP = 0.001; + + /** + * Maximum allowed length of a hash table bucket before falling back to a j.u.LinkedHashMap-based + * implementation. Experimentally determined. + */ + private static final int MAX_HASH_BUCKET_LENGTH = 9; + + // The way the `table`, `entries`, `keys`, and `values` arrays work together is as follows. + // + // The `table` array always has a size that is a power of 2. The hashcode of a key in the map + // is masked in order to correspond to the current table size. For example, if the table size + // is 128 then the mask is 127 == 0x7f, keeping the bottom 7 bits of the hash value. + // If a key hashes to 0x89abcdef the mask reduces it to 0x89abcdef & 0x7f == 0x6f. We'll call this + // the "short hash". + // + // The `keys`, `values`, and `entries` arrays always have the same size as each other. They can be + // seen as fields of an imaginary `Entry` object like this: + // + // class Entry { + // int hash; + // Entry next; + // K key; + // V value; + // } + // + // The imaginary `hash` and `next` values are combined into a single `int` value in the `entries` + // array. The top bits of this value are the remaining bits of the hash value that were not used + // in the short hash. We saw that a mask of 0x7f would keep the 7-bit value 0x6f from a full + // hashcode of 0x89abcdef. The imaginary `hash` value would then be the remaining top 25 bits, + // 0x89abcd80. To this is added (or'd) the `next` value, which is an index within `entries` + // (and therefore within `keys` and `values`) of another entry that has the same short hash + // value. In our example, it would be another entry for a key whose short hash is also 0x6f. + // + // Essentially, then, `table[h]` gives us the start of a linked list in `entries`, where every + // element of the list has the short hash value h. + // + // A wrinkle here is that the value 0 (called UNSET in the code) is used as the equivalent of a + // null pointer. If `table[h] == 0` that means there are no keys in the map whose short hash is h. + // If the `next` bits in `entries[i]` are 0 that means there are no further entries for the given + // short hash. But 0 is also a valid index in `entries`, so we add 1 to these indices before + // putting them in `table` or in `next` bits, and subtract 1 again when we need an index value. + // + // The elements of `keys`, `values`, and `entries` are added sequentially, so that elements 0 to + // `size() - 1` are used and remaining elements are not. This makes iteration straightforward. + // Removing an entry generally involves moving the last element of each array to where the removed + // entry was, and adjusting index links accordingly. + + /** + * The hashtable object. This can be either: + * + *

    + *
  • a byte[], short[], or int[], with size a power of two, created by + * CompactHashing.createTable, whose values are either + *
      + *
    • UNSET, meaning "null pointer" + *
    • one plus an index into the keys, values, and entries arrays + *
    + *
  • another java.util.Map delegate implementation. In most modern JDKs, normal java.util hash + * collections intelligently fall back to a binary search tree if hash table collisions are + * detected. Rather than going to all the trouble of reimplementing this ourselves, we + * simply switch over to use the JDK implementation wholesale if probable hash flooding is + * detected, sacrificing the compactness guarantee in very rare cases in exchange for much + * more reliable worst-case behavior. + *
  • null, if no entries have yet been added to the map + *
+ */ + private transient Object table; + + /** + * Contains the logical entries, in the range of [0, size()). The high bits of each int are the + * part of the smeared hash of the key not covered by the hashtable mask, whereas the low bits are + * the "next" pointer (pointing to the next entry in the bucket chain), which will always be less + * than or equal to the hashtable mask. + * + *
+   * hash  = aaaaaaaa
+   * mask  = 00000fff
+   * next  = 00000bbb
+   * entry = aaaaabbb
+   * 
+ * + *

The pointers in [size(), entries.length) are all "null" (UNSET). + */ + @VisibleForTesting transient int[] entries; + + /** + * The keys of the entries in the map, in the range of [0, size()). The keys in [size(), + * keys.length) are all {@code null}. + */ + @VisibleForTesting transient Object[] keys; + + /** + * The values of the entries in the map, in the range of [0, size()). The values in [size(), + * values.length) are all {@code null}. + */ + @VisibleForTesting transient Object[] values; + + /** + * Keeps track of metadata like the number of hash table bits and modifications of this data + * structure (to make it possible to throw ConcurrentModificationException in the iterator). Note + * that we choose not to make this volatile, so we do less of a "best effort" to track such + * errors, for better performance. + * + *

For a new instance, where the arrays above have not yet been allocated, the value of {@code + * metadata} is the size that the arrays should be allocated with. Once the arrays have been + * allocated, the value of {@code metadata} combines the number of bits in the "short hash", in + * its bottom {@value CompactHashing#HASH_TABLE_BITS_MAX_BITS} bits, with a modification count in + * the remaining bits that is used to detect concurrent modification during iteration. + */ + private transient int metadata; + + /** The number of elements contained in the set. */ + private transient int size; + + /** Constructs a new empty instance of {@code CompactHashMap}. */ + CompactHashMap() { + init(CompactHashing.DEFAULT_SIZE); + } + + /** + * Constructs a new instance of {@code CompactHashMap} with the specified capacity. + * + * @param expectedSize the initial capacity of this {@code CompactHashMap}. + */ + CompactHashMap(int expectedSize) { + init(expectedSize); + } + + /** Pseudoconstructor for serialization support. */ + void init(int expectedSize) { + Preconditions.checkArgument(expectedSize >= 0, "Expected size must be >= 0"); + + // Save expectedSize for use in allocArrays() + this.metadata = Ints.constrainToRange(expectedSize, 1, CompactHashing.MAX_SIZE); + } + + /** Returns whether arrays need to be allocated. */ + @VisibleForTesting + boolean needsAllocArrays() { + return table == null; + } + + /** Handle lazy allocation of arrays. */ + int allocArrays() { + Preconditions.checkState(needsAllocArrays(), "Arrays already allocated"); + + int expectedSize = metadata; + int buckets = CompactHashing.tableSize(expectedSize); + this.table = CompactHashing.createTable(buckets); + setHashTableMask(buckets - 1); + + this.entries = new int[expectedSize]; + this.keys = new Object[expectedSize]; + this.values = new Object[expectedSize]; + + return expectedSize; + } + + @SuppressWarnings("unchecked") + @VisibleForTesting + Map delegateOrNull() { + if (table instanceof Map) { + return (Map) table; + } + return null; + } + + Map createHashFloodingResistantDelegate(int tableSize) { + return new LinkedHashMap<>(tableSize, 1.0f); + } + + @VisibleForTesting + Map convertToHashFloodingResistantImplementation() { + Map newDelegate = createHashFloodingResistantDelegate(hashTableMask() + 1); + for (int i = firstEntryIndex(); i >= 0; i = getSuccessor(i)) { + newDelegate.put(key(i), value(i)); + } + this.table = newDelegate; + this.entries = null; + this.keys = null; + this.values = null; + incrementModCount(); + return newDelegate; + } + + /** Stores the hash table mask as the number of bits needed to represent an index. */ + private void setHashTableMask(int mask) { + int hashTableBits = Integer.SIZE - Integer.numberOfLeadingZeros(mask); + metadata = + CompactHashing.maskCombine(metadata, hashTableBits, CompactHashing.HASH_TABLE_BITS_MASK); + } + + /** Gets the hash table mask using the stored number of hash table bits. */ + private int hashTableMask() { + return (1 << (metadata & CompactHashing.HASH_TABLE_BITS_MASK)) - 1; + } + + void incrementModCount() { + metadata += CompactHashing.MODIFICATION_COUNT_INCREMENT; + } + + /** + * Mark an access of the specified entry. Used only in {@code CompactLinkedHashMap} for LRU + * ordering. + */ + void accessEntry(int index) { + // no-op by default + } + + @Override + public V put(@ParametricNullness K key, @ParametricNullness V value) { + if (needsAllocArrays()) { + allocArrays(); + } + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.put(key, value); + } + int[] entries = requireEntries(); + Object[] keys = requireKeys(); + Object[] values = requireValues(); + + int newEntryIndex = this.size; // current size, and pointer to the entry to be appended + int newSize = newEntryIndex + 1; + int hash = smearedHash(key); + int mask = hashTableMask(); + int tableIndex = hash & mask; + int next = CompactHashing.tableGet(requireTable(), tableIndex); + if (next == UNSET) { // uninitialized bucket + if (newSize > mask) { + // Resize and add new entry + mask = resizeTable(mask, CompactHashing.newCapacity(mask), hash, newEntryIndex); + } else { + CompactHashing.tableSet(requireTable(), tableIndex, newEntryIndex + 1); + } + } else { + int entryIndex; + int entry; + int hashPrefix = CompactHashing.getHashPrefix(hash, mask); + int bucketLength = 0; + do { + entryIndex = next - 1; + entry = entries[entryIndex]; + if (CompactHashing.getHashPrefix(entry, mask) == hashPrefix + && Objects.equal(key, keys[entryIndex])) { + @SuppressWarnings("unchecked") // known to be a V + V oldValue = (V) values[entryIndex]; + + values[entryIndex] = value; + accessEntry(entryIndex); + return oldValue; + } + next = CompactHashing.getNext(entry, mask); + bucketLength++; + } while (next != UNSET); + + if (bucketLength >= MAX_HASH_BUCKET_LENGTH) { + return convertToHashFloodingResistantImplementation().put(key, value); + } + + if (newSize > mask) { + // Resize and add new entry + mask = resizeTable(mask, CompactHashing.newCapacity(mask), hash, newEntryIndex); + } else { + entries[entryIndex] = CompactHashing.maskCombine(entry, newEntryIndex + 1, mask); + } + } + resizeMeMaybe(newSize); + insertEntry(newEntryIndex, key, value, hash, mask); + this.size = newSize; + incrementModCount(); + return null; + } + + /** + * Creates a fresh entry with the specified object at the specified position in the entry arrays. + */ + void insertEntry( + int entryIndex, @ParametricNullness K key, @ParametricNullness V value, int hash, int mask) { + this.setEntry(entryIndex, CompactHashing.maskCombine(hash, UNSET, mask)); + this.setKey(entryIndex, key); + this.setValue(entryIndex, value); + } + + /** Resizes the entries storage if necessary. */ + private void resizeMeMaybe(int newSize) { + int entriesSize = requireEntries().length; + if (newSize > entriesSize) { + // 1.5x but round up to nearest odd (this is optimal for memory consumption on Android) + int newCapacity = + Math.min(CompactHashing.MAX_SIZE, (entriesSize + Math.max(1, entriesSize >>> 1)) | 1); + if (newCapacity != entriesSize) { + resizeEntries(newCapacity); + } + } + } + + /** + * Resizes the internal entries array to the specified capacity, which may be greater or less than + * the current capacity. + */ + void resizeEntries(int newCapacity) { + this.entries = Arrays.copyOf(requireEntries(), newCapacity); + this.keys = Arrays.copyOf(requireKeys(), newCapacity); + this.values = Arrays.copyOf(requireValues(), newCapacity); + } + + private int resizeTable(int oldMask, int newCapacity, int targetHash, int targetEntryIndex) { + Object newTable = CompactHashing.createTable(newCapacity); + int newMask = newCapacity - 1; + + if (targetEntryIndex != UNSET) { + // Add target first; it must be last in the chain because its entry hasn't yet been created + CompactHashing.tableSet(newTable, targetHash & newMask, targetEntryIndex + 1); + } + + Object oldTable = requireTable(); + int[] entries = requireEntries(); + + // Loop over `oldTable` to construct its replacement, ``newTable`. The entries do not move, so + // the `keys` and `values` arrays do not need to change. But because the "short hash" now has a + // different number of bits, we must rewrite each element of `entries` so that its contribution + // to the full hashcode reflects the change, and so that its `next` link corresponds to the new + // linked list of entries with the new short hash. + for (int oldTableIndex = 0; oldTableIndex <= oldMask; oldTableIndex++) { + int oldNext = CompactHashing.tableGet(oldTable, oldTableIndex); + // Each element of `oldTable` is the head of a (possibly empty) linked list of elements in + // `entries`. The `oldNext` loop is going to traverse that linked list. + // We need to rewrite the `next` link of each of the elements so that it is in the appropriate + // linked list starting from `newTable`. In general, each element from the old linked list + // belongs to a different linked list from `newTable`. We insert each element in turn at the + // head of its appropriate `newTable` linked list. + while (oldNext != UNSET) { + int entryIndex = oldNext - 1; + int oldEntry = entries[entryIndex]; + + // Rebuild the full 32-bit hash using entry hashPrefix and oldTableIndex ("hashSuffix"). + int hash = CompactHashing.getHashPrefix(oldEntry, oldMask) | oldTableIndex; + + int newTableIndex = hash & newMask; + int newNext = CompactHashing.tableGet(newTable, newTableIndex); + CompactHashing.tableSet(newTable, newTableIndex, oldNext); + entries[entryIndex] = CompactHashing.maskCombine(hash, newNext, newMask); + + oldNext = CompactHashing.getNext(oldEntry, oldMask); + } + } + + this.table = newTable; + setHashTableMask(newMask); + return newMask; + } + + private int indexOf(Object key) { + if (needsAllocArrays()) { + return -1; + } + int hash = smearedHash(key); + int mask = hashTableMask(); + int next = CompactHashing.tableGet(requireTable(), hash & mask); + if (next == UNSET) { + return -1; + } + int hashPrefix = CompactHashing.getHashPrefix(hash, mask); + do { + int entryIndex = next - 1; + int entry = entry(entryIndex); + if (CompactHashing.getHashPrefix(entry, mask) == hashPrefix + && Objects.equal(key, key(entryIndex))) { + return entryIndex; + } + next = CompactHashing.getNext(entry, mask); + } while (next != UNSET); + return -1; + } + + @Override + public boolean containsKey(Object key) { + Map delegate = delegateOrNull(); + return (delegate != null) ? delegate.containsKey(key) : indexOf(key) != -1; + } + + @Override + public V get(Object key) { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.get(key); + } + int index = indexOf(key); + if (index == -1) { + return null; + } + accessEntry(index); + return value(index); + } + + @SuppressWarnings("unchecked") // known to be a V + @Override + public V remove(Object key) { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.remove(key); + } + Object oldValue = removeHelper(key); + return (oldValue == NOT_FOUND) ? null : (V) oldValue; + } + + private Object removeHelper(Object key) { + if (needsAllocArrays()) { + return NOT_FOUND; + } + int mask = hashTableMask(); + int index = + CompactHashing.remove( + key, + /* value= */ null, + mask, + requireTable(), + requireEntries(), + requireKeys(), + /* values= */ null); + if (index == -1) { + return NOT_FOUND; + } + + Object oldValue = value(index); + + moveLastEntry(index, mask); + size--; + incrementModCount(); + + return oldValue; + } + + /** + * Moves the last entry in the entry array into {@code dstIndex}, and nulls out its old position. + */ + void moveLastEntry(int dstIndex, int mask) { + Object table = requireTable(); + int[] entries = requireEntries(); + Object[] keys = requireKeys(); + Object[] values = requireValues(); + int srcIndex = size() - 1; + if (dstIndex < srcIndex) { + // move last entry to deleted spot + Object key = keys[srcIndex]; + keys[dstIndex] = key; + values[dstIndex] = values[srcIndex]; + keys[srcIndex] = null; + values[srcIndex] = null; + + // move the last entry to the removed spot, just like we moved the element + entries[dstIndex] = entries[srcIndex]; + entries[srcIndex] = 0; + + // also need to update whoever's "next" pointer was pointing to the last entry place + int tableIndex = smearedHash(key) & mask; + int next = CompactHashing.tableGet(table, tableIndex); + int srcNext = srcIndex + 1; + if (next == srcNext) { + // we need to update the root pointer + CompactHashing.tableSet(table, tableIndex, dstIndex + 1); + } else { + // we need to update a pointer in an entry + int entryIndex; + int entry; + do { + entryIndex = next - 1; + entry = entries[entryIndex]; + next = CompactHashing.getNext(entry, mask); + } while (next != srcNext); + // here, entries[entryIndex] points to the old entry location; update it + entries[entryIndex] = CompactHashing.maskCombine(entry, dstIndex + 1, mask); + } + } else { + keys[dstIndex] = null; + values[dstIndex] = null; + entries[dstIndex] = 0; + } + } + + int firstEntryIndex() { + return isEmpty() ? -1 : 0; + } + + int getSuccessor(int entryIndex) { + return (entryIndex + 1 < size) ? entryIndex + 1 : -1; + } + + /** + * Updates the index an iterator is pointing to after a call to remove: returns the index of the + * entry that should be looked at after a removal on indexRemoved, with indexBeforeRemove as the + * index that *was* the next entry that would be looked at. + */ + int adjustAfterRemove(int indexBeforeRemove, @SuppressWarnings("unused") int indexRemoved) { + return indexBeforeRemove - 1; + } + + private abstract class Itr implements Iterator { + int expectedMetadata = metadata; + int currentIndex = firstEntryIndex(); + int indexToRemove = -1; + + @Override + public boolean hasNext() { + return currentIndex >= 0; + } + + @ParametricNullness + abstract T getOutput(int entry); + + @Override + @ParametricNullness + public T next() { + checkForConcurrentModification(); + if (!hasNext()) { + throw new NoSuchElementException(); + } + indexToRemove = currentIndex; + T result = getOutput(currentIndex); + currentIndex = getSuccessor(currentIndex); + return result; + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkRemove(indexToRemove >= 0); + incrementExpectedModCount(); + CompactHashMap.this.remove(key(indexToRemove)); + currentIndex = adjustAfterRemove(currentIndex, indexToRemove); + indexToRemove = -1; + } + + void incrementExpectedModCount() { + expectedMetadata += CompactHashing.MODIFICATION_COUNT_INCREMENT; + } + + private void checkForConcurrentModification() { + if (metadata != expectedMetadata) { + throw new ConcurrentModificationException(); + } + } + } + + @Override + public void replaceAll(BiFunction function) { + checkNotNull(function); + Map delegate = delegateOrNull(); + if (delegate != null) { + delegate.replaceAll(function); + } else { + for (int i = 0; i < size; i++) { + setValue(i, function.apply(key(i), value(i))); + } + } + } + + private transient Set keySetView; + + @Override + public Set keySet() { + return (keySetView == null) ? keySetView = createKeySet() : keySetView; + } + + Set createKeySet() { + return new KeySetView(); + } + + class KeySetView extends Maps.KeySet { + KeySetView() { + super(CompactHashMap.this); + } + + @Override + public Object[] toArray() { + if (needsAllocArrays()) { + return new Object[0]; + } + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.keySet().toArray() + : ObjectArrays.copyAsObjectArray(requireKeys(), 0, size); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + if (needsAllocArrays()) { + if (a.length > 0) { + Object[] unsoundlyCovariantArray = a; + unsoundlyCovariantArray[0] = null; + } + return a; + } + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.keySet().toArray(a) + : ObjectArrays.toArrayImpl(requireKeys(), 0, size, a); + } + + @Override + public boolean remove(Object o) { + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.keySet().remove(o) + : CompactHashMap.this.removeHelper(o) != NOT_FOUND; + } + + @Override + public Iterator iterator() { + return keySetIterator(); + } + + @Override + public Spliterator spliterator() { + if (needsAllocArrays()) { + return Spliterators.spliterator(new Object[0], Spliterator.DISTINCT | Spliterator.ORDERED); + } + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.keySet().spliterator() + : Spliterators.spliterator( + requireKeys(), 0, size, Spliterator.DISTINCT | Spliterator.ORDERED); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + Map delegate = delegateOrNull(); + if (delegate != null) { + delegate.keySet().forEach(action); + } else { + for (int i = firstEntryIndex(); i >= 0; i = getSuccessor(i)) { + action.accept(key(i)); + } + } + } + } + + Iterator keySetIterator() { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.keySet().iterator(); + } + return new Itr() { + @Override + @ParametricNullness + K getOutput(int entry) { + return key(entry); + } + }; + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + Map delegate = delegateOrNull(); + if (delegate != null) { + delegate.forEach(action); + } else { + for (int i = firstEntryIndex(); i >= 0; i = getSuccessor(i)) { + action.accept(key(i), value(i)); + } + } + } + + private transient Set> entrySetView; + + @Override + public Set> entrySet() { + return (entrySetView == null) ? entrySetView = createEntrySet() : entrySetView; + } + + Set> createEntrySet() { + return new EntrySetView(); + } + + class EntrySetView extends Maps.EntrySet { + @Override + Map map() { + return CompactHashMap.this; + } + + @Override + public Iterator> iterator() { + return entrySetIterator(); + } + + @Override + public Spliterator> spliterator() { + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.entrySet().spliterator() + : CollectSpliterators.indexed( + size, Spliterator.DISTINCT | Spliterator.ORDERED, MapEntry::new); + } + + @Override + public boolean contains(Object o) { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.entrySet().contains(o); + } else if (o instanceof Entry) { + Entry entry = (Entry) o; + int index = indexOf(entry.getKey()); + return index != -1 && Objects.equal(value(index), entry.getValue()); + } + return false; + } + + @Override + public boolean remove(Object o) { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.entrySet().remove(o); + } else if (o instanceof Entry) { + Entry entry = (Entry) o; + if (needsAllocArrays()) { + return false; + } + int mask = hashTableMask(); + int index = + CompactHashing.remove( + entry.getKey(), + entry.getValue(), + mask, + requireTable(), + requireEntries(), + requireKeys(), + requireValues()); + if (index == -1) { + return false; + } + + moveLastEntry(index, mask); + size--; + incrementModCount(); + + return true; + } + return false; + } + } + + Iterator> entrySetIterator() { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.entrySet().iterator(); + } + return new Itr>() { + @Override + Entry getOutput(int entry) { + return new MapEntry(entry); + } + }; + } + + final class MapEntry extends AbstractMapEntry { + @ParametricNullness private final K key; + + private int lastKnownIndex; + + MapEntry(int index) { + this.key = key(index); + this.lastKnownIndex = index; + } + + @Override + @ParametricNullness + public K getKey() { + return key; + } + + private void updateLastKnownIndex() { + if (lastKnownIndex == -1 + || lastKnownIndex >= size() + || !Objects.equal(key, key(lastKnownIndex))) { + lastKnownIndex = indexOf(key); + } + } + + @Override + @ParametricNullness + public V getValue() { + Map delegate = delegateOrNull(); + if (delegate != null) { + /* + * The cast is safe because the entry is present in the map. Or, if it has been removed by a + * concurrent modification, behavior is undefined. + */ + return uncheckedCastNullableTToT(delegate.get(key)); + } + updateLastKnownIndex(); + /* + * If the entry has been removed from the map, we return null, even though that might not be a + * valid value. That's the best we can do, short of holding a reference to the most recently + * seen value. And while we *could* do that, we aren't required to: Map.Entry explicitly says + * that behavior is undefined when the backing map is modified through another API. (It even + * permits us to throw IllegalStateException. Maybe we should have done that, but we probably + * shouldn't change now for fear of breaking people.) + */ + return (lastKnownIndex == -1) ? unsafeNull() : value(lastKnownIndex); + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V value) { + Map delegate = delegateOrNull(); + if (delegate != null) { + return uncheckedCastNullableTToT(delegate.put(key, value)); // See discussion in getValue(). + } + updateLastKnownIndex(); + if (lastKnownIndex == -1) { + put(key, value); + return unsafeNull(); // See discussion in getValue(). + } else { + V old = value(lastKnownIndex); + CompactHashMap.this.setValue(lastKnownIndex, value); + return old; + } + } + } + + @Override + public int size() { + Map delegate = delegateOrNull(); + return (delegate != null) ? delegate.size() : size; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsValue(Object value) { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.containsValue(value); + } + for (int i = 0; i < size; i++) { + if (Objects.equal(value, value(i))) { + return true; + } + } + return false; + } + + private transient Collection valuesView; + + @Override + public Collection values() { + return (valuesView == null) ? valuesView = createValues() : valuesView; + } + + Collection createValues() { + return new ValuesView(); + } + + class ValuesView extends Maps.Values { + ValuesView() { + super(CompactHashMap.this); + } + + @Override + public Iterator iterator() { + return valuesIterator(); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + Map delegate = delegateOrNull(); + if (delegate != null) { + delegate.values().forEach(action); + } else { + for (int i = firstEntryIndex(); i >= 0; i = getSuccessor(i)) { + action.accept(value(i)); + } + } + } + + @Override + public Spliterator spliterator() { + if (needsAllocArrays()) { + return Spliterators.spliterator(new Object[0], Spliterator.ORDERED); + } + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.values().spliterator() + : Spliterators.spliterator(requireValues(), 0, size, Spliterator.ORDERED); + } + + @Override + public Object[] toArray() { + if (needsAllocArrays()) { + return new Object[0]; + } + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.values().toArray() + : ObjectArrays.copyAsObjectArray(requireValues(), 0, size); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + if (needsAllocArrays()) { + if (a.length > 0) { + Object[] unsoundlyCovariantArray = a; + unsoundlyCovariantArray[0] = null; + } + return a; + } + Map delegate = delegateOrNull(); + return (delegate != null) + ? delegate.values().toArray(a) + : ObjectArrays.toArrayImpl(requireValues(), 0, size, a); + } + } + + Iterator valuesIterator() { + Map delegate = delegateOrNull(); + if (delegate != null) { + return delegate.values().iterator(); + } + return new Itr() { + @Override + @ParametricNullness + V getOutput(int entry) { + return value(entry); + } + }; + } + + /** + * Ensures that this {@code CompactHashMap} has the smallest representation in memory, given its + * current size. + */ + public void trimToSize() { + if (needsAllocArrays()) { + return; + } + Map delegate = delegateOrNull(); + if (delegate != null) { + Map newDelegate = createHashFloodingResistantDelegate(size()); + newDelegate.putAll(delegate); + this.table = newDelegate; + return; + } + int size = this.size; + if (size < requireEntries().length) { + resizeEntries(size); + } + int minimumTableSize = CompactHashing.tableSize(size); + int mask = hashTableMask(); + if (minimumTableSize < mask) { // smaller table size will always be less than current mask + resizeTable(mask, minimumTableSize, UNSET, UNSET); + } + } + + @Override + public void clear() { + if (needsAllocArrays()) { + return; + } + incrementModCount(); + Map delegate = delegateOrNull(); + if (delegate != null) { + metadata = + Ints.constrainToRange(size(), CompactHashing.DEFAULT_SIZE, CompactHashing.MAX_SIZE); + delegate.clear(); // invalidate any iterators left over! + table = null; + size = 0; + } else { + Arrays.fill(requireKeys(), 0, size, null); + Arrays.fill(requireValues(), 0, size, null); + CompactHashing.tableClear(requireTable()); + Arrays.fill(requireEntries(), 0, size, 0); + this.size = 0; + } + } + + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(size()); + Iterator> entryIterator = entrySetIterator(); + while (entryIterator.hasNext()) { + Entry e = entryIterator.next(); + stream.writeObject(e.getKey()); + stream.writeObject(e.getValue()); + } + } + + @SuppressWarnings("unchecked") + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int elementCount = stream.readInt(); + if (elementCount < 0) { + throw new InvalidObjectException("Invalid size: " + elementCount); + } + init(elementCount); + for (int i = 0; i < elementCount; i++) { + K key = (K) stream.readObject(); + V value = (V) stream.readObject(); + put(key, value); + } + } + + /* + * The following methods are safe to call as long as both of the following hold: + * + * - allocArrays() has been called. Callers can confirm this by checking needsAllocArrays(). + * + * - The map has not switched to delegating to a java.util implementation to mitigate hash + * flooding. Callers can confirm this by null-checking delegateOrNull(). + * + * In an ideal world, we would document why we know those things are true every time we call these + * methods. But that is a bit too painful.... + */ + + private Object requireTable() { + return requireNonNull(table); + } + + private int[] requireEntries() { + return requireNonNull(entries); + } + + private Object[] requireKeys() { + return requireNonNull(keys); + } + + private Object[] requireValues() { + return requireNonNull(values); + } + + /* + * The following methods are safe to call as long as the conditions in the *previous* comment are + * met *and* the index is less than size(). + * + * (The above explains when these methods are safe from a `nullness` perspective. From an + * `unchecked` perspective, they're safe because we put only K/V elements into each array.) + */ + + @SuppressWarnings("unchecked") + private K key(int i) { + return (K) requireKeys()[i]; + } + + @SuppressWarnings("unchecked") + private V value(int i) { + return (V) requireValues()[i]; + } + + private int entry(int i) { + return requireEntries()[i]; + } + + private void setKey(int i, K key) { + requireKeys()[i] = key; + } + + private void setValue(int i, V value) { + requireValues()[i] = value; + } + + private void setEntry(int i, int value) { + requireEntries()[i] = value; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashSet.java new file mode 100644 index 0000000000000..eb2b21c2a98b3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashSet.java @@ -0,0 +1,749 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; +import static org.apache.iotdb.google.common.collect.CompactHashing.UNSET; +import static org.apache.iotdb.google.common.collect.Hashing.smearedHash; + +/** + * CompactHashSet is an implementation of a Set. All optional operations (adding and removing) are + * supported. The elements can be any objects. + * + *

{@code contains(x)}, {@code add(x)} and {@code remove(x)}, are all (expected and amortized) + * constant time operations. Expected in the hashtable sense (depends on the hash function doing a + * good job of distributing the elements to the buckets to a distribution not far from uniform), and + * amortized since some operations can trigger a hash table resize. + * + *

Unlike {@code java.util.HashSet}, iteration is only proportional to the actual {@code size()}, + * which is optimal, and not the size of the internal hashtable, which could be much larger + * than {@code size()}. Furthermore, this structure only depends on a fixed number of arrays; {@code + * add(x)} operations do not create objects for the garbage collector to deal with, and for + * every element added, the garbage collector will have to traverse {@code 1.5} references on + * average, in the marking phase, not {@code 5.0} as in {@code java.util.HashSet}. + * + *

If there are no removals, then {@link #iterator iteration} order is the same as insertion + * order. Any removal invalidates any ordering guarantees. + * + *

This class should not be assumed to be universally superior to {@code java.util.HashSet}. + * Generally speaking, this class reduces object allocation and memory consumption at the price of + * moderately increased constant factors of CPU. Only use this class when there is a specific reason + * to prioritize memory over CPU. + * + * @author Dimitris Andreou + * @author Jon Noack + */ +@GwtIncompatible // not worth using in GWT for now +@ElementTypesAreNonnullByDefault +class CompactHashSet extends AbstractSet implements Serializable { + // TODO(user): cache all field accesses in local vars + + /** Creates an empty {@code CompactHashSet} instance. */ + public static CompactHashSet create() { + return new CompactHashSet<>(); + } + + /** + * Creates a mutable {@code CompactHashSet} instance containing the elements of the given + * collection in unspecified order. + * + * @param collection the elements that the set should contain + * @return a new {@code CompactHashSet} containing those elements (minus duplicates) + */ + public static CompactHashSet create(Collection collection) { + CompactHashSet set = createWithExpectedSize(collection.size()); + set.addAll(collection); + return set; + } + + /** + * Creates a mutable {@code CompactHashSet} instance containing the given elements in + * unspecified order. + * + * @param elements the elements that the set should contain + * @return a new {@code CompactHashSet} containing those elements (minus duplicates) + */ + @SafeVarargs + public static CompactHashSet create(E... elements) { + CompactHashSet set = createWithExpectedSize(elements.length); + Collections.addAll(set, elements); + return set; + } + + /** + * Creates a {@code CompactHashSet} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without growth. + * + * @param expectedSize the number of elements you expect to add to the returned set + * @return a new, empty {@code CompactHashSet} with enough capacity to hold {@code expectedSize} + * elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static CompactHashSet createWithExpectedSize(int expectedSize) { + return new CompactHashSet<>(expectedSize); + } + + /** + * Maximum allowed false positive probability of detecting a hash flooding attack given random + * input. + */ + @VisibleForTesting() static final double HASH_FLOODING_FPP = 0.001; + + /** + * Maximum allowed length of a hash table bucket before falling back to a j.u.LinkedHashSet based + * implementation. Experimentally determined. + */ + private static final int MAX_HASH_BUCKET_LENGTH = 9; + + // See CompactHashMap for a detailed description of how the following fields work. That + // description talks about `keys`, `values`, and `entries`; here the `keys` and `values` arrays + // are replaced by a single `elements` array but everything else works similarly. + + /** + * The hashtable object. This can be either: + * + *

    + *
  • a byte[], short[], or int[], with size a power of two, created by + * CompactHashing.createTable, whose values are either + *
      + *
    • UNSET, meaning "null pointer" + *
    • one plus an index into the entries and elements array + *
    + *
  • another java.util.Set delegate implementation. In most modern JDKs, normal java.util hash + * collections intelligently fall back to a binary search tree if hash table collisions are + * detected. Rather than going to all the trouble of reimplementing this ourselves, we + * simply switch over to use the JDK implementation wholesale if probable hash flooding is + * detected, sacrificing the compactness guarantee in very rare cases in exchange for much + * more reliable worst-case behavior. + *
  • null, if no entries have yet been added to the map + *
+ */ + private transient Object table; + + /** + * Contains the logical entries, in the range of [0, size()). The high bits of each int are the + * part of the smeared hash of the element not covered by the hashtable mask, whereas the low bits + * are the "next" pointer (pointing to the next entry in the bucket chain), which will always be + * less than or equal to the hashtable mask. + * + *
+   * hash  = aaaaaaaa
+   * mask  = 00000fff
+   * next  = 00000bbb
+   * entry = aaaaabbb
+   * 
+ * + *

The pointers in [size(), entries.length) are all "null" (UNSET). + */ + private transient int[] entries; + + /** + * The elements contained in the set, in the range of [0, size()). The elements in [size(), + * elements.length) are all {@code null}. + */ + @VisibleForTesting transient Object[] elements; + + /** + * Keeps track of metadata like the number of hash table bits and modifications of this data + * structure (to make it possible to throw ConcurrentModificationException in the iterator). Note + * that we choose not to make this volatile, so we do less of a "best effort" to track such + * errors, for better performance. + */ + private transient int metadata; + + /** The number of elements contained in the set. */ + private transient int size; + + /** Constructs a new empty instance of {@code CompactHashSet}. */ + CompactHashSet() { + init(CompactHashing.DEFAULT_SIZE); + } + + /** + * Constructs a new instance of {@code CompactHashSet} with the specified capacity. + * + * @param expectedSize the initial capacity of this {@code CompactHashSet}. + */ + CompactHashSet(int expectedSize) { + init(expectedSize); + } + + /** Pseudoconstructor for serialization support. */ + void init(int expectedSize) { + Preconditions.checkArgument(expectedSize >= 0, "Expected size must be >= 0"); + + // Save expectedSize for use in allocArrays() + this.metadata = Ints.constrainToRange(expectedSize, 1, CompactHashing.MAX_SIZE); + } + + /** Returns whether arrays need to be allocated. */ + @VisibleForTesting + boolean needsAllocArrays() { + return table == null; + } + + /** Handle lazy allocation of arrays. */ + int allocArrays() { + Preconditions.checkState(needsAllocArrays(), "Arrays already allocated"); + + int expectedSize = metadata; + int buckets = CompactHashing.tableSize(expectedSize); + this.table = CompactHashing.createTable(buckets); + setHashTableMask(buckets - 1); + + this.entries = new int[expectedSize]; + this.elements = new Object[expectedSize]; + + return expectedSize; + } + + @SuppressWarnings("unchecked") + @VisibleForTesting + Set delegateOrNull() { + if (table instanceof Set) { + return (Set) table; + } + return null; + } + + private Set createHashFloodingResistantDelegate(int tableSize) { + return new LinkedHashSet<>(tableSize, 1.0f); + } + + @VisibleForTesting + Set convertToHashFloodingResistantImplementation() { + Set newDelegate = createHashFloodingResistantDelegate(hashTableMask() + 1); + for (int i = firstEntryIndex(); i >= 0; i = getSuccessor(i)) { + newDelegate.add(element(i)); + } + this.table = newDelegate; + this.entries = null; + this.elements = null; + incrementModCount(); + return newDelegate; + } + + @VisibleForTesting + boolean isUsingHashFloodingResistance() { + return delegateOrNull() != null; + } + + /** Stores the hash table mask as the number of bits needed to represent an index. */ + private void setHashTableMask(int mask) { + int hashTableBits = Integer.SIZE - Integer.numberOfLeadingZeros(mask); + metadata = + CompactHashing.maskCombine(metadata, hashTableBits, CompactHashing.HASH_TABLE_BITS_MASK); + } + + /** Gets the hash table mask using the stored number of hash table bits. */ + private int hashTableMask() { + return (1 << (metadata & CompactHashing.HASH_TABLE_BITS_MASK)) - 1; + } + + void incrementModCount() { + metadata += CompactHashing.MODIFICATION_COUNT_INCREMENT; + } + + @Override + public boolean add(@ParametricNullness E object) { + if (needsAllocArrays()) { + allocArrays(); + } + Set delegate = delegateOrNull(); + if (delegate != null) { + return delegate.add(object); + } + int[] entries = requireEntries(); + Object[] elements = requireElements(); + + int newEntryIndex = this.size; // current size, and pointer to the entry to be appended + int newSize = newEntryIndex + 1; + int hash = smearedHash(object); + int mask = hashTableMask(); + int tableIndex = hash & mask; + int next = CompactHashing.tableGet(requireTable(), tableIndex); + if (next == UNSET) { // uninitialized bucket + if (newSize > mask) { + // Resize and add new entry + mask = resizeTable(mask, CompactHashing.newCapacity(mask), hash, newEntryIndex); + } else { + CompactHashing.tableSet(requireTable(), tableIndex, newEntryIndex + 1); + } + } else { + int entryIndex; + int entry; + int hashPrefix = CompactHashing.getHashPrefix(hash, mask); + int bucketLength = 0; + do { + entryIndex = next - 1; + entry = entries[entryIndex]; + if (CompactHashing.getHashPrefix(entry, mask) == hashPrefix + && Objects.equal(object, elements[entryIndex])) { + return false; + } + next = CompactHashing.getNext(entry, mask); + bucketLength++; + } while (next != UNSET); + + if (bucketLength >= MAX_HASH_BUCKET_LENGTH) { + return convertToHashFloodingResistantImplementation().add(object); + } + + if (newSize > mask) { + // Resize and add new entry + mask = resizeTable(mask, CompactHashing.newCapacity(mask), hash, newEntryIndex); + } else { + entries[entryIndex] = CompactHashing.maskCombine(entry, newEntryIndex + 1, mask); + } + } + resizeMeMaybe(newSize); + insertEntry(newEntryIndex, object, hash, mask); + this.size = newSize; + incrementModCount(); + return true; + } + + /** + * Creates a fresh entry with the specified object at the specified position in the entry arrays. + */ + void insertEntry(int entryIndex, @ParametricNullness E object, int hash, int mask) { + setEntry(entryIndex, CompactHashing.maskCombine(hash, UNSET, mask)); + setElement(entryIndex, object); + } + + /** Resizes the entries storage if necessary. */ + private void resizeMeMaybe(int newSize) { + int entriesSize = requireEntries().length; + if (newSize > entriesSize) { + // 1.5x but round up to nearest odd (this is optimal for memory consumption on Android) + int newCapacity = + Math.min(CompactHashing.MAX_SIZE, (entriesSize + Math.max(1, entriesSize >>> 1)) | 1); + if (newCapacity != entriesSize) { + resizeEntries(newCapacity); + } + } + } + + /** + * Resizes the internal entries array to the specified capacity, which may be greater or less than + * the current capacity. + */ + void resizeEntries(int newCapacity) { + this.entries = Arrays.copyOf(requireEntries(), newCapacity); + this.elements = Arrays.copyOf(requireElements(), newCapacity); + } + + private int resizeTable(int oldMask, int newCapacity, int targetHash, int targetEntryIndex) { + Object newTable = CompactHashing.createTable(newCapacity); + int newMask = newCapacity - 1; + + if (targetEntryIndex != UNSET) { + // Add target first; it must be last in the chain because its entry hasn't yet been created + CompactHashing.tableSet(newTable, targetHash & newMask, targetEntryIndex + 1); + } + + Object oldTable = requireTable(); + int[] entries = requireEntries(); + + // Loop over current hashtable + for (int oldTableIndex = 0; oldTableIndex <= oldMask; oldTableIndex++) { + int oldNext = CompactHashing.tableGet(oldTable, oldTableIndex); + while (oldNext != UNSET) { + int entryIndex = oldNext - 1; + int oldEntry = entries[entryIndex]; + + // Rebuild hash using entry hashPrefix and tableIndex ("hashSuffix") + int hash = CompactHashing.getHashPrefix(oldEntry, oldMask) | oldTableIndex; + + int newTableIndex = hash & newMask; + int newNext = CompactHashing.tableGet(newTable, newTableIndex); + CompactHashing.tableSet(newTable, newTableIndex, oldNext); + entries[entryIndex] = CompactHashing.maskCombine(hash, newNext, newMask); + + oldNext = CompactHashing.getNext(oldEntry, oldMask); + } + } + + this.table = newTable; + setHashTableMask(newMask); + return newMask; + } + + @Override + public boolean contains(Object object) { + if (needsAllocArrays()) { + return false; + } + Set delegate = delegateOrNull(); + if (delegate != null) { + return delegate.contains(object); + } + int hash = smearedHash(object); + int mask = hashTableMask(); + int next = CompactHashing.tableGet(requireTable(), hash & mask); + if (next == UNSET) { + return false; + } + int hashPrefix = CompactHashing.getHashPrefix(hash, mask); + do { + int entryIndex = next - 1; + int entry = entry(entryIndex); + if (CompactHashing.getHashPrefix(entry, mask) == hashPrefix + && Objects.equal(object, element(entryIndex))) { + return true; + } + next = CompactHashing.getNext(entry, mask); + } while (next != UNSET); + return false; + } + + @Override + public boolean remove(Object object) { + if (needsAllocArrays()) { + return false; + } + Set delegate = delegateOrNull(); + if (delegate != null) { + return delegate.remove(object); + } + int mask = hashTableMask(); + int index = + CompactHashing.remove( + object, + /* value= */ null, + mask, + requireTable(), + requireEntries(), + requireElements(), + /* values= */ null); + if (index == -1) { + return false; + } + + moveLastEntry(index, mask); + size--; + incrementModCount(); + + return true; + } + + /** + * Moves the last entry in the entry array into {@code dstIndex}, and nulls out its old position. + */ + void moveLastEntry(int dstIndex, int mask) { + Object table = requireTable(); + int[] entries = requireEntries(); + Object[] elements = requireElements(); + int srcIndex = size() - 1; + if (dstIndex < srcIndex) { + // move last entry to deleted spot + Object object = elements[srcIndex]; + elements[dstIndex] = object; + elements[srcIndex] = null; + + // move the last entry to the removed spot, just like we moved the element + entries[dstIndex] = entries[srcIndex]; + entries[srcIndex] = 0; + + // also need to update whoever's "next" pointer was pointing to the last entry place + int tableIndex = smearedHash(object) & mask; + int next = CompactHashing.tableGet(table, tableIndex); + int srcNext = srcIndex + 1; + if (next == srcNext) { + // we need to update the root pointer + CompactHashing.tableSet(table, tableIndex, dstIndex + 1); + } else { + // we need to update a pointer in an entry + int entryIndex; + int entry; + do { + entryIndex = next - 1; + entry = entries[entryIndex]; + next = CompactHashing.getNext(entry, mask); + } while (next != srcNext); + // here, entries[entryIndex] points to the old entry location; update it + entries[entryIndex] = CompactHashing.maskCombine(entry, dstIndex + 1, mask); + } + } else { + elements[dstIndex] = null; + entries[dstIndex] = 0; + } + } + + int firstEntryIndex() { + return isEmpty() ? -1 : 0; + } + + int getSuccessor(int entryIndex) { + return (entryIndex + 1 < size) ? entryIndex + 1 : -1; + } + + /** + * Updates the index an iterator is pointing to after a call to remove: returns the index of the + * entry that should be looked at after a removal on indexRemoved, with indexBeforeRemove as the + * index that *was* the next entry that would be looked at. + */ + int adjustAfterRemove(int indexBeforeRemove, @SuppressWarnings("unused") int indexRemoved) { + return indexBeforeRemove - 1; + } + + @Override + public Iterator iterator() { + Set delegate = delegateOrNull(); + if (delegate != null) { + return delegate.iterator(); + } + return new Iterator() { + int expectedMetadata = metadata; + int currentIndex = firstEntryIndex(); + int indexToRemove = -1; + + @Override + public boolean hasNext() { + return currentIndex >= 0; + } + + @Override + @ParametricNullness + public E next() { + checkForConcurrentModification(); + if (!hasNext()) { + throw new NoSuchElementException(); + } + indexToRemove = currentIndex; + E result = element(currentIndex); + currentIndex = getSuccessor(currentIndex); + return result; + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkRemove(indexToRemove >= 0); + incrementExpectedModCount(); + CompactHashSet.this.remove(element(indexToRemove)); + currentIndex = adjustAfterRemove(currentIndex, indexToRemove); + indexToRemove = -1; + } + + void incrementExpectedModCount() { + expectedMetadata += CompactHashing.MODIFICATION_COUNT_INCREMENT; + } + + private void checkForConcurrentModification() { + if (metadata != expectedMetadata) { + throw new ConcurrentModificationException(); + } + } + }; + } + + @Override + public Spliterator spliterator() { + if (needsAllocArrays()) { + return Spliterators.spliterator(new Object[0], Spliterator.DISTINCT | Spliterator.ORDERED); + } + Set delegate = delegateOrNull(); + return (delegate != null) + ? delegate.spliterator() + : Spliterators.spliterator( + requireElements(), 0, size, Spliterator.DISTINCT | Spliterator.ORDERED); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + Set delegate = delegateOrNull(); + if (delegate != null) { + delegate.forEach(action); + } else { + for (int i = firstEntryIndex(); i >= 0; i = getSuccessor(i)) { + action.accept(element(i)); + } + } + } + + @Override + public int size() { + Set delegate = delegateOrNull(); + return (delegate != null) ? delegate.size() : size; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public Object[] toArray() { + if (needsAllocArrays()) { + return new Object[0]; + } + Set delegate = delegateOrNull(); + return (delegate != null) ? delegate.toArray() : Arrays.copyOf(requireElements(), size); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + if (needsAllocArrays()) { + if (a.length > 0) { + a[0] = null; + } + return a; + } + Set delegate = delegateOrNull(); + return (delegate != null) + ? delegate.toArray(a) + : ObjectArrays.toArrayImpl(requireElements(), 0, size, a); + } + + /** + * Ensures that this {@code CompactHashSet} has the smallest representation in memory, given its + * current size. + */ + public void trimToSize() { + if (needsAllocArrays()) { + return; + } + Set delegate = delegateOrNull(); + if (delegate != null) { + Set newDelegate = createHashFloodingResistantDelegate(size()); + newDelegate.addAll(delegate); + this.table = newDelegate; + return; + } + int size = this.size; + if (size < requireEntries().length) { + resizeEntries(size); + } + int minimumTableSize = CompactHashing.tableSize(size); + int mask = hashTableMask(); + if (minimumTableSize < mask) { // smaller table size will always be less than current mask + resizeTable(mask, minimumTableSize, UNSET, UNSET); + } + } + + @Override + public void clear() { + if (needsAllocArrays()) { + return; + } + incrementModCount(); + Set delegate = delegateOrNull(); + if (delegate != null) { + metadata = + Ints.constrainToRange(size(), CompactHashing.DEFAULT_SIZE, CompactHashing.MAX_SIZE); + delegate.clear(); // invalidate any iterators left over! + table = null; + size = 0; + } else { + Arrays.fill(requireElements(), 0, size, null); + CompactHashing.tableClear(requireTable()); + Arrays.fill(requireEntries(), 0, size, 0); + this.size = 0; + } + } + + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(size()); + for (E e : this) { + stream.writeObject(e); + } + } + + @SuppressWarnings("unchecked") + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int elementCount = stream.readInt(); + if (elementCount < 0) { + throw new InvalidObjectException("Invalid size: " + elementCount); + } + init(elementCount); + for (int i = 0; i < elementCount; i++) { + E element = (E) stream.readObject(); + add(element); + } + } + + /* + * For discussion of the safety of the following methods, see the comments near the end of + * CompactHashMap. + */ + + private Object requireTable() { + return requireNonNull(table); + } + + private int[] requireEntries() { + return requireNonNull(entries); + } + + private Object[] requireElements() { + return requireNonNull(elements); + } + + @SuppressWarnings("unchecked") + private E element(int i) { + return (E) requireElements()[i]; + } + + private int entry(int i) { + return requireEntries()[i]; + } + + private void setElement(int i, E value) { + requireElements()[i] = value; + } + + private void setEntry(int i, int value) { + requireEntries()[i] = value; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashing.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashing.java new file mode 100644 index 0000000000000..45192526e96a9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactHashing.java @@ -0,0 +1,199 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.Arrays; + +/** + * Helper classes and static methods for implementing compact hash-based collections. + * + * @author Jon Noack + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class CompactHashing { + private CompactHashing() {} + + /** Indicates blank table entries. */ + static final byte UNSET = 0; + + /** Number of bits used to store the numbers of hash table bits (max 30). */ + private static final int HASH_TABLE_BITS_MAX_BITS = 5; + + /** Use high bits of metadata for modification count. */ + static final int MODIFICATION_COUNT_INCREMENT = (1 << HASH_TABLE_BITS_MAX_BITS); + + /** Bitmask that selects the low bits of metadata to get hashTableBits. */ + static final int HASH_TABLE_BITS_MASK = (1 << HASH_TABLE_BITS_MAX_BITS) - 1; + + /** Maximum size of a compact hash-based collection (2^30 - 1 because 0 is UNSET). */ + static final int MAX_SIZE = Ints.MAX_POWER_OF_TWO - 1; + + /** Default size of a compact hash-based collection. */ + static final int DEFAULT_SIZE = 3; + + /** + * Minimum size of the hash table of a compact hash-based collection. Because small hash tables + * use a byte[], any smaller size uses the same amount of memory due to object padding. + */ + private static final int MIN_HASH_TABLE_SIZE = 4; + + private static final int BYTE_MAX_SIZE = 1 << Byte.SIZE; // 2^8 = 256 + private static final int BYTE_MASK = (1 << Byte.SIZE) - 1; // 2^8 - 1 = 255 + + private static final int SHORT_MAX_SIZE = 1 << Short.SIZE; // 2^16 = 65_536 + private static final int SHORT_MASK = (1 << Short.SIZE) - 1; // 2^16 - 1 = 65_535 + + /** + * Returns the power of 2 hashtable size required to hold the expected number of items or the + * minimum hashtable size, whichever is greater. + */ + static int tableSize(int expectedSize) { + // We use entries next == 0 to indicate UNSET, so actual capacity is 1 less than requested. + return Math.max(MIN_HASH_TABLE_SIZE, Hashing.closedTableSize(expectedSize + 1, 1.0f)); + } + + /** Creates and returns a properly-sized array with the given number of buckets. */ + static Object createTable(int buckets) { + if (buckets < 2 + || buckets > Ints.MAX_POWER_OF_TWO + || Integer.highestOneBit(buckets) != buckets) { + throw new IllegalArgumentException("must be power of 2 between 2^1 and 2^30: " + buckets); + } + if (buckets <= BYTE_MAX_SIZE) { + return new byte[buckets]; + } else if (buckets <= SHORT_MAX_SIZE) { + return new short[buckets]; + } else { + return new int[buckets]; + } + } + + static void tableClear(Object table) { + if (table instanceof byte[]) { + Arrays.fill((byte[]) table, (byte) 0); + } else if (table instanceof short[]) { + Arrays.fill((short[]) table, (short) 0); + } else { + Arrays.fill((int[]) table, 0); + } + } + + /** + * Returns {@code table[index]}, where {@code table} is actually a {@code byte[]}, {@code + * short[]}, or {@code int[]}. When it is a {@code byte[]} or {@code short[]}, the returned value + * is unsigned, so the range of possible returned values is 0–255 or 0–65535, respectively. + */ + static int tableGet(Object table, int index) { + if (table instanceof byte[]) { + return ((byte[]) table)[index] & BYTE_MASK; // unsigned read + } else if (table instanceof short[]) { + return ((short[]) table)[index] & SHORT_MASK; // unsigned read + } else { + return ((int[]) table)[index]; + } + } + + /** + * Sets {@code table[index]} to {@code entry}, where {@code table} is actually a {@code byte[]}, + * {@code short[]}, or {@code int[]}. The value of {@code entry} should fit in the size of the + * assigned array element, when seen as an unsigned value. So if {@code table} is a {@code byte[]} + * then we should have {@code 0 ≤ entry ≤ 255}, and if {@code table} is a {@code short[]} then we + * should have {@code 0 ≤ entry ≤ 65535}. It is the caller's responsibility to ensure this. + */ + static void tableSet(Object table, int index, int entry) { + if (table instanceof byte[]) { + ((byte[]) table)[index] = (byte) entry; // unsigned write + } else if (table instanceof short[]) { + ((short[]) table)[index] = (short) entry; // unsigned write + } else { + ((int[]) table)[index] = entry; + } + } + + /** + * Returns a larger power of 2 hashtable size given the current mask. + * + *

For hashtable sizes less than or equal to 32, the returned power of 2 is 4x the current + * hashtable size to reduce expensive rehashing. Otherwise the returned power of 2 is 2x the + * current hashtable size. + */ + static int newCapacity(int mask) { + return ((mask < 32) ? 4 : 2) * (mask + 1); + } + + /** Returns the hash prefix given the current mask. */ + static int getHashPrefix(int value, int mask) { + return value & ~mask; + } + + /** Returns the index, or 0 if the entry is "null". */ + static int getNext(int entry, int mask) { + return entry & mask; + } + + /** Returns a new value combining the prefix and suffix using the given mask. */ + static int maskCombine(int prefix, int suffix, int mask) { + return (prefix & ~mask) | (suffix & mask); + } + + static int remove( + Object key, + Object value, + int mask, + Object table, + int[] entries, + Object[] keys, + Object[] values) { + int hash = Hashing.smearedHash(key); + int tableIndex = hash & mask; + int next = tableGet(table, tableIndex); + if (next == UNSET) { + return -1; + } + int hashPrefix = getHashPrefix(hash, mask); + int lastEntryIndex = -1; + do { + int entryIndex = next - 1; + int entry = entries[entryIndex]; + if (getHashPrefix(entry, mask) == hashPrefix + && Objects.equal(key, keys[entryIndex]) + && (values == null || Objects.equal(value, values[entryIndex]))) { + int newNext = getNext(entry, mask); + if (lastEntryIndex == -1) { + // we need to update the root link from table[] + tableSet(table, tableIndex, newNext); + } else { + // we need to update the link from the chain + entries[lastEntryIndex] = maskCombine(entries[lastEntryIndex], newNext, mask); + } + + return entryIndex; + } + lastEntryIndex = entryIndex; + next = getNext(entry, mask); + } while (next != UNSET); + return -1; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactLinkedHashMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactLinkedHashMap.java new file mode 100644 index 0000000000000..da016fe7dafbd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactLinkedHashMap.java @@ -0,0 +1,319 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; + +import static java.util.Objects.requireNonNull; + +/** + * CompactLinkedHashMap is an implementation of a Map with insertion or LRU iteration order, + * maintained with a doubly linked list through the entries. All optional operations (put and + * remove) are supported. Null keys and values are supported. + * + *

{@code containsKey(k)}, {@code put(k, v)} and {@code remove(k)} are all (expected and + * amortized) constant time operations. Expected in the hashtable sense (depends on the hash + * function doing a good job of distributing the elements to the buckets to a distribution not far + * from uniform), and amortized since some operations can trigger a hash table resize. + * + *

As compared with {@link java.util.LinkedHashMap}, this structure places significantly reduced + * load on the garbage collector by only using a constant number of internal objects. + * + *

This class should not be assumed to be universally superior to {@code + * java.util.LinkedHashMap}. Generally speaking, this class reduces object allocation and memory + * consumption at the price of moderately increased constant factors of CPU. Only use this class + * when there is a specific reason to prioritize memory over CPU. + * + * @author Louis Wasserman + */ +@J2ktIncompatible // no support for access-order mode in LinkedHashMap delegate +@GwtIncompatible // not worth using in GWT for now +@ElementTypesAreNonnullByDefault +class CompactLinkedHashMap extends CompactHashMap { + // TODO(lowasser): implement removeEldestEntry so this can be used as a drop-in replacement + + /** Creates an empty {@code CompactLinkedHashMap} instance. */ + public static CompactLinkedHashMap create() { + return new CompactLinkedHashMap<>(); + } + + /** + * Creates a {@code CompactLinkedHashMap} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without rebuilding internal data structures. + * + * @param expectedSize the number of elements you expect to add to the returned set + * @return a new, empty {@code CompactLinkedHashMap} with enough capacity to hold {@code + * expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static + CompactLinkedHashMap createWithExpectedSize(int expectedSize) { + return new CompactLinkedHashMap<>(expectedSize); + } + + private static final int ENDPOINT = -2; + + /** + * Contains the link pointers corresponding with the entries, in the range of [0, size()). The + * high 32 bits of each long is the "prev" pointer, whereas the low 32 bits is the "succ" pointer + * (pointing to the next entry in the linked list). The pointers in [size(), entries.length) are + * all "null" (UNSET). + * + *

A node with "prev" pointer equal to {@code ENDPOINT} is the first node in the linked list, + * and a node with "next" pointer equal to {@code ENDPOINT} is the last node. + */ + @VisibleForTesting transient long[] links; + + /** Pointer to the first node in the linked list, or {@code ENDPOINT} if there are no entries. */ + private transient int firstEntry; + + /** Pointer to the last node in the linked list, or {@code ENDPOINT} if there are no entries. */ + private transient int lastEntry; + + private final boolean accessOrder; + + CompactLinkedHashMap() { + this(CompactHashing.DEFAULT_SIZE); + } + + CompactLinkedHashMap(int expectedSize) { + this(expectedSize, false); + } + + CompactLinkedHashMap(int expectedSize, boolean accessOrder) { + super(expectedSize); + this.accessOrder = accessOrder; + } + + @Override + void init(int expectedSize) { + super.init(expectedSize); + this.firstEntry = ENDPOINT; + this.lastEntry = ENDPOINT; + } + + @Override + int allocArrays() { + int expectedSize = super.allocArrays(); + this.links = new long[expectedSize]; + return expectedSize; + } + + @Override + Map createHashFloodingResistantDelegate(int tableSize) { + return new LinkedHashMap(tableSize, 1.0f, accessOrder); + } + + @Override + Map convertToHashFloodingResistantImplementation() { + Map result = super.convertToHashFloodingResistantImplementation(); + links = null; + return result; + } + + /* + * For discussion of the safety of the following methods for operating on predecessors and + * successors, see the comments near the end of CompactHashMap, noting that the methods here call + * link(), which is defined at the end of this file. + */ + + private int getPredecessor(int entry) { + return ((int) (link(entry) >>> 32)) - 1; + } + + @Override + int getSuccessor(int entry) { + return ((int) link(entry)) - 1; + } + + private void setSuccessor(int entry, int succ) { + long succMask = (~0L) >>> 32; + setLink(entry, (link(entry) & ~succMask) | ((succ + 1) & succMask)); + } + + private void setPredecessor(int entry, int pred) { + long predMask = ~0L << 32; + setLink(entry, (link(entry) & ~predMask) | ((long) (pred + 1) << 32)); + } + + private void setSucceeds(int pred, int succ) { + if (pred == ENDPOINT) { + firstEntry = succ; + } else { + setSuccessor(pred, succ); + } + + if (succ == ENDPOINT) { + lastEntry = pred; + } else { + setPredecessor(succ, pred); + } + } + + @Override + void insertEntry( + int entryIndex, @ParametricNullness K key, @ParametricNullness V value, int hash, int mask) { + super.insertEntry(entryIndex, key, value, hash, mask); + setSucceeds(lastEntry, entryIndex); + setSucceeds(entryIndex, ENDPOINT); + } + + @Override + void accessEntry(int index) { + if (accessOrder) { + // delete from previous position... + setSucceeds(getPredecessor(index), getSuccessor(index)); + // ...and insert at the end. + setSucceeds(lastEntry, index); + setSucceeds(index, ENDPOINT); + incrementModCount(); + } + } + + @Override + void moveLastEntry(int dstIndex, int mask) { + int srcIndex = size() - 1; + super.moveLastEntry(dstIndex, mask); + + setSucceeds(getPredecessor(dstIndex), getSuccessor(dstIndex)); + if (dstIndex < srcIndex) { + setSucceeds(getPredecessor(srcIndex), dstIndex); + setSucceeds(dstIndex, getSuccessor(srcIndex)); + } + setLink(srcIndex, 0); + } + + @Override + void resizeEntries(int newCapacity) { + super.resizeEntries(newCapacity); + links = Arrays.copyOf(requireLinks(), newCapacity); + } + + @Override + int firstEntryIndex() { + return firstEntry; + } + + @Override + int adjustAfterRemove(int indexBeforeRemove, int indexRemoved) { + return (indexBeforeRemove >= size()) ? indexRemoved : indexBeforeRemove; + } + + @Override + Set> createEntrySet() { + class EntrySetImpl extends EntrySetView { + @Override + public Spliterator> spliterator() { + return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT); + } + } + return new EntrySetImpl(); + } + + @Override + Set createKeySet() { + class KeySetImpl extends KeySetView { + @Override + public Object[] toArray() { + return ObjectArrays.toArrayImpl(this); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + return ObjectArrays.toArrayImpl(this, a); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT); + } + } + return new KeySetImpl(); + } + + @Override + Collection createValues() { + class ValuesImpl extends ValuesView { + @Override + public Object[] toArray() { + return ObjectArrays.toArrayImpl(this); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + return ObjectArrays.toArrayImpl(this, a); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.ORDERED); + } + } + return new ValuesImpl(); + } + + @Override + public void clear() { + if (needsAllocArrays()) { + return; + } + this.firstEntry = ENDPOINT; + this.lastEntry = ENDPOINT; + if (links != null) { + Arrays.fill(links, 0, size(), 0); + } + super.clear(); + } + + /* + * For discussion of the safety of the following methods, see the comments near the end of + * CompactHashMap. + */ + + private long[] requireLinks() { + return requireNonNull(links); + } + + private long link(int i) { + return requireLinks()[i]; + } + + private void setLink(int i, long value) { + requireLinks()[i] = value; + } + + /* + * We don't define getPredecessor+getSuccessor and setPredecessor+setSuccessor here because + * they're defined above -- including logic to add and subtract 1 to map between the values stored + * in the predecessor/successor arrays and the indexes in the elements array that they identify. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactLinkedHashSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactLinkedHashSet.java new file mode 100644 index 0000000000000..2a3497f62c887 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompactLinkedHashSet.java @@ -0,0 +1,284 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; + +import static java.util.Objects.requireNonNull; + +/** + * CompactLinkedHashSet is an implementation of a Set, which a predictable iteration order that + * matches the insertion order. All optional operations (adding and removing) are supported. All + * elements, including {@code null}, are permitted. + * + *

{@code contains(x)}, {@code add(x)} and {@code remove(x)}, are all (expected and amortized) + * constant time operations. Expected in the hashtable sense (depends on the hash function doing a + * good job of distributing the elements to the buckets to a distribution not far from uniform), and + * amortized since some operations can trigger a hash table resize. + * + *

This implementation consumes significantly less memory than {@code java.util.LinkedHashSet} or + * even {@code java.util.HashSet}, and places considerably less load on the garbage collector. Like + * {@code java.util.LinkedHashSet}, it offers insertion-order iteration, with identical behavior. + * + *

This class should not be assumed to be universally superior to {@code + * java.util.LinkedHashSet}. Generally speaking, this class reduces object allocation and memory + * consumption at the price of moderately increased constant factors of CPU. Only use this class + * when there is a specific reason to prioritize memory over CPU. + * + * @author Louis Wasserman + */ +@GwtIncompatible // not worth using in GWT for now +@ElementTypesAreNonnullByDefault +class CompactLinkedHashSet extends CompactHashSet { + + /** Creates an empty {@code CompactLinkedHashSet} instance. */ + public static CompactLinkedHashSet create() { + return new CompactLinkedHashSet<>(); + } + + /** + * Creates a mutable {@code CompactLinkedHashSet} instance containing the elements of the + * given collection in the order returned by the collection's iterator. + * + * @param collection the elements that the set should contain + * @return a new {@code CompactLinkedHashSet} containing those elements (minus duplicates) + */ + public static CompactLinkedHashSet create( + Collection collection) { + CompactLinkedHashSet set = createWithExpectedSize(collection.size()); + set.addAll(collection); + return set; + } + + /** + * Creates a {@code CompactLinkedHashSet} instance containing the given elements in unspecified + * order. + * + * @param elements the elements that the set should contain + * @return a new {@code CompactLinkedHashSet} containing those elements (minus duplicates) + */ + @SafeVarargs + public static CompactLinkedHashSet create(E... elements) { + CompactLinkedHashSet set = createWithExpectedSize(elements.length); + Collections.addAll(set, elements); + return set; + } + + /** + * Creates a {@code CompactLinkedHashSet} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without rebuilding internal data structures. + * + * @param expectedSize the number of elements you expect to add to the returned set + * @return a new, empty {@code CompactLinkedHashSet} with enough capacity to hold {@code + * expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static CompactLinkedHashSet createWithExpectedSize( + int expectedSize) { + return new CompactLinkedHashSet<>(expectedSize); + } + + private static final int ENDPOINT = -2; + + // TODO(user): predecessors and successors should be collocated (reducing cache misses). + // Might also explore collocating all of [hash, next, predecessor, successor] fields of an + // entry in a *single* long[], though that reduces the maximum size of the set by a factor of 2 + + /** + * Pointer to the predecessor of an entry in insertion order. ENDPOINT indicates a node is the + * first node in insertion order; all values at indices ≥ {@link #size()} are UNSET. + */ + private transient int[] predecessor; + + /** + * Pointer to the successor of an entry in insertion order. ENDPOINT indicates a node is the last + * node in insertion order; all values at indices ≥ {@link #size()} are UNSET. + */ + private transient int[] successor; + + /** Pointer to the first node in the linked list, or {@code ENDPOINT} if there are no entries. */ + private transient int firstEntry; + + /** Pointer to the last node in the linked list, or {@code ENDPOINT} if there are no entries. */ + private transient int lastEntry; + + CompactLinkedHashSet() { + super(); + } + + CompactLinkedHashSet(int expectedSize) { + super(expectedSize); + } + + @Override + void init(int expectedSize) { + super.init(expectedSize); + this.firstEntry = ENDPOINT; + this.lastEntry = ENDPOINT; + } + + @Override + int allocArrays() { + int expectedSize = super.allocArrays(); + this.predecessor = new int[expectedSize]; + this.successor = new int[expectedSize]; + return expectedSize; + } + + @Override + Set convertToHashFloodingResistantImplementation() { + Set result = super.convertToHashFloodingResistantImplementation(); + this.predecessor = null; + this.successor = null; + return result; + } + + /* + * For discussion of the safety of the following methods for operating on predecessors and + * successors, see the comments near the end of CompactHashMap, noting that the methods here call + * requirePredecessors() and requireSuccessors(), which are defined at the end of this file. + */ + + private int getPredecessor(int entry) { + return requirePredecessors()[entry] - 1; + } + + @Override + int getSuccessor(int entry) { + return requireSuccessors()[entry] - 1; + } + + private void setSuccessor(int entry, int succ) { + requireSuccessors()[entry] = succ + 1; + } + + private void setPredecessor(int entry, int pred) { + requirePredecessors()[entry] = pred + 1; + } + + private void setSucceeds(int pred, int succ) { + if (pred == ENDPOINT) { + firstEntry = succ; + } else { + setSuccessor(pred, succ); + } + + if (succ == ENDPOINT) { + lastEntry = pred; + } else { + setPredecessor(succ, pred); + } + } + + @Override + void insertEntry(int entryIndex, @ParametricNullness E object, int hash, int mask) { + super.insertEntry(entryIndex, object, hash, mask); + setSucceeds(lastEntry, entryIndex); + setSucceeds(entryIndex, ENDPOINT); + } + + @Override + void moveLastEntry(int dstIndex, int mask) { + int srcIndex = size() - 1; + super.moveLastEntry(dstIndex, mask); + + setSucceeds(getPredecessor(dstIndex), getSuccessor(dstIndex)); + if (dstIndex < srcIndex) { + setSucceeds(getPredecessor(srcIndex), dstIndex); + setSucceeds(dstIndex, getSuccessor(srcIndex)); + } + requirePredecessors()[srcIndex] = 0; + requireSuccessors()[srcIndex] = 0; + } + + @Override + void resizeEntries(int newCapacity) { + super.resizeEntries(newCapacity); + predecessor = Arrays.copyOf(requirePredecessors(), newCapacity); + successor = Arrays.copyOf(requireSuccessors(), newCapacity); + } + + @Override + int firstEntryIndex() { + return firstEntry; + } + + @Override + int adjustAfterRemove(int indexBeforeRemove, int indexRemoved) { + return (indexBeforeRemove >= size()) ? indexRemoved : indexBeforeRemove; + } + + @Override + public Object[] toArray() { + return ObjectArrays.toArrayImpl(this); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + return ObjectArrays.toArrayImpl(this, a); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.DISTINCT); + } + + @Override + public void clear() { + if (needsAllocArrays()) { + return; + } + this.firstEntry = ENDPOINT; + this.lastEntry = ENDPOINT; + // Either both arrays are null or neither is, but we check both to satisfy the nullness checker. + if (predecessor != null && successor != null) { + Arrays.fill(predecessor, 0, size(), 0); + Arrays.fill(successor, 0, size(), 0); + } + super.clear(); + } + + /* + * For discussion of the safety of the following methods, see the comments near the end of + * CompactHashMap. + */ + + private int[] requirePredecessors() { + return requireNonNull(predecessor); + } + + private int[] requireSuccessors() { + return requireNonNull(successor); + } + + /* + * We don't define getPredecessor+getSuccessor and setPredecessor+setSuccessor here because + * they're defined above -- including logic to add and subtract 1 to map between the values stored + * in the predecessor/successor arrays and the indexes in the elements array that they identify. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComparatorOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComparatorOrdering.java new file mode 100644 index 0000000000000..7d10819ee8c02 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComparatorOrdering.java @@ -0,0 +1,67 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Comparator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** An ordering for a pre-existing comparator. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class ComparatorOrdering extends Ordering implements Serializable { + final Comparator comparator; + + ComparatorOrdering(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public int compare(@ParametricNullness T a, @ParametricNullness T b) { + return comparator.compare(a, b); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ComparatorOrdering) { + ComparatorOrdering that = (ComparatorOrdering) object; + return this.comparator.equals(that.comparator); + } + return false; + } + + @Override + public int hashCode() { + return comparator.hashCode(); + } + + @Override + public String toString() { + return comparator.toString(); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Comparators.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Comparators.java new file mode 100644 index 0000000000000..24cfc6c3a7d92 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Comparators.java @@ -0,0 +1,274 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Provides static methods for working with {@link Comparator} instances. For many other helpful + * comparator utilities, see either {@code Comparator} itself (for Java 8 or later), or {@code + * org.apache.iotdb.google.common.collect.Ordering} (otherwise). + * + *

Relationship to {@code Ordering}

+ * + *

In light of the significant enhancements to {@code Comparator} in Java 8, the overwhelming + * majority of usages of {@code Ordering} can be written using only built-in JDK APIs. This class is + * intended to "fill the gap" and provide those features of {@code Ordering} not already provided by + * the JDK. + * + * @since 21.0 + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Comparators { + private Comparators() {} + + /** + * Returns a new comparator which sorts iterables by comparing corresponding elements pairwise + * until a nonzero result is found; imposes "dictionary order." If the end of one iterable is + * reached, but not the other, the shorter iterable is considered to be less than the longer one. + * For example, a lexicographical natural ordering over integers considers {@code [] < [1] < [1, + * 1] < [1, 2] < [2]}. + * + *

Note that {@code Collections.reverseOrder(lexicographical(comparator))} is not equivalent to + * {@code lexicographical(Collections.reverseOrder(comparator))} (consider how each would order + * {@code [1]} and {@code [1, 1]}). + */ + // Note: 90% of the time we don't add type parameters or wildcards that serve only to "tweak" the + // desired return type. However, *nested* generics introduce a special class of problems that we + // think tip it over into being worthwhile. + public static Comparator> lexicographical( + Comparator comparator) { + return new LexicographicalOrdering(checkNotNull(comparator)); + } + + /** + * Returns {@code true} if each element in {@code iterable} after the first is greater than or + * equal to the element that preceded it, according to the specified comparator. Note that this is + * always true when the iterable has fewer than two elements. + */ + public static boolean isInOrder( + Iterable iterable, Comparator comparator) { + checkNotNull(comparator); + Iterator it = iterable.iterator(); + if (it.hasNext()) { + T prev = it.next(); + while (it.hasNext()) { + T next = it.next(); + if (comparator.compare(prev, next) > 0) { + return false; + } + prev = next; + } + } + return true; + } + + /** + * Returns {@code true} if each element in {@code iterable} after the first is strictly + * greater than the element that preceded it, according to the specified comparator. Note that + * this is always true when the iterable has fewer than two elements. + */ + public static boolean isInStrictOrder( + Iterable iterable, Comparator comparator) { + checkNotNull(comparator); + Iterator it = iterable.iterator(); + if (it.hasNext()) { + T prev = it.next(); + while (it.hasNext()) { + T next = it.next(); + if (comparator.compare(prev, next) >= 0) { + return false; + } + prev = next; + } + } + return true; + } + + /** + * Returns a {@code Collector} that returns the {@code k} smallest (relative to the specified + * {@code Comparator}) input elements, in ascending order, as an unmodifiable {@code List}. Ties + * are broken arbitrarily. + * + *

For example: + * + *

{@code
+   * Stream.of("foo", "quux", "banana", "elephant")
+   *     .collect(least(2, comparingInt(String::length)))
+   * // returns {"foo", "quux"}
+   * }
+ * + *

This {@code Collector} uses O(k) memory and takes expected time O(n) (worst-case O(n log + * k)), as opposed to e.g. {@code Stream.sorted(comparator).limit(k)}, which currently takes O(n + * log n) time and O(n) space. + * + * @throws IllegalArgumentException if {@code k < 0} + * @since 22.0 + */ + public static Collector> least( + int k, Comparator comparator) { + checkNonnegative(k, "k"); + checkNotNull(comparator); + return Collector.of( + () -> TopKSelector.least(k, comparator), + TopKSelector::offer, + TopKSelector::combine, + TopKSelector::topK, + Collector.Characteristics.UNORDERED); + } + + /** + * Returns a {@code Collector} that returns the {@code k} greatest (relative to the specified + * {@code Comparator}) input elements, in descending order, as an unmodifiable {@code List}. Ties + * are broken arbitrarily. + * + *

For example: + * + *

{@code
+   * Stream.of("foo", "quux", "banana", "elephant")
+   *     .collect(greatest(2, comparingInt(String::length)))
+   * // returns {"elephant", "banana"}
+   * }
+ * + *

This {@code Collector} uses O(k) memory and takes expected time O(n) (worst-case O(n log + * k)), as opposed to e.g. {@code Stream.sorted(comparator.reversed()).limit(k)}, which currently + * takes O(n log n) time and O(n) space. + * + * @throws IllegalArgumentException if {@code k < 0} + * @since 22.0 + */ + public static Collector> greatest( + int k, Comparator comparator) { + return least(k, comparator.reversed()); + } + + /** + * Returns a comparator of {@link Optional} values which treats {@link Optional#empty} as less + * than all other values, and orders the rest using {@code valueComparator} on the contained + * value. + * + * @since 22.0 + */ + public static Comparator> emptiesFirst(Comparator valueComparator) { + checkNotNull(valueComparator); + return Comparator., T>comparing( + o -> o.orElse(null), Comparator.nullsFirst(valueComparator)); + } + + /** + * Returns a comparator of {@link Optional} values which treats {@link Optional#empty} as greater + * than all other values, and orders the rest using {@code valueComparator} on the contained + * value. + * + * @since 22.0 + */ + public static Comparator> emptiesLast(Comparator valueComparator) { + checkNotNull(valueComparator); + return Comparator., T>comparing( + o -> o.orElse(null), Comparator.nullsLast(valueComparator)); + } + + /** + * Returns the minimum of the two values. If the values compare as 0, the first is returned. + * + *

The recommended solution for finding the {@code minimum} of some values depends on the type + * of your data and the number of elements you have. Read more in the Guava User Guide article on + * {@code + * Comparators}. + * + * @param a first value to compare, returned if less than or equal to b. + * @param b second value to compare. + * @throws ClassCastException if the parameters are not mutually comparable. + * @since 30.0 + */ + public static > T min(T a, T b) { + return (a.compareTo(b) <= 0) ? a : b; + } + + /** + * Returns the minimum of the two values, according to the given comparator. If the values compare + * as equal, the first is returned. + * + *

The recommended solution for finding the {@code minimum} of some values depends on the type + * of your data and the number of elements you have. Read more in the Guava User Guide article on + * {@code + * Comparators}. + * + * @param a first value to compare, returned if less than or equal to b + * @param b second value to compare. + * @throws ClassCastException if the parameters are not mutually comparable using the given + * comparator. + * @since 30.0 + */ + @ParametricNullness + public static T min( + @ParametricNullness T a, @ParametricNullness T b, Comparator comparator) { + return (comparator.compare(a, b) <= 0) ? a : b; + } + + /** + * Returns the maximum of the two values. If the values compare as 0, the first is returned. + * + *

The recommended solution for finding the {@code maximum} of some values depends on the type + * of your data and the number of elements you have. Read more in the Guava User Guide article on + * {@code + * Comparators}. + * + * @param a first value to compare, returned if greater than or equal to b. + * @param b second value to compare. + * @throws ClassCastException if the parameters are not mutually comparable. + * @since 30.0 + */ + public static > T max(T a, T b) { + return (a.compareTo(b) >= 0) ? a : b; + } + + /** + * Returns the maximum of the two values, according to the given comparator. If the values compare + * as equal, the first is returned. + * + *

The recommended solution for finding the {@code maximum} of some values depends on the type + * of your data and the number of elements you have. Read more in the Guava User Guide article on + * {@code + * Comparators}. + * + * @param a first value to compare, returned if greater than or equal to b. + * @param b second value to compare. + * @throws ClassCastException if the parameters are not mutually comparable using the given + * comparator. + * @since 30.0 + */ + @ParametricNullness + public static T max( + @ParametricNullness T a, @ParametricNullness T b, Comparator comparator) { + return (comparator.compare(a, b) >= 0) ? a : b; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComparisonChain.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComparisonChain.java new file mode 100644 index 0000000000000..b558ccdaf0676 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComparisonChain.java @@ -0,0 +1,312 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.primitives.Booleans; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.primitives.Longs; + +import java.util.Comparator; + +/** + * A utility for performing a chained comparison statement. Note: Java 8+ users should + * generally prefer the methods in {@link Comparator}; see below. + * + *

Example usage of {@code ComparisonChain}: + * + *

{@code
+ * public int compareTo(Foo that) {
+ *   return ComparisonChain.start()
+ *       .compare(this.aString, that.aString)
+ *       .compare(this.anInt, that.anInt)
+ *       .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
+ *       .result();
+ * }
+ * }
+ * + *

The value of this expression will have the same sign as the first nonzero comparison + * result in the chain, or will be zero if every comparison result was zero. + * + *

Note: {@code ComparisonChain} instances are immutable. For this utility to work + * correctly, calls must be chained as illustrated above. + * + *

Performance note: Even though the {@code ComparisonChain} caller always invokes its {@code + * compare} methods unconditionally, the {@code ComparisonChain} implementation stops calling its + * inputs' {@link Comparable#compareTo compareTo} and {@link Comparator#compare compare} methods as + * soon as one of them returns a nonzero result. This optimization is typically important only in + * the presence of expensive {@code compareTo} and {@code compare} implementations. + * + *

See the Guava User Guide article on {@code + * ComparisonChain}. + * + *

Java 8+ equivalents

+ * + * If you are using Java version 8 or greater, you should generally use the static methods in {@link + * Comparator} instead of {@code ComparisonChain}. The example above can be implemented like this: + * + *
{@code
+ * import static java.util.Comparator.comparing;
+ * import static java.util.Comparator.nullsLast;
+ * import static java.util.Comparator.naturalOrder;
+ *
+ * ...
+ *   private static final Comparator COMPARATOR =
+ *       comparing((Foo foo) -> foo.aString)
+ *           .thenComparing(foo -> foo.anInt)
+ *           .thenComparing(foo -> foo.anEnum, nullsLast(naturalOrder()));}
+ *
+ *   {@code @Override}{@code
+ *   public int compareTo(Foo that) {
+ *     return COMPARATOR.compare(this, that);
+ *   }
+ * }
+ * + *

With method references it is more succinct: {@code comparing(Foo::aString)} for example. + * + *

Using {@link Comparator} avoids certain types of bugs, for example when you meant to write + * {@code .compare(a.foo, b.foo)} but you actually wrote {@code .compare(a.foo, a.foo)} or {@code + * .compare(a.foo, b.bar)}. {@code ComparisonChain} also has a potential performance problem that + * {@code Comparator} doesn't: it evaluates all the parameters of all the {@code .compare} calls, + * even when the result of the comparison is already known from previous {@code .compare} calls. + * That can be expensive. + * + * @author Mark Davis + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ComparisonChain { + private ComparisonChain() {} + + /** Begins a new chained comparison statement. See example in the class documentation. */ + public static ComparisonChain start() { + return ACTIVE; + } + + private static final ComparisonChain ACTIVE = + new ComparisonChain() { + @SuppressWarnings("unchecked") // unsafe; see discussion on supertype + @Override + public ComparisonChain compare(Comparable left, Comparable right) { + return classify(((Comparable) left).compareTo(right)); + } + + @Override + public ComparisonChain compare( + @ParametricNullness T left, @ParametricNullness T right, Comparator comparator) { + return classify(comparator.compare(left, right)); + } + + @Override + public ComparisonChain compare(int left, int right) { + return classify(Ints.compare(left, right)); + } + + @Override + public ComparisonChain compare(long left, long right) { + return classify(Longs.compare(left, right)); + } + + @Override + public ComparisonChain compare(float left, float right) { + return classify(Float.compare(left, right)); + } + + @Override + public ComparisonChain compare(double left, double right) { + return classify(Double.compare(left, right)); + } + + @Override + public ComparisonChain compareTrueFirst(boolean left, boolean right) { + return classify(Booleans.compare(right, left)); // reversed + } + + @Override + public ComparisonChain compareFalseFirst(boolean left, boolean right) { + return classify(Booleans.compare(left, right)); + } + + ComparisonChain classify(int result) { + return (result < 0) ? LESS : (result > 0) ? GREATER : ACTIVE; + } + + @Override + public int result() { + return 0; + } + }; + + private static final ComparisonChain LESS = new InactiveComparisonChain(-1); + + private static final ComparisonChain GREATER = new InactiveComparisonChain(1); + + private static final class InactiveComparisonChain extends ComparisonChain { + final int result; + + InactiveComparisonChain(int result) { + this.result = result; + } + + @Override + public ComparisonChain compare(Comparable left, Comparable right) { + return this; + } + + @Override + public ComparisonChain compare( + @ParametricNullness T left, @ParametricNullness T right, Comparator comparator) { + return this; + } + + @Override + public ComparisonChain compare(int left, int right) { + return this; + } + + @Override + public ComparisonChain compare(long left, long right) { + return this; + } + + @Override + public ComparisonChain compare(float left, float right) { + return this; + } + + @Override + public ComparisonChain compare(double left, double right) { + return this; + } + + @Override + public ComparisonChain compareTrueFirst(boolean left, boolean right) { + return this; + } + + @Override + public ComparisonChain compareFalseFirst(boolean left, boolean right) { + return this; + } + + @Override + public int result() { + return result; + } + } + + /** + * Compares two comparable objects as specified by {@link Comparable#compareTo}, if the + * result of this comparison chain has not already been determined. + * + *

This method is declared to accept any 2 {@code Comparable} objects, even if they are not mutually + * comparable. If you pass objects that are not mutually comparable, this method may throw an + * exception. (The reason for this decision is lost to time, but the reason might be that + * we wanted to support legacy classes that implement the raw type {@code Comparable} (instead of + * implementing {@code Comparable}) without producing warnings. If so, we would prefer today + * to produce warnings in that case, and we may change this method to do so in the future. Support + * for raw {@code Comparable} types in Guava in general is tracked as #989.) + * + * @throws ClassCastException if the parameters are not mutually comparable + */ + public abstract ComparisonChain compare(Comparable left, Comparable right); + + /** + * Compares two objects using a comparator, if the result of this comparison chain has not + * already been determined. + */ + public abstract ComparisonChain compare( + @ParametricNullness T left, @ParametricNullness T right, Comparator comparator); + + /** + * Compares two {@code int} values as specified by {@link Ints#compare}, if the result of + * this comparison chain has not already been determined. + */ + public abstract ComparisonChain compare(int left, int right); + + /** + * Compares two {@code long} values as specified by {@link Longs#compare}, if the result of + * this comparison chain has not already been determined. + */ + public abstract ComparisonChain compare(long left, long right); + + /** + * Compares two {@code float} values as specified by {@link Float#compare}, if the result + * of this comparison chain has not already been determined. + */ + public abstract ComparisonChain compare(float left, float right); + + /** + * Compares two {@code double} values as specified by {@link Double#compare}, if the result + * of this comparison chain has not already been determined. + */ + public abstract ComparisonChain compare(double left, double right); + + /** + * Discouraged synonym for {@link #compareFalseFirst}. + * + * @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed are being either + * negated or reversed, undo the negation or reversal and use {@link #compareTrueFirst}. + * @since 19.0 + */ + @Deprecated + public final ComparisonChain compare(Boolean left, Boolean right) { + return compareFalseFirst(left, right); + } + + /** + * Compares two {@code boolean} values, considering {@code true} to be less than {@code false}, + * if the result of this comparison chain has not already been determined. + * + *

Java 8+ users: you can get the equivalent from {@link Booleans#trueFirst()}. For example: + * + *

+   * Comparator.comparing(Foo::isBar, {@link Booleans#trueFirst()})
+   * 
+ * + * @since 12.0 + */ + public abstract ComparisonChain compareTrueFirst(boolean left, boolean right); + + /** + * Compares two {@code boolean} values, considering {@code false} to be less than {@code true}, + * if the result of this comparison chain has not already been determined. + * + *

Java 8+ users: you can get the equivalent from {@link Booleans#falseFirst()}. For example: + * + *

+   * Comparator.comparing(Foo::isBar, {@link Booleans#falseFirst()})
+   * 
+ * + * @since 12.0 (present as {@code compare} since 2.0) + */ + public abstract ComparisonChain compareFalseFirst(boolean left, boolean right); + + /** + * Ends this comparison chain and returns its result: a value having the same sign as the first + * nonzero comparison result in the chain, or zero if every result was zero. + */ + public abstract int result(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompoundOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompoundOrdering.java new file mode 100644 index 0000000000000..d3c6e674004d2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/CompoundOrdering.java @@ -0,0 +1,78 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Comparator; + +/** An ordering that tries several comparators in order. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class CompoundOrdering extends Ordering implements Serializable { + final Comparator[] comparators; + + @SuppressWarnings("unchecked") // Generic array creation + CompoundOrdering(Comparator primary, Comparator secondary) { + this.comparators = (Comparator[]) new Comparator[] {primary, secondary}; + } + + @SuppressWarnings("unchecked") // Generic array creation + CompoundOrdering(Iterable> comparators) { + this.comparators = Iterables.toArray(comparators, (Comparator[]) new Comparator[0]); + } + + @Override + public int compare(@ParametricNullness T left, @ParametricNullness T right) { + for (int i = 0; i < comparators.length; i++) { + int result = comparators[i].compare(left, right); + if (result != 0) { + return result; + } + } + return 0; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof CompoundOrdering) { + CompoundOrdering that = (CompoundOrdering) object; + return Arrays.equals(this.comparators, that.comparators); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(comparators); + } + + @Override + public String toString() { + return "Ordering.compound(" + Arrays.toString(comparators) + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComputationException.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComputationException.java new file mode 100644 index 0000000000000..f6cac45fc780f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ComputationException.java @@ -0,0 +1,47 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Wraps an exception that occurred during a computation. + * + * @author Bob Lee + * @since 2.0 + * @deprecated This exception is no longer thrown by {@code org.apache.iotdb.google.common}. + * Previously, it was thrown by {@link MapMaker} computing maps. When support for computing maps + * was removed from {@code MapMaker}, it was added to {@code CacheBuilder}, which throws {@code + * ExecutionException}, {@code UncheckedExecutionException}, and {@code ExecutionError}. Any + * code that is still catching {@code ComputationException} may need to be updated to catch some + * of those types instead. (Note that this type, though deprecated, is not planned to be removed + * from Guava.) + */ +@Deprecated +@GwtCompatible +@ElementTypesAreNonnullByDefault +public class ComputationException extends RuntimeException { + /** Creates a new instance with the given cause. */ + public ComputationException(Throwable cause) { + super(cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ConcurrentHashMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ConcurrentHashMultiset.java new file mode 100644 index 0000000000000..0412515dd129a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ConcurrentHashMultiset.java @@ -0,0 +1,604 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.collect.Serialization.FieldSetter; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * A multiset that supports concurrent modifications and that provides atomic versions of most + * {@code Multiset} operations (exceptions where noted). Null elements are not supported. + * + *

See the Guava User Guide article on {@code Multiset}. + * + * @author Cliff L. Biffle + * @author mike nonemacher + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class ConcurrentHashMultiset extends AbstractMultiset implements Serializable { + + /* + * The ConcurrentHashMultiset's atomic operations are implemented primarily in terms of + * AtomicInteger's atomic operations, with some help from ConcurrentMap's atomic operations on + * creation and removal (including automatic removal of zeroes). If the modification of an + * AtomicInteger results in zero, we compareAndSet the value to zero; if that succeeds, we remove + * the entry from the Map. If another operation sees a zero in the map, it knows that the entry is + * about to be removed, so this operation may remove it (often by replacing it with a new + * AtomicInteger). + */ + + /** The number of occurrences of each element. */ + private final transient ConcurrentMap countMap; + + // This constant allows the deserialization code to set a final field. This holder class + // makes sure it is not initialized unless an instance is deserialized. + private static class FieldSettersHolder { + static final FieldSetter COUNT_MAP_FIELD_SETTER = + Serialization.getFieldSetter(ConcurrentHashMultiset.class, "countMap"); + } + + /** + * Creates a new, empty {@code ConcurrentHashMultiset} using the default initial capacity, load + * factor, and concurrency settings. + */ + public static ConcurrentHashMultiset create() { + // TODO(schmoe): provide a way to use this class with other (possibly arbitrary) + // ConcurrentMap implementors. One possibility is to extract most of this class into + // an AbstractConcurrentMapMultiset. + return new ConcurrentHashMultiset<>(new ConcurrentHashMap()); + } + + /** + * Creates a new {@code ConcurrentHashMultiset} containing the specified elements, using the + * default initial capacity, load factor, and concurrency settings. + * + *

This implementation is highly efficient when {@code elements} is itself a {@link Multiset}. + * + * @param elements the elements that the multiset should contain + */ + public static ConcurrentHashMultiset create(Iterable elements) { + ConcurrentHashMultiset multiset = ConcurrentHashMultiset.create(); + Iterables.addAll(multiset, elements); + return multiset; + } + + /** + * Creates a new, empty {@code ConcurrentHashMultiset} using {@code countMap} as the internal + * backing map. + * + *

This instance will assume ownership of {@code countMap}, and other code should not maintain + * references to the map or modify it in any way. + * + *

The returned multiset is serializable if the input map is. + * + * @param countMap backing map for storing the elements in the multiset and their counts. It must + * be empty. + * @throws IllegalArgumentException if {@code countMap} is not empty + * @since 20.0 + */ + public static ConcurrentHashMultiset create(ConcurrentMap countMap) { + return new ConcurrentHashMultiset<>(countMap); + } + + @VisibleForTesting + ConcurrentHashMultiset(ConcurrentMap countMap) { + checkArgument(countMap.isEmpty(), "the backing map (%s) must be empty", countMap); + this.countMap = countMap; + } + + // Query Operations + + /** + * Returns the number of occurrences of {@code element} in this multiset. + * + * @param element the element to look for + * @return the nonnegative number of occurrences of the element + */ + @Override + public int count(Object element) { + AtomicInteger existingCounter = Maps.safeGet(countMap, element); + return (existingCounter == null) ? 0 : existingCounter.get(); + } + + /** + * {@inheritDoc} + * + *

If the data in the multiset is modified by any other threads during this method, it is + * undefined which (if any) of these modifications will be reflected in the result. + */ + @Override + public int size() { + long sum = 0L; + for (AtomicInteger value : countMap.values()) { + sum += value.get(); + } + return Ints.saturatedCast(sum); + } + + /* + * Note: the superclass toArray() methods assume that size() gives a correct + * answer, which ours does not. + */ + + @Override + public Object[] toArray() { + return snapshot().toArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return snapshot().toArray(array); + } + + /* + * We'd love to use 'new ArrayList(this)' or 'list.addAll(this)', but + * either of these would recurse back to us again! + */ + private List snapshot() { + List list = Lists.newArrayListWithExpectedSize(size()); + for (Multiset.Entry entry : entrySet()) { + E element = entry.getElement(); + for (int i = entry.getCount(); i > 0; i--) { + list.add(element); + } + } + return list; + } + + // Modification Operations + + /** + * Adds a number of occurrences of the specified element to this multiset. + * + * @param element the element to add + * @param occurrences the number of occurrences to add + * @return the previous count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code occurrences} is negative, or if the resulting amount + * would exceed {@link Integer#MAX_VALUE} + */ + @Override + public int add(E element, int occurrences) { + checkNotNull(element); + if (occurrences == 0) { + return count(element); + } + CollectPreconditions.checkPositive(occurrences, "occurrences"); + + while (true) { + AtomicInteger existingCounter = Maps.safeGet(countMap, element); + if (existingCounter == null) { + existingCounter = countMap.putIfAbsent(element, new AtomicInteger(occurrences)); + if (existingCounter == null) { + return 0; + } + // existingCounter != null: fall through to operate against the existing AtomicInteger + } + + while (true) { + int oldValue = existingCounter.get(); + if (oldValue != 0) { + try { + int newValue = IntMath.checkedAdd(oldValue, occurrences); + if (existingCounter.compareAndSet(oldValue, newValue)) { + // newValue can't == 0, so no need to check & remove + return oldValue; + } + } catch (ArithmeticException overflow) { + throw new IllegalArgumentException( + "Overflow adding " + occurrences + " occurrences to a count of " + oldValue); + } + } else { + // In the case of a concurrent remove, we might observe a zero value, which means another + // thread is about to remove (element, existingCounter) from the map. Rather than wait, + // we can just do that work here. + AtomicInteger newCounter = new AtomicInteger(occurrences); + if ((countMap.putIfAbsent(element, newCounter) == null) + || countMap.replace(element, existingCounter, newCounter)) { + return 0; + } + break; + } + } + + // If we're still here, there was a race, so just try again. + } + } + + /** + * Removes a number of occurrences of the specified element from this multiset. If the multiset + * contains fewer than this number of occurrences to begin with, all occurrences will be removed. + * + * @param element the element whose occurrences should be removed + * @param occurrences the number of occurrences of the element to remove + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code occurrences} is negative + */ + /* + * TODO(cpovirk): remove and removeExactly currently accept null inputs only + * if occurrences == 0. This satisfies both NullPointerTester and + * CollectionRemoveTester.testRemove_nullAllowed, but it's not clear that it's + * a good policy, especially because, in order for the test to pass, the + * parameter must be misleadingly annotated as @Nullable. I suspect that + * we'll want to remove @Nullable, add an eager checkNotNull, and loosen up + * testRemove_nullAllowed. + */ + @Override + public int remove(Object element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + CollectPreconditions.checkPositive(occurrences, "occurrences"); + + AtomicInteger existingCounter = Maps.safeGet(countMap, element); + if (existingCounter == null) { + return 0; + } + while (true) { + int oldValue = existingCounter.get(); + if (oldValue != 0) { + int newValue = Math.max(0, oldValue - occurrences); + if (existingCounter.compareAndSet(oldValue, newValue)) { + if (newValue == 0) { + // Just CASed to 0; remove the entry to clean up the map. If the removal fails, + // another thread has already replaced it with a new counter, which is fine. + countMap.remove(element, existingCounter); + } + return oldValue; + } + } else { + return 0; + } + } + } + + /** + * Removes exactly the specified number of occurrences of {@code element}, or makes no change if + * this is not possible. + * + *

This method, in contrast to {@link #remove(Object, int)}, has no effect when the element + * count is smaller than {@code occurrences}. + * + * @param element the element to remove + * @param occurrences the number of occurrences of {@code element} to remove + * @return {@code true} if the removal was possible (including if {@code occurrences} is zero) + * @throws IllegalArgumentException if {@code occurrences} is negative + */ + public boolean removeExactly(Object element, int occurrences) { + if (occurrences == 0) { + return true; + } + CollectPreconditions.checkPositive(occurrences, "occurrences"); + + AtomicInteger existingCounter = Maps.safeGet(countMap, element); + if (existingCounter == null) { + return false; + } + while (true) { + int oldValue = existingCounter.get(); + if (oldValue < occurrences) { + return false; + } + int newValue = oldValue - occurrences; + if (existingCounter.compareAndSet(oldValue, newValue)) { + if (newValue == 0) { + // Just CASed to 0; remove the entry to clean up the map. If the removal fails, + // another thread has already replaced it with a new counter, which is fine. + countMap.remove(element, existingCounter); + } + return true; + } + } + } + + /** + * Adds or removes occurrences of {@code element} such that the {@link #count} of the element + * becomes {@code count}. + * + * @return the count of {@code element} in the multiset before this call + * @throws IllegalArgumentException if {@code count} is negative + */ + @Override + public int setCount(E element, int count) { + checkNotNull(element); + checkNonnegative(count, "count"); + while (true) { + AtomicInteger existingCounter = Maps.safeGet(countMap, element); + if (existingCounter == null) { + if (count == 0) { + return 0; + } else { + existingCounter = countMap.putIfAbsent(element, new AtomicInteger(count)); + if (existingCounter == null) { + return 0; + } + // existingCounter != null: fall through + } + } + + while (true) { + int oldValue = existingCounter.get(); + if (oldValue == 0) { + if (count == 0) { + return 0; + } else { + AtomicInteger newCounter = new AtomicInteger(count); + if ((countMap.putIfAbsent(element, newCounter) == null) + || countMap.replace(element, existingCounter, newCounter)) { + return 0; + } + } + break; + } else { + if (existingCounter.compareAndSet(oldValue, count)) { + if (count == 0) { + // Just CASed to 0; remove the entry to clean up the map. If the removal fails, + // another thread has already replaced it with a new counter, which is fine. + countMap.remove(element, existingCounter); + } + return oldValue; + } + } + } + } + } + + /** + * Sets the number of occurrences of {@code element} to {@code newCount}, but only if the count is + * currently {@code expectedOldCount}. If {@code element} does not appear in the multiset exactly + * {@code expectedOldCount} times, no changes will be made. + * + * @return {@code true} if the change was successful. This usually indicates that the multiset has + * been modified, but not always: in the case that {@code expectedOldCount == newCount}, the + * method will return {@code true} if the condition was met. + * @throws IllegalArgumentException if {@code expectedOldCount} or {@code newCount} is negative + */ + @Override + public boolean setCount(E element, int expectedOldCount, int newCount) { + checkNotNull(element); + checkNonnegative(expectedOldCount, "oldCount"); + checkNonnegative(newCount, "newCount"); + + AtomicInteger existingCounter = Maps.safeGet(countMap, element); + if (existingCounter == null) { + if (expectedOldCount != 0) { + return false; + } else if (newCount == 0) { + return true; + } else { + // if our write lost the race, it must have lost to a nonzero value, so we can stop + return countMap.putIfAbsent(element, new AtomicInteger(newCount)) == null; + } + } + int oldValue = existingCounter.get(); + if (oldValue == expectedOldCount) { + if (oldValue == 0) { + if (newCount == 0) { + // Just observed a 0; try to remove the entry to clean up the map + countMap.remove(element, existingCounter); + return true; + } else { + AtomicInteger newCounter = new AtomicInteger(newCount); + return (countMap.putIfAbsent(element, newCounter) == null) + || countMap.replace(element, existingCounter, newCounter); + } + } else { + if (existingCounter.compareAndSet(oldValue, newCount)) { + if (newCount == 0) { + // Just CASed to 0; remove the entry to clean up the map. If the removal fails, + // another thread has already replaced it with a new counter, which is fine. + countMap.remove(element, existingCounter); + } + return true; + } + } + } + return false; + } + + // Views + + @Override + Set createElementSet() { + Set delegate = countMap.keySet(); + return new ForwardingSet() { + @Override + protected Set delegate() { + return delegate; + } + + @Override + public boolean contains(Object object) { + return object != null && Collections2.safeContains(delegate, object); + } + + @Override + public boolean containsAll(Collection collection) { + return standardContainsAll(collection); + } + + @Override + public boolean remove(Object object) { + return object != null && Collections2.safeRemove(delegate, object); + } + + @Override + public boolean removeAll(Collection c) { + return standardRemoveAll(c); + } + }; + } + + @Override + Iterator elementIterator() { + throw new AssertionError("should never be called"); + } + + /** + * @deprecated Internal method, use {@link #entrySet()}. + */ + @Deprecated + @Override + public Set> createEntrySet() { + return new EntrySet(); + } + + @Override + int distinctElements() { + return countMap.size(); + } + + @Override + public boolean isEmpty() { + return countMap.isEmpty(); + } + + @Override + Iterator> entryIterator() { + // AbstractIterator makes this fairly clean, but it doesn't support remove(). To support + // remove(), we create an AbstractIterator, and then use ForwardingIterator to delegate to it. + Iterator> readOnlyIterator = + new AbstractIterator>() { + private final Iterator> mapEntries = + countMap.entrySet().iterator(); + + @Override + protected Entry computeNext() { + while (true) { + if (!mapEntries.hasNext()) { + return endOfData(); + } + Map.Entry mapEntry = mapEntries.next(); + int count = mapEntry.getValue().get(); + if (count != 0) { + return Multisets.immutableEntry(mapEntry.getKey(), count); + } + } + } + }; + + return new ForwardingIterator>() { + private Entry last; + + @Override + protected Iterator> delegate() { + return readOnlyIterator; + } + + @Override + public Entry next() { + last = super.next(); + return last; + } + + @Override + public void remove() { + checkState(last != null, "no calls to next() since the last call to remove()"); + ConcurrentHashMultiset.this.setCount(last.getElement(), 0); + last = null; + } + }; + } + + @Override + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + @Override + public void clear() { + countMap.clear(); + } + + private class EntrySet extends AbstractMultiset.EntrySet { + @Override + ConcurrentHashMultiset multiset() { + return ConcurrentHashMultiset.this; + } + + /* + * Note: the superclass toArray() methods assume that size() gives a correct + * answer, which ours does not. + */ + + @Override + public Object[] toArray() { + return snapshot().toArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return snapshot().toArray(array); + } + + private List> snapshot() { + List> list = Lists.newArrayListWithExpectedSize(size()); + // Not Iterables.addAll(list, this), because that'll forward right back here. + Iterators.addAll(list, iterator()); + return list; + } + } + + /** + * @serialData the ConcurrentMap of elements and their counts. + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(countMap); + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + @SuppressWarnings("unchecked") // reading data stored by writeObject + ConcurrentMap deserializedCountMap = + (ConcurrentMap) stream.readObject(); + FieldSettersHolder.COUNT_MAP_FIELD_SETTER.set(this, deserializedCountMap); + } + + private static final long serialVersionUID = 1; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ConsumingQueueIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ConsumingQueueIterator.java new file mode 100644 index 0000000000000..3dce506b72db8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ConsumingQueueIterator.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Queue; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An Iterator implementation which draws elements from a queue, removing them from the queue as it + * iterates. This class is not thread safe. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class ConsumingQueueIterator extends AbstractIterator { + private final Queue queue; + + ConsumingQueueIterator(Queue queue) { + this.queue = checkNotNull(queue); + } + + @Override + protected T computeNext() { + // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. + if (queue.isEmpty()) { + return endOfData(); + } + return queue.remove(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ContiguousSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ContiguousSet.java new file mode 100644 index 0000000000000..4b127ffc15432 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ContiguousSet.java @@ -0,0 +1,272 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Collections; +import java.util.NoSuchElementException; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A sorted set of contiguous values in a given {@link DiscreteDomain}. Example: + * + *

{@code
+ * ContiguousSet.create(Range.closed(5, 42), DiscreteDomain.integers())
+ * }
+ * + *

Note that because bounded ranges over {@code int} and {@code long} values are so common, this + * particular example can be written as just: + * + *

{@code
+ * ContiguousSet.closed(5, 42)
+ * }
+ * + *

Warning: Be extremely careful what you do with conceptually large instances (such as + * {@code ContiguousSet.create(Range.greaterThan(0), DiscreteDomain.integers()}). Certain operations + * on such a set can be performed efficiently, but others (such as {@link Set#hashCode} or {@link + * Collections#frequency}) can cause major performance problems. + * + * @author Gregory Kick + * @since 10.0 + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("rawtypes") // allow ungenerified Comparable types +@ElementTypesAreNonnullByDefault +public abstract class ContiguousSet extends ImmutableSortedSet { + /** + * Returns a {@code ContiguousSet} containing the same values in the given domain {@linkplain + * Range#contains contained} by the range. + * + * @throws IllegalArgumentException if neither range nor the domain has a lower bound, or if + * neither has an upper bound + * @since 13.0 + */ + public static ContiguousSet create( + Range range, DiscreteDomain domain) { + checkNotNull(range); + checkNotNull(domain); + Range effectiveRange = range; + try { + if (!range.hasLowerBound()) { + effectiveRange = effectiveRange.intersection(Range.atLeast(domain.minValue())); + } + if (!range.hasUpperBound()) { + effectiveRange = effectiveRange.intersection(Range.atMost(domain.maxValue())); + } + } catch (NoSuchElementException e) { + throw new IllegalArgumentException(e); + } + + boolean empty; + if (effectiveRange.isEmpty()) { + empty = true; + } else { + /* + * requireNonNull is safe because the effectiveRange operations above would have thrown or + * effectiveRange.isEmpty() would have returned true. + */ + C afterLower = requireNonNull(range.lowerBound.leastValueAbove(domain)); + C beforeUpper = requireNonNull(range.upperBound.greatestValueBelow(domain)); + // Per class spec, we are allowed to throw CCE if necessary + empty = Range.compareOrThrow(afterLower, beforeUpper) > 0; + } + + return empty + ? new EmptyContiguousSet(domain) + : new RegularContiguousSet(effectiveRange, domain); + } + + /** + * Returns a nonempty contiguous set containing all {@code int} values from {@code lower} + * (inclusive) to {@code upper} (inclusive). (These are the same values contained in {@code + * Range.closed(lower, upper)}.) + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @since 23.0 + */ + public static ContiguousSet closed(int lower, int upper) { + return create(Range.closed(lower, upper), DiscreteDomain.integers()); + } + + /** + * Returns a nonempty contiguous set containing all {@code long} values from {@code lower} + * (inclusive) to {@code upper} (inclusive). (These are the same values contained in {@code + * Range.closed(lower, upper)}.) + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @since 23.0 + */ + public static ContiguousSet closed(long lower, long upper) { + return create(Range.closed(lower, upper), DiscreteDomain.longs()); + } + + /** + * Returns a contiguous set containing all {@code int} values from {@code lower} (inclusive) to + * {@code upper} (exclusive). If the endpoints are equal, an empty set is returned. (These are the + * same values contained in {@code Range.closedOpen(lower, upper)}.) + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @since 23.0 + */ + public static ContiguousSet closedOpen(int lower, int upper) { + return create(Range.closedOpen(lower, upper), DiscreteDomain.integers()); + } + + /** + * Returns a contiguous set containing all {@code long} values from {@code lower} (inclusive) to + * {@code upper} (exclusive). If the endpoints are equal, an empty set is returned. (These are the + * same values contained in {@code Range.closedOpen(lower, upper)}.) + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @since 23.0 + */ + public static ContiguousSet closedOpen(long lower, long upper) { + return create(Range.closedOpen(lower, upper), DiscreteDomain.longs()); + } + + final DiscreteDomain domain; + + ContiguousSet(DiscreteDomain domain) { + super(Ordering.natural()); + this.domain = domain; + } + + @Override + public ContiguousSet headSet(C toElement) { + return headSetImpl(checkNotNull(toElement), false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public ContiguousSet headSet(C toElement, boolean inclusive) { + return headSetImpl(checkNotNull(toElement), inclusive); + } + + @Override + public ContiguousSet subSet(C fromElement, C toElement) { + checkNotNull(fromElement); + checkNotNull(toElement); + checkArgument(comparator().compare(fromElement, toElement) <= 0); + return subSetImpl(fromElement, true, toElement, false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public ContiguousSet subSet( + C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + checkNotNull(fromElement); + checkNotNull(toElement); + checkArgument(comparator().compare(fromElement, toElement) <= 0); + return subSetImpl(fromElement, fromInclusive, toElement, toInclusive); + } + + @Override + public ContiguousSet tailSet(C fromElement) { + return tailSetImpl(checkNotNull(fromElement), true); + } + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public ContiguousSet tailSet(C fromElement, boolean inclusive) { + return tailSetImpl(checkNotNull(fromElement), inclusive); + } + + /* + * These methods perform most headSet, subSet, and tailSet logic, besides parameter validation. + */ + @SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT. + abstract ContiguousSet headSetImpl(C toElement, boolean inclusive); + + @SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT. + abstract ContiguousSet subSetImpl( + C fromElement, boolean fromInclusive, C toElement, boolean toInclusive); + + @SuppressWarnings("MissingOverride") // Supermethod does not exist under GWT. + abstract ContiguousSet tailSetImpl(C fromElement, boolean inclusive); + + /** + * Returns the set of values that are contained in both this set and the other. + * + *

This method should always be used instead of {@link Sets#intersection} for {@link + * ContiguousSet} instances. + */ + public abstract ContiguousSet intersection(ContiguousSet other); + + /** + * Returns a range, closed on both ends, whose endpoints are the minimum and maximum values + * contained in this set. This is equivalent to {@code range(CLOSED, CLOSED)}. + * + * @throws NoSuchElementException if this set is empty + */ + public abstract Range range(); + + /** + * Returns the minimal range with the given boundary types for which all values in this set are + * {@linkplain Range#contains(Comparable) contained} within the range. + * + *

Note that this method will return ranges with unbounded endpoints if {@link BoundType#OPEN} + * is requested for a domain minimum or maximum. For example, if {@code set} was created from the + * range {@code [1..Integer.MAX_VALUE]} then {@code set.range(CLOSED, OPEN)} must return {@code + * [1..∞)}. + * + * @throws NoSuchElementException if this set is empty + */ + public abstract Range range(BoundType lowerBoundType, BoundType upperBoundType); + + @Override + @GwtIncompatible // NavigableSet + ImmutableSortedSet createDescendingSet() { + return new DescendingImmutableSortedSet<>(this); + } + + /** Returns a shorthand representation of the contents such as {@code "[1..100]"}. */ + @Override + public String toString() { + return range().toString(); + } + + /** + * Not supported. {@code ContiguousSet} instances are constructed with {@link #create}. This + * method exists only to hide {@link ImmutableSet#builder} from consumers of {@code + * ContiguousSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link #create}. + */ + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Count.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Count.java new file mode 100644 index 0000000000000..f895ed853c311 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Count.java @@ -0,0 +1,76 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** + * A mutable value of type {@code int}, for multisets to use in tracking counts of values. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class Count implements Serializable { + private int value; + + Count(int value) { + this.value = value; + } + + public int get() { + return value; + } + + public void add(int delta) { + value += delta; + } + + public int addAndGet(int delta) { + return value += delta; + } + + public void set(int newValue) { + value = newValue; + } + + public int getAndSet(int newValue) { + int result = value; + value = newValue; + return result; + } + + @Override + public int hashCode() { + return value; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Count && ((Count) obj).value == value; + } + + @Override + public String toString() { + return Integer.toString(value); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Cut.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Cut.java new file mode 100644 index 0000000000000..cb34656a54015 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Cut.java @@ -0,0 +1,484 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.primitives.Booleans; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation detail for the internal structure of {@link Range} instances. Represents a unique + * way of "cutting" a "number line" (actually of instances of type {@code C}, not necessarily + * "numbers") into two sections; this can be done below a certain value, above a certain value, + * below all values or above all values. With this object defined in this way, an interval can + * always be represented by a pair of {@code Cut} instances. + * + * @author Kevin Bourrillion + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class Cut implements Comparable>, Serializable { + final C endpoint; + + Cut(C endpoint) { + this.endpoint = endpoint; + } + + abstract boolean isLessThan(C value); + + abstract BoundType typeAsLowerBound(); + + abstract BoundType typeAsUpperBound(); + + abstract Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain); + + abstract Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain); + + abstract void describeAsLowerBound(StringBuilder sb); + + abstract void describeAsUpperBound(StringBuilder sb); + + abstract C leastValueAbove(DiscreteDomain domain); + + abstract C greatestValueBelow(DiscreteDomain domain); + + /* + * The canonical form is a BelowValue cut whenever possible, otherwise ABOVE_ALL, or + * (only in the case of types that are unbounded below) BELOW_ALL. + */ + Cut canonical(DiscreteDomain domain) { + return this; + } + + // note: overridden by {BELOW,ABOVE}_ALL + @Override + public int compareTo(Cut that) { + if (that == belowAll()) { + return 1; + } + if (that == aboveAll()) { + return -1; + } + int result = Range.compareOrThrow(endpoint, that.endpoint); + if (result != 0) { + return result; + } + // same value. below comes before above + return Booleans.compare(this instanceof AboveValue, that instanceof AboveValue); + } + + C endpoint() { + return endpoint; + } + + @SuppressWarnings("unchecked") // catching CCE + @Override + public boolean equals(Object obj) { + if (obj instanceof Cut) { + // It might not really be a Cut, but we'll catch a CCE if it's not + Cut that = (Cut) obj; + try { + int compareResult = compareTo(that); + return compareResult == 0; + } catch (ClassCastException wastNotComparableToOurType) { + return false; + } + } + return false; + } + + // Prevent "missing hashCode" warning by explicitly forcing subclasses implement it + @Override + public abstract int hashCode(); + + /* + * The implementation neither produces nor consumes any non-null instance of type C, so + * casting the type parameter is safe. + */ + @SuppressWarnings("unchecked") + static Cut belowAll() { + return (Cut) BelowAll.INSTANCE; + } + + private static final long serialVersionUID = 0; + + private static final class BelowAll extends Cut> { + private static final BelowAll INSTANCE = new BelowAll(); + + private BelowAll() { + /* + * No code ever sees this bogus value for `endpoint`: This class overrides both methods that + * use the `endpoint` field, compareTo() and endpoint(). Additionally, the main implementation + * of Cut.compareTo checks for belowAll before reading accessing `endpoint` on another Cut + * instance. + */ + super(""); + } + + @Override + Comparable endpoint() { + throw new IllegalStateException("range unbounded on this side"); + } + + @Override + boolean isLessThan(Comparable value) { + return true; + } + + @Override + BoundType typeAsLowerBound() { + throw new IllegalStateException(); + } + + @Override + BoundType typeAsUpperBound() { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + Cut> withLowerBoundType( + BoundType boundType, DiscreteDomain> domain) { + throw new IllegalStateException(); + } + + @Override + Cut> withUpperBoundType( + BoundType boundType, DiscreteDomain> domain) { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + sb.append("(-\u221e"); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + throw new AssertionError(); + } + + @Override + Comparable leastValueAbove(DiscreteDomain> domain) { + return domain.minValue(); + } + + @Override + Comparable greatestValueBelow(DiscreteDomain> domain) { + throw new AssertionError(); + } + + @Override + Cut> canonical(DiscreteDomain> domain) { + try { + return Cut.>belowValue(domain.minValue()); + } catch (NoSuchElementException e) { + return this; + } + } + + @Override + public int compareTo(Cut> o) { + return (o == this) ? 0 : -1; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public String toString() { + return "-\u221e"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } + + /* + * The implementation neither produces nor consumes any non-null instance of + * type C, so casting the type parameter is safe. + */ + @SuppressWarnings("unchecked") + static Cut aboveAll() { + return (Cut) AboveAll.INSTANCE; + } + + private static final class AboveAll extends Cut> { + private static final AboveAll INSTANCE = new AboveAll(); + + private AboveAll() { + // For discussion of "", see BelowAll. + super(""); + } + + @Override + Comparable endpoint() { + throw new IllegalStateException("range unbounded on this side"); + } + + @Override + boolean isLessThan(Comparable value) { + return false; + } + + @Override + BoundType typeAsLowerBound() { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + BoundType typeAsUpperBound() { + throw new IllegalStateException(); + } + + @Override + Cut> withLowerBoundType( + BoundType boundType, DiscreteDomain> domain) { + throw new AssertionError("this statement should be unreachable"); + } + + @Override + Cut> withUpperBoundType( + BoundType boundType, DiscreteDomain> domain) { + throw new IllegalStateException(); + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + throw new AssertionError(); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + sb.append("+\u221e)"); + } + + @Override + Comparable leastValueAbove(DiscreteDomain> domain) { + throw new AssertionError(); + } + + @Override + Comparable greatestValueBelow(DiscreteDomain> domain) { + return domain.maxValue(); + } + + @Override + public int compareTo(Cut> o) { + return (o == this) ? 0 : 1; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public String toString() { + return "+\u221e"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } + + static Cut belowValue(C endpoint) { + return new BelowValue<>(endpoint); + } + + private static final class BelowValue extends Cut { + BelowValue(C endpoint) { + super(checkNotNull(endpoint)); + } + + @Override + boolean isLessThan(C value) { + return Range.compareOrThrow(endpoint, value) <= 0; + } + + @Override + BoundType typeAsLowerBound() { + return BoundType.CLOSED; + } + + @Override + BoundType typeAsUpperBound() { + return BoundType.OPEN; + } + + @Override + Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case CLOSED: + return this; + case OPEN: + C previous = domain.previous(endpoint); + return (previous == null) ? Cut.belowAll() : new AboveValue(previous); + default: + throw new AssertionError(); + } + } + + @Override + Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case CLOSED: + C previous = domain.previous(endpoint); + return (previous == null) ? Cut.aboveAll() : new AboveValue(previous); + case OPEN: + return this; + default: + throw new AssertionError(); + } + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + sb.append('[').append(endpoint); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + sb.append(endpoint).append(')'); + } + + @Override + C leastValueAbove(DiscreteDomain domain) { + return endpoint; + } + + @Override + C greatestValueBelow(DiscreteDomain domain) { + return domain.previous(endpoint); + } + + @Override + public int hashCode() { + return endpoint.hashCode(); + } + + @Override + public String toString() { + return "\\" + endpoint + "/"; + } + + private static final long serialVersionUID = 0; + } + + static Cut aboveValue(C endpoint) { + return new AboveValue<>(endpoint); + } + + private static final class AboveValue extends Cut { + AboveValue(C endpoint) { + super(checkNotNull(endpoint)); + } + + @Override + boolean isLessThan(C value) { + return Range.compareOrThrow(endpoint, value) < 0; + } + + @Override + BoundType typeAsLowerBound() { + return BoundType.OPEN; + } + + @Override + BoundType typeAsUpperBound() { + return BoundType.CLOSED; + } + + @Override + Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case OPEN: + return this; + case CLOSED: + C next = domain.next(endpoint); + return (next == null) ? Cut.belowAll() : belowValue(next); + default: + throw new AssertionError(); + } + } + + @Override + Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { + switch (boundType) { + case OPEN: + C next = domain.next(endpoint); + return (next == null) ? Cut.aboveAll() : belowValue(next); + case CLOSED: + return this; + default: + throw new AssertionError(); + } + } + + @Override + void describeAsLowerBound(StringBuilder sb) { + sb.append('(').append(endpoint); + } + + @Override + void describeAsUpperBound(StringBuilder sb) { + sb.append(endpoint).append(']'); + } + + @Override + C leastValueAbove(DiscreteDomain domain) { + return domain.next(endpoint); + } + + @Override + C greatestValueBelow(DiscreteDomain domain) { + return endpoint; + } + + @Override + Cut canonical(DiscreteDomain domain) { + C next = leastValueAbove(domain); + return (next != null) ? belowValue(next) : Cut.aboveAll(); + } + + @Override + public int hashCode() { + return ~endpoint.hashCode(); + } + + @Override + public String toString() { + return "/" + endpoint + "\\"; + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DenseImmutableTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DenseImmutableTable.java new file mode 100644 index 0000000000000..72ef90a8a63f4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DenseImmutableTable.java @@ -0,0 +1,283 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableMap.IteratorBasedImmutableMap; + +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +/** A {@code RegularImmutableTable} optimized for dense data. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class DenseImmutableTable extends RegularImmutableTable { + private final ImmutableMap rowKeyToIndex; + private final ImmutableMap columnKeyToIndex; + private final ImmutableMap> rowMap; + private final ImmutableMap> columnMap; + + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final int[] rowCounts; + + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final int[] columnCounts; + + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final V[][] values; + + // For each cell in iteration order, the index of that cell's row key in the row key list. + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final int[] cellRowIndices; + + // For each cell in iteration order, the index of that cell's column key in the column key list. + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final int[] cellColumnIndices; + + DenseImmutableTable( + ImmutableList> cellList, + ImmutableSet rowSpace, + ImmutableSet columnSpace) { + @SuppressWarnings("unchecked") + V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()]; + this.values = array; + this.rowKeyToIndex = Maps.indexMap(rowSpace); + this.columnKeyToIndex = Maps.indexMap(columnSpace); + rowCounts = new int[rowKeyToIndex.size()]; + columnCounts = new int[columnKeyToIndex.size()]; + int[] cellRowIndices = new int[cellList.size()]; + int[] cellColumnIndices = new int[cellList.size()]; + for (int i = 0; i < cellList.size(); i++) { + Cell cell = cellList.get(i); + R rowKey = cell.getRowKey(); + C columnKey = cell.getColumnKey(); + // The requireNonNull calls are safe because we construct the indexes with indexMap. + int rowIndex = requireNonNull(rowKeyToIndex.get(rowKey)); + int columnIndex = requireNonNull(columnKeyToIndex.get(columnKey)); + V existingValue = values[rowIndex][columnIndex]; + checkNoDuplicate(rowKey, columnKey, existingValue, cell.getValue()); + values[rowIndex][columnIndex] = cell.getValue(); + rowCounts[rowIndex]++; + columnCounts[columnIndex]++; + cellRowIndices[i] = rowIndex; + cellColumnIndices[i] = columnIndex; + } + this.cellRowIndices = cellRowIndices; + this.cellColumnIndices = cellColumnIndices; + this.rowMap = new RowMap(); + this.columnMap = new ColumnMap(); + } + + /** An immutable map implementation backed by an indexed nullable array. */ + private abstract static class ImmutableArrayMap extends IteratorBasedImmutableMap { + private final int size; + + ImmutableArrayMap(int size) { + this.size = size; + } + + abstract ImmutableMap keyToIndex(); + + // True if getValue never returns null. + private boolean isFull() { + return size == keyToIndex().size(); + } + + K getKey(int index) { + return keyToIndex().keySet().asList().get(index); + } + + abstract V getValue(int keyIndex); + + @Override + ImmutableSet createKeySet() { + return isFull() ? keyToIndex().keySet() : super.createKeySet(); + } + + @Override + public int size() { + return size; + } + + @Override + public V get(Object key) { + Integer keyIndex = keyToIndex().get(key); + return (keyIndex == null) ? null : getValue(keyIndex); + } + + @Override + UnmodifiableIterator> entryIterator() { + return new AbstractIterator>() { + private int index = -1; + private final int maxIndex = keyToIndex().size(); + + @Override + protected Entry computeNext() { + for (index++; index < maxIndex; index++) { + V value = getValue(index); + if (value != null) { + return Maps.immutableEntry(getKey(index), value); + } + } + return endOfData(); + } + }; + } + } + + private final class Row extends ImmutableArrayMap { + private final int rowIndex; + + Row(int rowIndex) { + super(rowCounts[rowIndex]); + this.rowIndex = rowIndex; + } + + @Override + ImmutableMap keyToIndex() { + return columnKeyToIndex; + } + + @Override + V getValue(int keyIndex) { + return values[rowIndex][keyIndex]; + } + + @Override + boolean isPartialView() { + return true; + } + } + + private final class Column extends ImmutableArrayMap { + private final int columnIndex; + + Column(int columnIndex) { + super(columnCounts[columnIndex]); + this.columnIndex = columnIndex; + } + + @Override + ImmutableMap keyToIndex() { + return rowKeyToIndex; + } + + @Override + V getValue(int keyIndex) { + return values[keyIndex][columnIndex]; + } + + @Override + boolean isPartialView() { + return true; + } + } + + private final class RowMap extends ImmutableArrayMap> { + private RowMap() { + super(rowCounts.length); + } + + @Override + ImmutableMap keyToIndex() { + return rowKeyToIndex; + } + + @Override + ImmutableMap getValue(int keyIndex) { + return new Row(keyIndex); + } + + @Override + boolean isPartialView() { + return false; + } + } + + private final class ColumnMap extends ImmutableArrayMap> { + private ColumnMap() { + super(columnCounts.length); + } + + @Override + ImmutableMap keyToIndex() { + return columnKeyToIndex; + } + + @Override + ImmutableMap getValue(int keyIndex) { + return new Column(keyIndex); + } + + @Override + boolean isPartialView() { + return false; + } + } + + @Override + public ImmutableMap> columnMap() { + // Casts without copying. + ImmutableMap> columnMap = this.columnMap; + return ImmutableMap.>copyOf(columnMap); + } + + @Override + public ImmutableMap> rowMap() { + // Casts without copying. + ImmutableMap> rowMap = this.rowMap; + return ImmutableMap.>copyOf(rowMap); + } + + @Override + public V get(Object rowKey, Object columnKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + return ((rowIndex == null) || (columnIndex == null)) ? null : values[rowIndex][columnIndex]; + } + + @Override + public int size() { + return cellRowIndices.length; + } + + @Override + Cell getCell(int index) { + int rowIndex = cellRowIndices[index]; + int columnIndex = cellColumnIndices[index]; + R rowKey = rowKeySet().asList().get(rowIndex); + C columnKey = columnKeySet().asList().get(columnIndex); + // requireNonNull is safe because we use indexes that were populated by the constructor. + V value = requireNonNull(values[rowIndex][columnIndex]); + return cellOf(rowKey, columnKey, value); + } + + @Override + V getValue(int index) { + // requireNonNull is safe because we use indexes that were populated by the constructor. + return requireNonNull(values[cellRowIndices[index]][cellColumnIndices[index]]); + } + + @Override + SerializedForm createSerializedForm() { + return SerializedForm.create(this, cellRowIndices, cellColumnIndices); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingImmutableSortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingImmutableSortedMultiset.java new file mode 100644 index 0000000000000..d97d51c695d4c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingImmutableSortedMultiset.java @@ -0,0 +1,88 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +/** + * A descending wrapper around an {@code ImmutableSortedMultiset} + * + * @author Louis Wasserman + */ +@SuppressWarnings("serial") // uses writeReplace, not default serialization +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class DescendingImmutableSortedMultiset extends ImmutableSortedMultiset { + private final transient ImmutableSortedMultiset forward; + + DescendingImmutableSortedMultiset(ImmutableSortedMultiset forward) { + this.forward = forward; + } + + @Override + public int count(Object element) { + return forward.count(element); + } + + @Override + public Entry firstEntry() { + return forward.lastEntry(); + } + + @Override + public Entry lastEntry() { + return forward.firstEntry(); + } + + @Override + public int size() { + return forward.size(); + } + + @Override + public ImmutableSortedSet elementSet() { + return forward.elementSet().descendingSet(); + } + + @Override + Entry getEntry(int index) { + return forward.entrySet().asList().reverse().get(index); + } + + @Override + public ImmutableSortedMultiset descendingMultiset() { + return forward; + } + + @Override + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + return forward.tailMultiset(upperBound, boundType).descendingMultiset(); + } + + @Override + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return forward.headMultiset(lowerBound, boundType).descendingMultiset(); + } + + @Override + boolean isPartialView() { + return forward.isPartialView(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingImmutableSortedSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingImmutableSortedSet.java new file mode 100644 index 0000000000000..e02ca94f34c47 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingImmutableSortedSet.java @@ -0,0 +1,122 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +/** + * Skeletal implementation of {@link ImmutableSortedSet#descendingSet()}. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class DescendingImmutableSortedSet extends ImmutableSortedSet { + private final ImmutableSortedSet forward; + + DescendingImmutableSortedSet(ImmutableSortedSet forward) { + super(Ordering.from(forward.comparator()).reverse()); + this.forward = forward; + } + + @Override + public boolean contains(Object object) { + return forward.contains(object); + } + + @Override + public int size() { + return forward.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return forward.descendingIterator(); + } + + @Override + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return forward.tailSet(toElement, inclusive).descendingSet(); + } + + @Override + ImmutableSortedSet subSetImpl( + E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); + } + + @Override + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return forward.headSet(fromElement, inclusive).descendingSet(); + } + + @Override + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet descendingSet() { + return forward; + } + + @Override + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return forward.iterator(); + } + + @Override + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + throw new AssertionError("should never be called"); + } + + @Override + public E lower(E element) { + return forward.higher(element); + } + + @Override + public E floor(E element) { + return forward.ceiling(element); + } + + @Override + public E ceiling(E element) { + return forward.floor(element); + } + + @Override + public E higher(E element) { + return forward.lower(element); + } + + @Override + int indexOf(Object target) { + int index = forward.indexOf(target); + if (index == -1) { + return index; + } else { + return size() - 1 - index; + } + } + + @Override + boolean isPartialView() { + return forward.isPartialView(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingMultiset.java new file mode 100644 index 0000000000000..6f143fe962887 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DescendingMultiset.java @@ -0,0 +1,164 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; + +/** + * A skeleton implementation of a descending multiset. Only needs {@code forwardMultiset()} and + * {@code entryIterator()}. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class DescendingMultiset extends ForwardingMultiset + implements SortedMultiset { + abstract SortedMultiset forwardMultiset(); + + private transient Comparator comparator; + + @Override + public Comparator comparator() { + Comparator result = comparator; + if (result == null) { + return comparator = Ordering.from(forwardMultiset().comparator()).reverse(); + } + return result; + } + + private transient NavigableSet elementSet; + + @Override + public NavigableSet elementSet() { + NavigableSet result = elementSet; + if (result == null) { + return elementSet = new SortedMultisets.NavigableElementSet<>(this); + } + return result; + } + + @Override + public Entry pollFirstEntry() { + return forwardMultiset().pollLastEntry(); + } + + @Override + public Entry pollLastEntry() { + return forwardMultiset().pollFirstEntry(); + } + + @Override + public SortedMultiset headMultiset(@ParametricNullness E toElement, BoundType boundType) { + return forwardMultiset().tailMultiset(toElement, boundType).descendingMultiset(); + } + + @Override + public SortedMultiset subMultiset( + @ParametricNullness E fromElement, + BoundType fromBoundType, + @ParametricNullness E toElement, + BoundType toBoundType) { + return forwardMultiset() + .subMultiset(toElement, toBoundType, fromElement, fromBoundType) + .descendingMultiset(); + } + + @Override + public SortedMultiset tailMultiset(@ParametricNullness E fromElement, BoundType boundType) { + return forwardMultiset().headMultiset(fromElement, boundType).descendingMultiset(); + } + + @Override + protected Multiset delegate() { + return forwardMultiset(); + } + + @Override + public SortedMultiset descendingMultiset() { + return forwardMultiset(); + } + + @Override + public Entry firstEntry() { + return forwardMultiset().lastEntry(); + } + + @Override + public Entry lastEntry() { + return forwardMultiset().firstEntry(); + } + + abstract Iterator> entryIterator(); + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + Set> createEntrySet() { + class EntrySetImpl extends Multisets.EntrySet { + @Override + Multiset multiset() { + return DescendingMultiset.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public int size() { + return forwardMultiset().entrySet().size(); + } + } + return new EntrySetImpl(); + } + + @Override + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + @Override + public Object[] toArray() { + return standardToArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return standardToArray(array); + } + + @Override + public String toString() { + return entrySet().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DiscreteDomain.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DiscreteDomain.java new file mode 100644 index 0000000000000..e6e888bc5a1f9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/DiscreteDomain.java @@ -0,0 +1,338 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.NoSuchElementException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * A descriptor for a discrete {@code Comparable} domain such as all {@link Integer} + * instances. A discrete domain is one that supports the three basic operations: {@link #next}, + * {@link #previous} and {@link #distance}, according to their specifications. The methods {@link + * #minValue} and {@link #maxValue} should also be overridden for bounded types. + * + *

A discrete domain always represents the entire set of values of its type; it cannot + * represent partial domains such as "prime integers" or "strings of length 5." + * + *

See the Guava User Guide section on {@code + * DiscreteDomain}. + * + * @author Kevin Bourrillion + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class DiscreteDomain { + + /** + * Returns the discrete domain for values of type {@code Integer}. + * + *

This method always returns the same object. That object is serializable; deserializing it + * results in the same object too. + * + * @since 14.0 (since 10.0 as {@code DiscreteDomains.integers()}) + */ + public static DiscreteDomain integers() { + return IntegerDomain.INSTANCE; + } + + private static final class IntegerDomain extends DiscreteDomain implements Serializable { + private static final IntegerDomain INSTANCE = new IntegerDomain(); + + IntegerDomain() { + super(true); + } + + @Override + public Integer next(Integer value) { + int i = value; + return (i == Integer.MAX_VALUE) ? null : i + 1; + } + + @Override + public Integer previous(Integer value) { + int i = value; + return (i == Integer.MIN_VALUE) ? null : i - 1; + } + + @Override + Integer offset(Integer origin, long distance) { + checkNonnegative(distance, "distance"); + return Ints.checkedCast(origin.longValue() + distance); + } + + @Override + public long distance(Integer start, Integer end) { + return (long) end - start; + } + + @Override + public Integer minValue() { + return Integer.MIN_VALUE; + } + + @Override + public Integer maxValue() { + return Integer.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "DiscreteDomain.integers()"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the discrete domain for values of type {@code Long}. + * + *

This method always returns the same object. That object is serializable; deserializing it + * results in the same object too. + * + * @since 14.0 (since 10.0 as {@code DiscreteDomains.longs()}) + */ + public static DiscreteDomain longs() { + return LongDomain.INSTANCE; + } + + private static final class LongDomain extends DiscreteDomain implements Serializable { + private static final LongDomain INSTANCE = new LongDomain(); + + LongDomain() { + super(true); + } + + @Override + public Long next(Long value) { + long l = value; + return (l == Long.MAX_VALUE) ? null : l + 1; + } + + @Override + public Long previous(Long value) { + long l = value; + return (l == Long.MIN_VALUE) ? null : l - 1; + } + + @Override + Long offset(Long origin, long distance) { + checkNonnegative(distance, "distance"); + long result = origin + distance; + if (result < 0) { + checkArgument(origin < 0, "overflow"); + } + return result; + } + + @Override + public long distance(Long start, Long end) { + long result = end - start; + if (end > start && result < 0) { // overflow + return Long.MAX_VALUE; + } + if (end < start && result > 0) { // underflow + return Long.MIN_VALUE; + } + return result; + } + + @Override + public Long minValue() { + return Long.MIN_VALUE; + } + + @Override + public Long maxValue() { + return Long.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "DiscreteDomain.longs()"; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the discrete domain for values of type {@code BigInteger}. + * + *

This method always returns the same object. That object is serializable; deserializing it + * results in the same object too. + * + * @since 15.0 + */ + public static DiscreteDomain bigIntegers() { + return BigIntegerDomain.INSTANCE; + } + + private static final class BigIntegerDomain extends DiscreteDomain + implements Serializable { + private static final BigIntegerDomain INSTANCE = new BigIntegerDomain(); + + BigIntegerDomain() { + super(true); + } + + private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); + private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + + @Override + public BigInteger next(BigInteger value) { + return value.add(BigInteger.ONE); + } + + @Override + public BigInteger previous(BigInteger value) { + return value.subtract(BigInteger.ONE); + } + + @Override + BigInteger offset(BigInteger origin, long distance) { + checkNonnegative(distance, "distance"); + return origin.add(BigInteger.valueOf(distance)); + } + + @Override + public long distance(BigInteger start, BigInteger end) { + return end.subtract(start).max(MIN_LONG).min(MAX_LONG).longValue(); + } + + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "DiscreteDomain.bigIntegers()"; + } + + private static final long serialVersionUID = 0; + } + + final boolean supportsFastOffset; + + /** Constructor for use by subclasses. */ + protected DiscreteDomain() { + this(false); + } + + /** Private constructor for built-in DiscreteDomains supporting fast offset. */ + private DiscreteDomain(boolean supportsFastOffset) { + this.supportsFastOffset = supportsFastOffset; + } + + /** + * Returns, conceptually, "origin + distance", or equivalently, the result of calling {@link + * #next} on {@code origin} {@code distance} times. + */ + C offset(C origin, long distance) { + C current = origin; + checkNonnegative(distance, "distance"); + for (long i = 0; i < distance; i++) { + current = next(current); + if (current == null) { + throw new IllegalArgumentException( + "overflowed computing offset(" + origin + ", " + distance + ")"); + } + } + return current; + } + + /** + * Returns the unique least value of type {@code C} that is greater than {@code value}, or {@code + * null} if none exists. Inverse operation to {@link #previous}. + * + * @param value any value of type {@code C} + * @return the least value greater than {@code value}, or {@code null} if {@code value} is {@code + * maxValue()} + */ + public abstract C next(C value); + + /** + * Returns the unique greatest value of type {@code C} that is less than {@code value}, or {@code + * null} if none exists. Inverse operation to {@link #next}. + * + * @param value any value of type {@code C} + * @return the greatest value less than {@code value}, or {@code null} if {@code value} is {@code + * minValue()} + */ + public abstract C previous(C value); + + /** + * Returns a signed value indicating how many nested invocations of {@link #next} (if positive) or + * {@link #previous} (if negative) are needed to reach {@code end} starting from {@code start}. + * For example, if {@code end = next(next(next(start)))}, then {@code distance(start, end) == 3} + * and {@code distance(end, start) == -3}. As well, {@code distance(a, a)} is always zero. + * + *

Note that this function is necessarily well-defined for any discrete type. + * + * @return the distance as described above, or {@link Long#MIN_VALUE} or {@link Long#MAX_VALUE} if + * the distance is too small or too large, respectively. + */ + public abstract long distance(C start, C end); + + /** + * Returns the minimum value of type {@code C}, if it has one. The minimum value is the unique + * value for which {@link Comparable#compareTo(Object)} never returns a positive value for any + * input of type {@code C}. + * + *

The default implementation throws {@code NoSuchElementException}. + * + * @return the minimum value of type {@code C}; never null + * @throws NoSuchElementException if the type has no (practical) minimum value; for example, + * {@link java.math.BigInteger} + */ + public C minValue() { + throw new NoSuchElementException(); + } + + /** + * Returns the maximum value of type {@code C}, if it has one. The maximum value is the unique + * value for which {@link Comparable#compareTo(Object)} never returns a negative value for any + * input of type {@code C}. + * + *

The default implementation throws {@code NoSuchElementException}. + * + * @return the maximum value of type {@code C}; never null + * @throws NoSuchElementException if the type has no (practical) maximum value; for example, + * {@link java.math.BigInteger} + */ + public C maxValue() { + throw new NoSuchElementException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..4732a7462a975 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyContiguousSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyContiguousSet.java new file mode 100644 index 0000000000000..1221cf38ec597 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyContiguousSet.java @@ -0,0 +1,187 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * An empty contiguous set. + * + * @author Gregory Kick + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("rawtypes") // allow ungenerified Comparable types +@ElementTypesAreNonnullByDefault +final class EmptyContiguousSet extends ContiguousSet { + EmptyContiguousSet(DiscreteDomain domain) { + super(domain); + } + + @Override + public C first() { + throw new NoSuchElementException(); + } + + @Override + public C last() { + throw new NoSuchElementException(); + } + + @Override + public int size() { + return 0; + } + + @Override + public ContiguousSet intersection(ContiguousSet other) { + return this; + } + + @Override + public Range range() { + throw new NoSuchElementException(); + } + + @Override + public Range range(BoundType lowerBoundType, BoundType upperBoundType) { + throw new NoSuchElementException(); + } + + @Override + ContiguousSet headSetImpl(C toElement, boolean inclusive) { + return this; + } + + @Override + ContiguousSet subSetImpl( + C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + return this; + } + + @Override + ContiguousSet tailSetImpl(C fromElement, boolean fromInclusive) { + return this; + } + + @Override + public boolean contains(Object object) { + return false; + } + + @GwtIncompatible // not used by GWT emulation + @Override + int indexOf(Object target) { + return -1; + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @GwtIncompatible // NavigableSet + @Override + public UnmodifiableIterator descendingIterator() { + return Iterators.emptyIterator(); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public ImmutableList asList() { + return ImmutableList.of(); + } + + @Override + public String toString() { + return "[]"; + } + + @Override + public boolean equals(Object object) { + if (object instanceof Set) { + Set that = (Set) object; + return that.isEmpty(); + } + return false; + } + + @GwtIncompatible // not used in GWT + @Override + boolean isHashCodeFast() { + return true; + } + + @Override + public int hashCode() { + return 0; + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private static final class SerializedForm implements Serializable { + private final DiscreteDomain domain; + + private SerializedForm(DiscreteDomain domain) { + this.domain = domain; + } + + private Object readResolve() { + return new EmptyContiguousSet<>(domain); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible // serialization + @J2ktIncompatible + @Override + Object writeReplace() { + return new SerializedForm<>(domain); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @GwtIncompatible // NavigableSet + @Override + ImmutableSortedSet createDescendingSet() { + return ImmutableSortedSet.emptySet(Ordering.natural().reverse()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyImmutableListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyImmutableListMultimap.java new file mode 100644 index 0000000000000..8b2e14f09ee7c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyImmutableListMultimap.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; + +/** + * Implementation of {@link ImmutableListMultimap} with no entries. + * + * @author Jared Levy + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +class EmptyImmutableListMultimap extends ImmutableListMultimap { + static final EmptyImmutableListMultimap INSTANCE = new EmptyImmutableListMultimap(); + + private EmptyImmutableListMultimap() { + super(ImmutableMap.>of(), 0); + } + + /* + * TODO(b/242884182): Figure out why this helps produce the same class file when we compile most + * of common.collect a second time with the results of the first compilation on the classpath. Or + * just back this out once we stop doing that (which we'll do after our internal GWT setup + * changes). + */ + @Override + public ImmutableMap> asMap() { + return super.asMap(); + } + + private Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyImmutableSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyImmutableSetMultimap.java new file mode 100644 index 0000000000000..51eb9b916d30a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EmptyImmutableSetMultimap.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; + +/** + * Implementation of {@link ImmutableListMultimap} with no entries. + * + * @author Mike Ward + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +class EmptyImmutableSetMultimap extends ImmutableSetMultimap { + static final EmptyImmutableSetMultimap INSTANCE = new EmptyImmutableSetMultimap(); + + private EmptyImmutableSetMultimap() { + super(ImmutableMap.>of(), 0, null); + } + + /* + * TODO(b/242884182): Figure out why this helps produce the same class file when we compile most + * of common.collect a second time with the results of the first compilation on the classpath. Or + * just back this out once we stop doing that (which we'll do after our internal GWT setup + * changes). + */ + @Override + public ImmutableMap> asMap() { + return super.asMap(); + } + + private Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumBiMap.java new file mode 100644 index 0000000000000..f43bd03739ec7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumBiMap.java @@ -0,0 +1,167 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.Platform.getDeclaringClassOrObjectForJ2cl; + +/** + * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values are not permitted. + * An {@code EnumBiMap} and its inverse are both serializable. + * + *

See the Guava User Guide article on {@code BiMap}. + * + * @author Mike Bostock + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public final class EnumBiMap, V extends Enum> extends AbstractBiMap { + /* + * J2CL's EnumMap does not need the Class instance, so we can use Object.class instead. (Or we + * could use null, but that messes with our nullness checking, including under J2KT. We could + * probably work around it by changing how we annotate the J2CL EnumMap, but that's probably more + * trouble than just using Object.class.) + * + * Then we declare the getters for these fields as @GwtIncompatible so that no one can try to use + * them under J2CL—or, as an unfortunate side effect, under GWT. We do still give the fields + * themselves their proper values under GWT, since GWT's EnumMap does need the Class instance. + * + * Note that sometimes these fields *do* have correct values under J2CL: They will if the caller + * calls `create(Foo.class)`, rather than `create(map)`. That's fine; we just shouldn't rely on + * it. + */ + transient Class keyTypeOrObjectUnderJ2cl; + transient Class valueTypeOrObjectUnderJ2cl; + + /** + * Returns a new, empty {@code EnumBiMap} using the specified key and value types. + * + * @param keyType the key type + * @param valueType the value type + */ + public static , V extends Enum> EnumBiMap create( + Class keyType, Class valueType) { + return new EnumBiMap<>(keyType, valueType); + } + + /** + * Returns a new bimap with the same mappings as the specified map. If the specified map is an + * {@code EnumBiMap}, the new bimap has the same types as the provided map. Otherwise, the + * specified map must contain at least one mapping, in order to determine the key and value types. + * + * @param map the map whose mappings are to be placed in this map + * @throws IllegalArgumentException if map is not an {@code EnumBiMap} instance and contains no + * mappings + */ + public static , V extends Enum> EnumBiMap create(Map map) { + EnumBiMap bimap = + create(inferKeyTypeOrObjectUnderJ2cl(map), inferValueTypeOrObjectUnderJ2cl(map)); + bimap.putAll(map); + return bimap; + } + + private EnumBiMap(Class keyTypeOrObjectUnderJ2cl, Class valueTypeOrObjectUnderJ2cl) { + super( + new EnumMap(keyTypeOrObjectUnderJ2cl), new EnumMap(valueTypeOrObjectUnderJ2cl)); + this.keyTypeOrObjectUnderJ2cl = keyTypeOrObjectUnderJ2cl; + this.valueTypeOrObjectUnderJ2cl = valueTypeOrObjectUnderJ2cl; + } + + static > Class inferKeyTypeOrObjectUnderJ2cl(Map map) { + if (map instanceof EnumBiMap) { + return ((EnumBiMap) map).keyTypeOrObjectUnderJ2cl; + } + if (map instanceof EnumHashBiMap) { + return ((EnumHashBiMap) map).keyTypeOrObjectUnderJ2cl; + } + checkArgument(!map.isEmpty()); + return getDeclaringClassOrObjectForJ2cl(map.keySet().iterator().next()); + } + + private static > Class inferValueTypeOrObjectUnderJ2cl(Map map) { + if (map instanceof EnumBiMap) { + return ((EnumBiMap) map).valueTypeOrObjectUnderJ2cl; + } + checkArgument(!map.isEmpty()); + return getDeclaringClassOrObjectForJ2cl(map.values().iterator().next()); + } + + /** Returns the associated key type. */ + @GwtIncompatible + public Class keyType() { + return keyTypeOrObjectUnderJ2cl; + } + + /** Returns the associated value type. */ + @GwtIncompatible + public Class valueType() { + return valueTypeOrObjectUnderJ2cl; + } + + @Override + K checkKey(K key) { + return checkNotNull(key); + } + + @Override + V checkValue(V value) { + return checkNotNull(value); + } + + /** + * @serialData the key class, value class, number of entries, first key, first value, second key, + * second value, and so on. + */ + @GwtIncompatible // java.io.ObjectOutputStream + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyTypeOrObjectUnderJ2cl); + stream.writeObject(valueTypeOrObjectUnderJ2cl); + Serialization.writeMap(this, stream); + } + + @SuppressWarnings("unchecked") // reading fields populated by writeObject + @GwtIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyTypeOrObjectUnderJ2cl = (Class) stream.readObject(); + valueTypeOrObjectUnderJ2cl = (Class) stream.readObject(); + setDelegates( + new EnumMap(keyTypeOrObjectUnderJ2cl), new EnumMap(valueTypeOrObjectUnderJ2cl)); + Serialization.populateMap(this, stream); + } + + @GwtIncompatible // not needed in emulated source. + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumHashBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumHashBiMap.java new file mode 100644 index 0000000000000..5859a7911aae9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumHashBiMap.java @@ -0,0 +1,138 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@code BiMap} backed by an {@code EnumMap} instance for keys-to-values, and a {@code HashMap} + * instance for values-to-keys. Null keys are not permitted, but null values are. An {@code + * EnumHashBiMap} and its inverse are both serializable. + * + *

See the Guava User Guide article on {@code BiMap}. + * + * @author Mike Bostock + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public final class EnumHashBiMap, V extends Object> extends AbstractBiMap { + transient Class keyTypeOrObjectUnderJ2cl; + + /** + * Returns a new, empty {@code EnumHashBiMap} using the specified key type. + * + * @param keyType the key type + */ + public static , V extends Object> EnumHashBiMap create(Class keyType) { + return new EnumHashBiMap<>(keyType); + } + + /** + * Constructs a new bimap with the same mappings as the specified map. If the specified map is an + * {@code EnumHashBiMap} or an {@link EnumBiMap}, the new bimap has the same key type as the input + * bimap. Otherwise, the specified map must contain at least one mapping, in order to determine + * the key type. + * + * @param map the map whose mappings are to be placed in this map + * @throws IllegalArgumentException if map is not an {@code EnumBiMap} or an {@code EnumHashBiMap} + * instance and contains no mappings + */ + public static , V extends Object> EnumHashBiMap create( + Map map) { + EnumHashBiMap bimap = create(EnumBiMap.inferKeyTypeOrObjectUnderJ2cl(map)); + bimap.putAll(map); + return bimap; + } + + private EnumHashBiMap(Class keyType) { + super(new EnumMap(keyType), new HashMap()); + // TODO: cpovirk - Pre-size the HashMap based on the number of enum values? + this.keyTypeOrObjectUnderJ2cl = keyType; + } + + // Overriding these 3 methods to show that values may be null (but not keys) + + @Override + K checkKey(K key) { + return checkNotNull(key); + } + + @Override + @SuppressWarnings("RedundantOverride") // b/192446478: RedundantOverride ignores some annotations. + // TODO(b/192446998): Remove this override after tools understand nullness better. + public V put(K key, @ParametricNullness V value) { + return super.put(key, value); + } + + @Override + @SuppressWarnings("RedundantOverride") // b/192446478: RedundantOverride ignores some annotations. + // TODO(b/192446998): Remove this override after tools understand nullness better. + public V forcePut(K key, @ParametricNullness V value) { + return super.forcePut(key, value); + } + + /** Returns the associated key type. */ + @GwtIncompatible + public Class keyType() { + return keyTypeOrObjectUnderJ2cl; + } + + /** + * @serialData the key class, number of entries, first key, first value, second key, second value, + * and so on. + */ + @GwtIncompatible // java.io.ObjectOutputStream + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyTypeOrObjectUnderJ2cl); + Serialization.writeMap(this, stream); + } + + @SuppressWarnings("unchecked") // reading field populated by writeObject + @GwtIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyTypeOrObjectUnderJ2cl = (Class) stream.readObject(); + /* + * TODO: cpovirk - Pre-size the HashMap based on the number of enum values? (But *not* based on + * the number of entries in the map, as that makes it easy for hostile inputs to trigger lots of + * allocation—not that any program should be deserializing hostile inputs to begin with!) + */ + setDelegates(new EnumMap(keyTypeOrObjectUnderJ2cl), new HashMap()); + Serialization.populateMap(this, stream); + } + + @GwtIncompatible // only needed in emulated source. + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumMultiset.java new file mode 100644 index 0000000000000..18f91fcaa572e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EnumMultiset.java @@ -0,0 +1,322 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.ObjIntConsumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * Multiset implementation specialized for enum elements, supporting all single-element operations + * in O(1). + * + *

See the Guava User Guide article on {@code Multiset}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public final class EnumMultiset> extends AbstractMultiset + implements Serializable { + /** Creates an empty {@code EnumMultiset}. */ + public static > EnumMultiset create(Class type) { + return new EnumMultiset(type); + } + + /** + * Creates a new {@code EnumMultiset} containing the specified elements. + * + *

This implementation is highly efficient when {@code elements} is itself a {@link Multiset}. + * + * @param elements the elements that the multiset should contain + * @throws IllegalArgumentException if {@code elements} is empty + */ + public static > EnumMultiset create(Iterable elements) { + Iterator iterator = elements.iterator(); + checkArgument(iterator.hasNext(), "EnumMultiset constructor passed empty Iterable"); + EnumMultiset multiset = new EnumMultiset<>(iterator.next().getDeclaringClass()); + Iterables.addAll(multiset, elements); + return multiset; + } + + /** + * Returns a new {@code EnumMultiset} instance containing the given elements. Unlike {@link + * EnumMultiset#create(Iterable)}, this method does not produce an exception on an empty iterable. + * + * @since 14.0 + */ + public static > EnumMultiset create(Iterable elements, Class type) { + EnumMultiset result = create(type); + Iterables.addAll(result, elements); + return result; + } + + private transient Class type; + private transient E[] enumConstants; + private transient int[] counts; + private transient int distinctElements; + private transient long size; + + /** Creates an empty {@code EnumMultiset}. */ + private EnumMultiset(Class type) { + this.type = type; + checkArgument(type.isEnum()); + this.enumConstants = type.getEnumConstants(); + this.counts = new int[enumConstants.length]; + } + + private boolean isActuallyE(Object o) { + if (o instanceof Enum) { + Enum e = (Enum) o; + int index = e.ordinal(); + return index < enumConstants.length && enumConstants[index] == e; + } + return false; + } + + /** + * Returns {@code element} cast to {@code E}, if it actually is a nonnull E. Otherwise, throws + * either a NullPointerException or a ClassCastException as appropriate. + */ + private void checkIsE(Object element) { + checkNotNull(element); + if (!isActuallyE(element)) { + throw new ClassCastException("Expected an " + type + " but got " + element); + } + } + + @Override + int distinctElements() { + return distinctElements; + } + + @Override + public int size() { + return Ints.saturatedCast(size); + } + + @Override + public int count(Object element) { + // isActuallyE checks for null, but we check explicitly to help nullness checkers. + if (element == null || !isActuallyE(element)) { + return 0; + } + Enum e = (Enum) element; + return counts[e.ordinal()]; + } + + // Modification Operations + @Override + public int add(E element, int occurrences) { + checkIsE(element); + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + int index = element.ordinal(); + int oldCount = counts[index]; + long newCount = (long) oldCount + occurrences; + checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount); + counts[index] = (int) newCount; + if (oldCount == 0) { + distinctElements++; + } + size += occurrences; + return oldCount; + } + + // Modification Operations + @Override + public int remove(Object element, int occurrences) { + // isActuallyE checks for null, but we check explicitly to help nullness checkers. + if (element == null || !isActuallyE(element)) { + return 0; + } + Enum e = (Enum) element; + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + int index = e.ordinal(); + int oldCount = counts[index]; + if (oldCount == 0) { + return 0; + } else if (oldCount <= occurrences) { + counts[index] = 0; + distinctElements--; + size -= oldCount; + } else { + counts[index] = oldCount - occurrences; + size -= occurrences; + } + return oldCount; + } + + // Modification Operations + @Override + public int setCount(E element, int count) { + checkIsE(element); + checkNonnegative(count, "count"); + int index = element.ordinal(); + int oldCount = counts[index]; + counts[index] = count; + size += count - oldCount; + if (oldCount == 0 && count > 0) { + distinctElements++; + } else if (oldCount > 0 && count == 0) { + distinctElements--; + } + return oldCount; + } + + @Override + public void clear() { + Arrays.fill(counts, 0); + size = 0; + distinctElements = 0; + } + + abstract class Itr implements Iterator { + int index = 0; + int toRemove = -1; + + abstract T output(int index); + + @Override + public boolean hasNext() { + for (; index < enumConstants.length; index++) { + if (counts[index] > 0) { + return true; + } + } + return false; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + T result = output(index); + toRemove = index; + index++; + return result; + } + + @Override + public void remove() { + checkRemove(toRemove >= 0); + if (counts[toRemove] > 0) { + distinctElements--; + size -= counts[toRemove]; + counts[toRemove] = 0; + } + toRemove = -1; + } + } + + @Override + Iterator elementIterator() { + return new Itr() { + @Override + E output(int index) { + return enumConstants[index]; + } + }; + } + + @Override + Iterator> entryIterator() { + return new Itr>() { + @Override + Entry output(final int index) { + return new Multisets.AbstractEntry() { + @Override + public E getElement() { + return enumConstants[index]; + } + + @Override + public int getCount() { + return counts[index]; + } + }; + } + }; + } + + @Override + public void forEachEntry(ObjIntConsumer action) { + checkNotNull(action); + for (int i = 0; i < enumConstants.length; i++) { + if (counts[i] > 0) { + action.accept(enumConstants[i], counts[i]); + } + } + } + + @Override + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + @GwtIncompatible // java.io.ObjectOutputStream + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(type); + Serialization.writeMultiset(this, stream); + } + + /** + * @serialData the {@code Class} for the enum type, the number of distinct elements, the first + * element, its count, the second element, its count, and so on + */ + @GwtIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + @SuppressWarnings("unchecked") // reading data stored by writeObject + Class localType = (Class) stream.readObject(); + type = localType; + enumConstants = type.getEnumConstants(); + counts = new int[enumConstants.length]; + Serialization.populateMultiset(this, stream); + } + + @GwtIncompatible // Not needed in emulated source + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EvictingQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EvictingQueue.java new file mode 100644 index 0000000000000..5f91932d2112a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/EvictingQueue.java @@ -0,0 +1,145 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.Serializable; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Queue; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A non-blocking queue which automatically evicts elements from the head of the queue when + * attempting to add new elements onto the queue and it is full. This queue orders elements FIFO + * (first-in-first-out). This data structure is logically equivalent to a circular buffer (i.e., + * cyclic buffer or ring buffer). + * + *

An evicting queue must be configured with a maximum size. Each time an element is added to a + * full queue, the queue automatically removes its head element. This is different from conventional + * bounded queues, which either block or reject new elements when full. + * + *

This class is not thread-safe, and does not accept null elements. + * + * @author Kurt Alfred Kluever + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class EvictingQueue extends ForwardingQueue implements Serializable { + + private final Queue delegate; + + @VisibleForTesting final int maxSize; + + private EvictingQueue(int maxSize) { + checkArgument(maxSize >= 0, "maxSize (%s) must >= 0", maxSize); + this.delegate = new ArrayDeque<>(maxSize); + this.maxSize = maxSize; + } + + /** + * Creates and returns a new evicting queue that will hold up to {@code maxSize} elements. + * + *

When {@code maxSize} is zero, elements will be evicted immediately after being added to the + * queue. + */ + public static EvictingQueue create(int maxSize) { + return new EvictingQueue<>(maxSize); + } + + /** + * Returns the number of additional elements that this queue can accept without evicting; zero if + * the queue is currently full. + * + * @since 16.0 + */ + public int remainingCapacity() { + return maxSize - size(); + } + + @Override + protected Queue delegate() { + return delegate; + } + + /** + * Adds the given element to this queue. If the queue is currently full, the element at the head + * of the queue is evicted to make room. + * + * @return {@code true} always + */ + @Override + public boolean offer(E e) { + return add(e); + } + + /** + * Adds the given element to this queue. If the queue is currently full, the element at the head + * of the queue is evicted to make room. + * + * @return {@code true} always + */ + @Override + public boolean add(E e) { + checkNotNull(e); // check before removing + if (maxSize == 0) { + return true; + } + if (size() == maxSize) { + delegate.remove(); + } + delegate.add(e); + return true; + } + + @Override + public boolean addAll(Collection collection) { + int size = collection.size(); + if (size >= maxSize) { + clear(); + return Iterables.addAll(this, Iterables.skip(collection, size - maxSize)); + } + return standardAddAll(collection); + } + + @Override + @J2ktIncompatible // Incompatible return type change. Use inherited implementation + public Object[] toArray() { + /* + * If we could, we'd declare the no-arg `Collection.toArray()` to return "Object[] but elements + * have the same nullness as E." Since we can't, we declare it to return nullable elements, and + * we can override it in our non-null-guaranteeing subtypes to present a better signature to + * their users. + * + * However, the checker *we* use has this special knowledge about `Collection.toArray()` anyway, + * so in our implementation code, we can rely on that. That's why the expression below + * type-checks. + */ + return super.toArray(); + } + + private static final long serialVersionUID = 0L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ExplicitOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ExplicitOrdering.java new file mode 100644 index 0000000000000..ea860008a34ee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ExplicitOrdering.java @@ -0,0 +1,74 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.List; + +/** An ordering that compares objects according to a given order. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class ExplicitOrdering extends Ordering implements Serializable { + final ImmutableMap rankMap; + + ExplicitOrdering(List valuesInOrder) { + this(Maps.indexMap(valuesInOrder)); + } + + ExplicitOrdering(ImmutableMap rankMap) { + this.rankMap = rankMap; + } + + @Override + public int compare(T left, T right) { + return rank(left) - rank(right); // safe because both are nonnegative + } + + private int rank(T value) { + Integer rank = rankMap.get(value); + if (rank == null) { + throw new IncomparableValueException(value); + } + return rank; + } + + @Override + public boolean equals(Object object) { + if (object instanceof ExplicitOrdering) { + ExplicitOrdering that = (ExplicitOrdering) object; + return this.rankMap.equals(that.rankMap); + } + return false; + } + + @Override + public int hashCode() { + return rankMap.hashCode(); + } + + @Override + public String toString() { + return "Ordering.explicit(" + rankMap.keySet() + ")"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredEntryMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredEntryMultimap.java new file mode 100644 index 0000000000000..2822d19d8194a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredEntryMultimap.java @@ -0,0 +1,411 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.collect.Maps.ViewCachingAbstractMap; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Predicates.in; +import static org.apache.iotdb.google.common.base.Predicates.not; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Implementation of {@link Multimaps#filterEntries(Multimap, Predicate)}. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +class FilteredEntryMultimap extends AbstractMultimap + implements FilteredMultimap { + final Multimap unfiltered; + final Predicate> predicate; + + FilteredEntryMultimap(Multimap unfiltered, Predicate> predicate) { + this.unfiltered = checkNotNull(unfiltered); + this.predicate = checkNotNull(predicate); + } + + @Override + public Multimap unfiltered() { + return unfiltered; + } + + @Override + public Predicate> entryPredicate() { + return predicate; + } + + @Override + public int size() { + return entries().size(); + } + + private boolean satisfies(@ParametricNullness K key, @ParametricNullness V value) { + return predicate.apply(Maps.immutableEntry(key, value)); + } + + final class ValuePredicate implements Predicate { + @ParametricNullness private final K key; + + ValuePredicate(@ParametricNullness K key) { + this.key = key; + } + + @Override + public boolean apply(@ParametricNullness V value) { + return satisfies(key, value); + } + } + + static Collection filterCollection( + Collection collection, Predicate predicate) { + if (collection instanceof Set) { + return Sets.filter((Set) collection, predicate); + } else { + return Collections2.filter(collection, predicate); + } + } + + @Override + public boolean containsKey(Object key) { + return asMap().get(key) != null; + } + + @Override + public Collection removeAll(Object key) { + return MoreObjects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection()); + } + + Collection unmodifiableEmptyCollection() { + // These return false, rather than throwing a UOE, on remove calls. + return (unfiltered instanceof SetMultimap) + ? Collections.emptySet() + : Collections.emptyList(); + } + + @Override + public void clear() { + entries().clear(); + } + + @Override + public Collection get(@ParametricNullness K key) { + return filterCollection(unfiltered.get(key), new ValuePredicate(key)); + } + + @Override + Collection> createEntries() { + return filterCollection(unfiltered.entries(), predicate); + } + + @Override + Collection createValues() { + return new FilteredMultimapValues<>(this); + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Map> createAsMap() { + return new AsMap(); + } + + @Override + Set createKeySet() { + return asMap().keySet(); + } + + boolean removeEntriesIf(Predicate>> predicate) { + Iterator>> entryIterator = unfiltered.asMap().entrySet().iterator(); + boolean changed = false; + while (entryIterator.hasNext()) { + Entry> entry = entryIterator.next(); + K key = entry.getKey(); + Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key)); + if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) { + if (collection.size() == entry.getValue().size()) { + entryIterator.remove(); + } else { + collection.clear(); + } + changed = true; + } + } + return changed; + } + + class AsMap extends ViewCachingAbstractMap> { + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public void clear() { + FilteredEntryMultimap.this.clear(); + } + + @Override + public Collection get(Object key) { + Collection result = unfiltered.asMap().get(key); + if (result == null) { + return null; + } + @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself + K k = (K) key; + result = filterCollection(result, new ValuePredicate(k)); + return result.isEmpty() ? null : result; + } + + @Override + public Collection remove(Object key) { + Collection collection = unfiltered.asMap().get(key); + if (collection == null) { + return null; + } + @SuppressWarnings("unchecked") // it's definitely equal to a K + K k = (K) key; + List result = Lists.newArrayList(); + Iterator itr = collection.iterator(); + while (itr.hasNext()) { + V v = itr.next(); + if (satisfies(k, v)) { + itr.remove(); + result.add(v); + } + } + if (result.isEmpty()) { + return null; + } else if (unfiltered instanceof SetMultimap) { + return Collections.unmodifiableSet(Sets.newLinkedHashSet(result)); + } else { + return Collections.unmodifiableList(result); + } + } + + @Override + Set createKeySet() { + class KeySetImpl extends Maps.KeySet> { + KeySetImpl() { + super(AsMap.this); + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(Maps.keyPredicateOnEntries(in(c))); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(Maps.keyPredicateOnEntries(not(in(c)))); + } + + @Override + public boolean remove(Object o) { + return AsMap.this.remove(o) != null; + } + } + return new KeySetImpl(); + } + + @Override + Set>> createEntrySet() { + class EntrySetImpl extends Maps.EntrySet> { + @Override + Map> map() { + return AsMap.this; + } + + @Override + public Iterator>> iterator() { + return new AbstractIterator>>() { + final Iterator>> backingIterator = + unfiltered.asMap().entrySet().iterator(); + + @Override + protected Entry> computeNext() { + while (backingIterator.hasNext()) { + Entry> entry = backingIterator.next(); + K key = entry.getKey(); + Collection collection = + filterCollection(entry.getValue(), new ValuePredicate(key)); + if (!collection.isEmpty()) { + return Maps.immutableEntry(key, collection); + } + } + return endOfData(); + } + }; + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(in(c)); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(not(in(c))); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + } + return new EntrySetImpl(); + } + + @Override + Collection> createValues() { + class ValuesImpl extends Maps.Values> { + ValuesImpl() { + super(AsMap.this); + } + + @Override + public boolean remove(Object o) { + if (o instanceof Collection) { + Collection c = (Collection) o; + Iterator>> entryIterator = + unfiltered.asMap().entrySet().iterator(); + while (entryIterator.hasNext()) { + Entry> entry = entryIterator.next(); + K key = entry.getKey(); + Collection collection = + filterCollection(entry.getValue(), new ValuePredicate(key)); + if (!collection.isEmpty() && c.equals(collection)) { + if (collection.size() == entry.getValue().size()) { + entryIterator.remove(); + } else { + collection.clear(); + } + return true; + } + } + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(Maps.>valuePredicateOnEntries(in(c))); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(Maps.>valuePredicateOnEntries(not(in(c)))); + } + } + return new ValuesImpl(); + } + } + + @Override + Multiset createKeys() { + return new Keys(); + } + + class Keys extends Multimaps.Keys { + Keys() { + super(FilteredEntryMultimap.this); + } + + @Override + public int remove(Object key, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(key); + } + Collection collection = unfiltered.asMap().get(key); + if (collection == null) { + return 0; + } + @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself + K k = (K) key; + int oldCount = 0; + Iterator itr = collection.iterator(); + while (itr.hasNext()) { + V v = itr.next(); + if (satisfies(k, v)) { + oldCount++; + if (oldCount <= occurrences) { + itr.remove(); + } + } + } + return oldCount; + } + + @Override + public Set> entrySet() { + return new Multisets.EntrySet() { + + @Override + Multiset multiset() { + return Keys.this; + } + + @Override + public Iterator> iterator() { + return Keys.this.entryIterator(); + } + + @Override + public int size() { + return FilteredEntryMultimap.this.keySet().size(); + } + + private boolean removeEntriesIf(Predicate> predicate) { + return FilteredEntryMultimap.this.removeEntriesIf( + (Map.Entry> entry) -> + predicate.apply( + Multisets.immutableEntry(entry.getKey(), entry.getValue().size()))); + } + + @Override + public boolean removeAll(Collection c) { + return removeEntriesIf(in(c)); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntriesIf(not(in(c))); + } + }; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredEntrySetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredEntrySetMultimap.java new file mode 100644 index 0000000000000..17c3231dab0f6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredEntrySetMultimap.java @@ -0,0 +1,71 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Predicate; + +import java.util.Map.Entry; +import java.util.Set; + +/** + * Implementation of {@link Multimaps#filterEntries(SetMultimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class FilteredEntrySetMultimap + extends FilteredEntryMultimap implements FilteredSetMultimap { + + FilteredEntrySetMultimap(SetMultimap unfiltered, Predicate> predicate) { + super(unfiltered, predicate); + } + + @Override + public SetMultimap unfiltered() { + return (SetMultimap) unfiltered; + } + + @Override + public Set get(@ParametricNullness K key) { + return (Set) super.get(key); + } + + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + + @Override + Set> createEntries() { + return Sets.filter(unfiltered().entries(), entryPredicate()); + } + + @Override + public Set> entries() { + return (Set>) super.entries(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeyListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeyListMultimap.java new file mode 100644 index 0000000000000..9bdea14cf9447 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeyListMultimap.java @@ -0,0 +1,59 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Predicate; + +import java.util.List; + +/** + * Implementation of {@link Multimaps#filterKeys(ListMultimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class FilteredKeyListMultimap + extends FilteredKeyMultimap implements ListMultimap { + FilteredKeyListMultimap(ListMultimap unfiltered, Predicate keyPredicate) { + super(unfiltered, keyPredicate); + } + + @Override + public ListMultimap unfiltered() { + return (ListMultimap) super.unfiltered(); + } + + @Override + public List get(@ParametricNullness K key) { + return (List) super.get(key); + } + + @Override + public List removeAll(Object key) { + return (List) super.removeAll(key); + } + + @Override + public List replaceValues(@ParametricNullness K key, Iterable values) { + return (List) super.replaceValues(key, values); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeyMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeyMultimap.java new file mode 100644 index 0000000000000..4fc01ac36b36c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeyMultimap.java @@ -0,0 +1,225 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Predicate; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; + +/** + * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +class FilteredKeyMultimap extends AbstractMultimap + implements FilteredMultimap { + final Multimap unfiltered; + final Predicate keyPredicate; + + FilteredKeyMultimap(Multimap unfiltered, Predicate keyPredicate) { + this.unfiltered = checkNotNull(unfiltered); + this.keyPredicate = checkNotNull(keyPredicate); + } + + @Override + public Multimap unfiltered() { + return unfiltered; + } + + @Override + public Predicate> entryPredicate() { + return Maps.keyPredicateOnEntries(keyPredicate); + } + + @Override + public int size() { + int size = 0; + for (Collection collection : asMap().values()) { + size += collection.size(); + } + return size; + } + + @Override + public boolean containsKey(Object key) { + if (unfiltered.containsKey(key)) { + @SuppressWarnings("unchecked") // k is equal to a K, if not one itself + K k = (K) key; + return keyPredicate.apply(k); + } + return false; + } + + @Override + public Collection removeAll(Object key) { + return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection(); + } + + Collection unmodifiableEmptyCollection() { + if (unfiltered instanceof SetMultimap) { + return emptySet(); + } else { + return emptyList(); + } + } + + @Override + public void clear() { + keySet().clear(); + } + + @Override + Set createKeySet() { + return Sets.filter(unfiltered.keySet(), keyPredicate); + } + + @Override + public Collection get(@ParametricNullness K key) { + if (keyPredicate.apply(key)) { + return unfiltered.get(key); + } else if (unfiltered instanceof SetMultimap) { + return new AddRejectingSet<>(key); + } else { + return new AddRejectingList<>(key); + } + } + + static class AddRejectingSet extends ForwardingSet { + @ParametricNullness final K key; + + AddRejectingSet(@ParametricNullness K key) { + this.key = key; + } + + @Override + public boolean add(@ParametricNullness V element) { + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + public boolean addAll(Collection collection) { + checkNotNull(collection); + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + protected Set delegate() { + return Collections.emptySet(); + } + } + + static class AddRejectingList extends ForwardingList { + @ParametricNullness final K key; + + AddRejectingList(@ParametricNullness K key) { + this.key = key; + } + + @Override + public boolean add(@ParametricNullness V v) { + add(0, v); + return true; + } + + @Override + public void add(int index, @ParametricNullness V element) { + checkPositionIndex(index, 0); + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + public boolean addAll(Collection collection) { + addAll(0, collection); + return true; + } + + @Override + public boolean addAll(int index, Collection elements) { + checkNotNull(elements); + checkPositionIndex(index, 0); + throw new IllegalArgumentException("Key does not satisfy predicate: " + key); + } + + @Override + protected List delegate() { + return Collections.emptyList(); + } + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Collection> createEntries() { + return new Entries(); + } + + class Entries extends ForwardingCollection> { + @Override + protected Collection> delegate() { + return Collections2.filter(unfiltered.entries(), entryPredicate()); + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + if (unfiltered.containsKey(entry.getKey()) + // if this holds, then we know entry.getKey() is a K + && keyPredicate.apply((K) entry.getKey())) { + return unfiltered.remove(entry.getKey(), entry.getValue()); + } + } + return false; + } + } + + @Override + Collection createValues() { + return new FilteredMultimapValues<>(this); + } + + @Override + Map> createAsMap() { + return Maps.filterKeys(unfiltered.asMap(), keyPredicate); + } + + @Override + Multiset createKeys() { + return Multisets.filter(unfiltered.keys(), keyPredicate); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeySetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeySetMultimap.java new file mode 100644 index 0000000000000..3cda20b8d4169 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredKeySetMultimap.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Predicate; + +import java.util.Map.Entry; +import java.util.Set; + +/** + * Implementation of {@link Multimaps#filterKeys(SetMultimap, Predicate)}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class FilteredKeySetMultimap + extends FilteredKeyMultimap implements FilteredSetMultimap { + + FilteredKeySetMultimap(SetMultimap unfiltered, Predicate keyPredicate) { + super(unfiltered, keyPredicate); + } + + @Override + public SetMultimap unfiltered() { + return (SetMultimap) unfiltered; + } + + @Override + public Set get(@ParametricNullness K key) { + return (Set) super.get(key); + } + + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + return (Set) super.replaceValues(key, values); + } + + @Override + public Set> entries() { + return (Set>) super.entries(); + } + + @Override + Set> createEntries() { + return new EntrySet(); + } + + class EntrySet extends Entries implements Set> { + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(Object o) { + return Sets.equalsImpl(this, o); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredMultimap.java new file mode 100644 index 0000000000000..d0dbe049d1382 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredMultimap.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Predicate; + +import java.util.Map.Entry; + +/** + * An interface for all filtered multimap types. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +interface FilteredMultimap extends Multimap { + Multimap unfiltered(); + + Predicate> entryPredicate(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredMultimapValues.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredMultimapValues.java new file mode 100644 index 0000000000000..b36014fdb278e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredMultimapValues.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map.Entry; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation for {@link FilteredMultimap#values()}. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class FilteredMultimapValues + extends AbstractCollection { + private final FilteredMultimap multimap; + + FilteredMultimapValues(FilteredMultimap multimap) { + this.multimap = checkNotNull(multimap); + } + + @Override + public Iterator iterator() { + return Maps.valueIterator(multimap.entries().iterator()); + } + + @Override + public boolean contains(Object o) { + return multimap.containsValue(o); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public boolean remove(Object o) { + Predicate> entryPredicate = multimap.entryPredicate(); + for (Iterator> unfilteredItr = multimap.unfiltered().entries().iterator(); + unfilteredItr.hasNext(); ) { + Entry entry = unfilteredItr.next(); + if (entryPredicate.apply(entry) && Objects.equal(entry.getValue(), o)) { + unfilteredItr.remove(); + return true; + } + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + return Iterables.removeIf( + multimap.unfiltered().entries(), + // explicit > is required to build with JDK6 + Predicates.>and( + multimap.entryPredicate(), Maps.valuePredicateOnEntries(Predicates.in(c)))); + } + + @Override + public boolean retainAll(Collection c) { + return Iterables.removeIf( + multimap.unfiltered().entries(), + // explicit > is required to build with JDK6 + Predicates.>and( + multimap.entryPredicate(), + Maps.valuePredicateOnEntries(Predicates.not(Predicates.in(c))))); + } + + @Override + public void clear() { + multimap.clear(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredSetMultimap.java new file mode 100644 index 0000000000000..102a7b5698670 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FilteredSetMultimap.java @@ -0,0 +1,35 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * A supertype for filtered {@link SetMultimap} implementations. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +interface FilteredSetMultimap + extends FilteredMultimap, SetMultimap { + @Override + SetMultimap unfiltered(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FluentIterable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FluentIterable.java new file mode 100644 index 0000000000000..a55bd40cda0d4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/FluentIterable.java @@ -0,0 +1,857 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Predicate; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A discouraged (but not deprecated) precursor to Java's superior {@link Stream} library. + * + *

The following types of methods are provided: + * + *

    + *
  • chaining methods which return a new {@code FluentIterable} based in some way on the + * contents of the current one (for example {@link #transform}) + *
  • element extraction methods which facilitate the retrieval of certain elements (for example + * {@link #last}) + *
  • query methods which answer questions about the {@code FluentIterable}'s contents (for + * example {@link #anyMatch}) + *
  • conversion methods which copy the {@code FluentIterable}'s contents into a new collection + * or array (for example {@link #toList}) + *
+ * + *

Several lesser-used features are currently available only as static methods on the {@link + * Iterables} class. + * + *

+ * + *

Comparison to streams

+ * + *

{@link Stream} is similar to this class, but generally more powerful, and certainly more + * standard. Key differences include: + * + *

    + *
  • A stream is single-use; it becomes invalid as soon as any "terminal operation" such + * as {@code findFirst()} or {@code iterator()} is invoked. (Even though {@code Stream} + * contains all the right method signatures to implement {@link Iterable}, it does not + * actually do so, to avoid implying repeat-iterability.) {@code FluentIterable}, on the other + * hand, is multiple-use, and does implement {@link Iterable}. + *
  • Streams offer many features not found here, including {@code min/max}, {@code distinct}, + * {@code reduce}, {@code sorted}, the very powerful {@code collect}, and built-in support for + * parallelizing stream operations. + *
  • {@code FluentIterable} contains several features not available on {@code Stream}, which are + * noted in the method descriptions below. + *
  • Streams include primitive-specialized variants such as {@code IntStream}, the use of which + * is strongly recommended. + *
  • Streams are standard Java, not requiring a third-party dependency. + *
+ * + *

Example

+ * + *

Here is an example that accepts a list from a database call, filters it based on a predicate, + * transforms it by invoking {@code toString()} on each element, and returns the first 10 elements + * as a {@code List}: + * + *

{@code
+ * ImmutableList results =
+ *     FluentIterable.from(database.getClientList())
+ *         .filter(Client::isActiveInLastMonth)
+ *         .transform(Object::toString)
+ *         .limit(10)
+ *         .toList();
+ * }
+ * + * The approximate stream equivalent is: + * + *
{@code
+ * List results =
+ *     database.getClientList()
+ *         .stream()
+ *         .filter(Client::isActiveInLastMonth)
+ *         .map(Object::toString)
+ *         .limit(10)
+ *         .collect(Collectors.toList());
+ * }
+ * + * @author Marcin Mikosik + * @since 12.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class FluentIterable implements Iterable { + // We store 'iterable' and use it instead of 'this' to allow Iterables to perform instanceof + // checks on the _original_ iterable when FluentIterable.from is used. + // To avoid a self retain cycle under j2objc, we store Optional.absent() instead of + // Optional.of(this). To access the delegate iterable, call #getDelegate(), which converts to + // absent() back to 'this'. + private final Optional> iterableDelegate; + + /** Constructor for use by subclasses. */ + protected FluentIterable() { + this.iterableDelegate = Optional.absent(); + } + + FluentIterable(Iterable iterable) { + this.iterableDelegate = Optional.of(iterable); + } + + private Iterable getDelegate() { + return iterableDelegate.or(this); + } + + /** + * Returns a fluent iterable that wraps {@code iterable}, or {@code iterable} itself if it is + * already a {@code FluentIterable}. + * + *

{@code Stream} equivalent: {@link Collection#stream} if {@code iterable} is a {@link + * Collection}; {@link Streams#stream(Iterable)} otherwise. + */ + public static FluentIterable from(final Iterable iterable) { + return (iterable instanceof FluentIterable) + ? (FluentIterable) iterable + : new FluentIterable(iterable) { + @Override + public Iterator iterator() { + return iterable.iterator(); + } + }; + } + + /** + * Returns a fluent iterable containing {@code elements} in the specified order. + * + *

The returned iterable is an unmodifiable view of the input array. + * + *

{@code Stream} equivalent: {@link java.util.stream.Stream#of(Object[]) + * Stream.of(T...)}. + * + * @since 20.0 (since 18.0 as an overload of {@code of}) + */ + public static FluentIterable from(E[] elements) { + return from(Arrays.asList(elements)); + } + + /** + * Construct a fluent iterable from another fluent iterable. This is obviously never necessary, + * but is intended to help call out cases where one migration from {@code Iterable} to {@code + * FluentIterable} has obviated the need to explicitly convert to a {@code FluentIterable}. + * + * @deprecated instances of {@code FluentIterable} don't need to be converted to {@code + * FluentIterable} + */ + @Deprecated + public static FluentIterable from(FluentIterable iterable) { + return checkNotNull(iterable); + } + + /** + * Returns a fluent iterable that combines two iterables. The returned iterable has an iterator + * that traverses the elements in {@code a}, followed by the elements in {@code b}. The source + * iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

{@code Stream} equivalent: {@link Stream#concat}. + * + * @since 20.0 + */ + public static FluentIterable concat( + Iterable a, Iterable b) { + return concatNoDefensiveCopy(a, b); + } + + /** + * Returns a fluent iterable that combines three iterables. The returned iterable has an iterator + * that traverses the elements in {@code a}, followed by the elements in {@code b}, followed by + * the elements in {@code c}. The source iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

{@code Stream} equivalent: use nested calls to {@link Stream#concat}, or see the + * advice in {@link #concat(Iterable...)}. + * + * @since 20.0 + */ + public static FluentIterable concat( + Iterable a, Iterable b, Iterable c) { + return concatNoDefensiveCopy(a, b, c); + } + + /** + * Returns a fluent iterable that combines four iterables. The returned iterable has an iterator + * that traverses the elements in {@code a}, followed by the elements in {@code b}, followed by + * the elements in {@code c}, followed by the elements in {@code d}. The source iterators are not + * polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

{@code Stream} equivalent: use nested calls to {@link Stream#concat}, or see the + * advice in {@link #concat(Iterable...)}. + * + * @since 20.0 + */ + public static FluentIterable concat( + Iterable a, + Iterable b, + Iterable c, + Iterable d) { + return concatNoDefensiveCopy(a, b, c, d); + } + + /** + * Returns a fluent iterable that combines several iterables. The returned iterable has an + * iterator that traverses the elements of each iterable in {@code inputs}. The input iterators + * are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

{@code Stream} equivalent: to concatenate an arbitrary number of streams, use {@code + * Stream.of(stream1, stream2, ...).flatMap(s -> s)}. If the sources are iterables, use {@code + * Stream.of(iter1, iter2, ...).flatMap(Streams::stream)}. + * + * @throws NullPointerException if any of the provided iterables is {@code null} + * @since 20.0 + */ + public static FluentIterable concat(Iterable... inputs) { + return concatNoDefensiveCopy(Arrays.copyOf(inputs, inputs.length)); + } + + /** + * Returns a fluent iterable that combines several iterables. The returned iterable has an + * iterator that traverses the elements of each iterable in {@code inputs}. The input iterators + * are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. The methods of the returned iterable may throw {@code + * NullPointerException} if any of the input iterators is {@code null}. + * + *

{@code Stream} equivalent: {@code streamOfStreams.flatMap(s -> s)} or {@code + * streamOfIterables.flatMap(Streams::stream)}. (See {@link Streams#stream}.) + * + * @since 20.0 + */ + public static FluentIterable concat( + final Iterable> inputs) { + checkNotNull(inputs); + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.concat(Iterators.transform(inputs.iterator(), Iterable::iterator)); + } + }; + } + + /** Concatenates a varargs array of iterables without making a defensive copy of the array. */ + private static FluentIterable concatNoDefensiveCopy( + final Iterable... inputs) { + for (Iterable input : inputs) { + checkNotNull(input); + } + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.concat( + /* lazily generate the iterators on each input only as needed */ + new AbstractIndexedListIterator>(inputs.length) { + @Override + public Iterator get(int i) { + return inputs[i].iterator(); + } + }); + } + }; + } + + /** + * Returns a fluent iterable containing no elements. + * + *

{@code Stream} equivalent: {@link Stream#empty}. + * + * @since 20.0 + */ + public static FluentIterable of() { + return FluentIterable.from(Collections.emptyList()); + } + + /** + * Returns a fluent iterable containing the specified elements in order. + * + *

{@code Stream} equivalent: {@link java.util.stream.Stream#of(Object[]) + * Stream.of(T...)}. + * + * @since 20.0 + */ + public static FluentIterable of( + @ParametricNullness E element, E... elements) { + return from(Lists.asList(element, elements)); + } + + /** + * Returns a string representation of this fluent iterable, with the format {@code [e1, e2, ..., + * en]}. + * + *

{@code Stream} equivalent: {@code stream.collect(Collectors.joining(", ", "[", "]"))} + * or (less efficiently) {@code stream.collect(Collectors.toList()).toString()}. + */ + @Override + public String toString() { + return Iterables.toString(getDelegate()); + } + + /** + * Returns the number of elements in this fluent iterable. + * + *

{@code Stream} equivalent: {@link Stream#count}. + */ + public final int size() { + return Iterables.size(getDelegate()); + } + + /** + * Returns {@code true} if this fluent iterable contains any object for which {@code + * equals(target)} is true. + * + *

{@code Stream} equivalent: {@code stream.anyMatch(Predicate.isEqual(target))}. + */ + public final boolean contains(Object target) { + return Iterables.contains(getDelegate(), target); + } + + /** + * Returns a fluent iterable whose {@code Iterator} cycles indefinitely over the elements of this + * fluent iterable. + * + *

That iterator supports {@code remove()} if {@code iterable.iterator()} does. After {@code + * remove()} is called, subsequent cycles omit the removed element, which is no longer in this + * fluent iterable. The iterator's {@code hasNext()} method returns {@code true} until this fluent + * iterable is empty. + * + *

Warning: Typical uses of the resulting iterator may produce an infinite loop. You + * should use an explicit {@code break} or be certain that you will eventually remove all the + * elements. + * + *

{@code Stream} equivalent: if the source iterable has only a single element {@code + * e}, use {@code Stream.generate(() -> e)}. Otherwise, collect your stream into a collection and + * use {@code Stream.generate(() -> collection).flatMap(Collection::stream)}. + */ + public final FluentIterable cycle() { + return from(Iterables.cycle(getDelegate())); + } + + /** + * Returns a fluent iterable whose iterators traverse first the elements of this fluent iterable, + * followed by those of {@code other}. The iterators are not polled until necessary. + * + *

The returned iterable's {@code Iterator} supports {@code remove()} when the corresponding + * {@code Iterator} supports it. + * + *

{@code Stream} equivalent: {@link Stream#concat}. + * + * @since 18.0 + */ + public final FluentIterable append(Iterable other) { + return FluentIterable.concat(getDelegate(), other); + } + + /** + * Returns a fluent iterable whose iterators traverse first the elements of this fluent iterable, + * followed by {@code elements}. + * + *

{@code Stream} equivalent: {@code Stream.concat(thisStream, Stream.of(elements))}. + * + * @since 18.0 + */ + public final FluentIterable append(E... elements) { + return FluentIterable.concat(getDelegate(), Arrays.asList(elements)); + } + + /** + * Returns the elements from this fluent iterable that satisfy a predicate. The resulting fluent + * iterable's iterator does not support {@code remove()}. + * + *

{@code Stream} equivalent: {@link Stream#filter} (same). + */ + public final FluentIterable filter(Predicate predicate) { + return from(Iterables.filter(getDelegate(), predicate)); + } + + /** + * Returns the elements from this fluent iterable that are instances of class {@code type}. + * + *

{@code Stream} equivalent: {@code stream.filter(type::isInstance).map(type::cast)}. + * This does perform a little more work than necessary, so another option is to insert an + * unchecked cast at some later point: + * + *

+   * {@code @SuppressWarnings("unchecked") // safe because of ::isInstance check
+   * ImmutableList result =
+   *     (ImmutableList) stream.filter(NewType.class::isInstance).collect(toImmutableList());}
+   * 
+ */ + @GwtIncompatible // Class.isInstance + public final FluentIterable filter(Class type) { + return from(Iterables.filter(getDelegate(), type)); + } + + /** + * Returns {@code true} if any element in this fluent iterable satisfies the predicate. + * + *

{@code Stream} equivalent: {@link Stream#anyMatch} (same). + */ + public final boolean anyMatch(Predicate predicate) { + return Iterables.any(getDelegate(), predicate); + } + + /** + * Returns {@code true} if every element in this fluent iterable satisfies the predicate. If this + * fluent iterable is empty, {@code true} is returned. + * + *

{@code Stream} equivalent: {@link Stream#allMatch} (same). + */ + public final boolean allMatch(Predicate predicate) { + return Iterables.all(getDelegate(), predicate); + } + + /** + * Returns an {@link Optional} containing the first element in this fluent iterable that satisfies + * the given predicate, if such an element exists. + * + *

Warning: avoid using a {@code predicate} that matches {@code null}. If {@code null} + * is matched in this fluent iterable, a {@link NullPointerException} will be thrown. + * + *

{@code Stream} equivalent: {@code stream.filter(predicate).findFirst()}. + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final Optional firstMatch(Predicate predicate) { + return Iterables.tryFind((Iterable) getDelegate(), predicate); + } + + /** + * Returns a fluent iterable that applies {@code function} to each element of this fluent + * iterable. + * + *

The returned fluent iterable's iterator supports {@code remove()} if this iterable's + * iterator does. After a successful {@code remove()} call, this fluent iterable no longer + * contains the corresponding element. + * + *

{@code Stream} equivalent: {@link Stream#map}. + */ + public final FluentIterable transform(Function function) { + return from(Iterables.transform(getDelegate(), function)); + } + + /** + * Applies {@code function} to each element of this fluent iterable and returns a fluent iterable + * with the concatenated combination of results. {@code function} returns an Iterable of results. + * + *

The returned fluent iterable's iterator supports {@code remove()} if this function-returned + * iterables' iterator does. After a successful {@code remove()} call, the returned fluent + * iterable no longer contains the corresponding element. + * + *

{@code Stream} equivalent: {@link Stream#flatMap} (using a function that produces + * streams, not iterables). + * + * @since 13.0 (required {@code Function>} until 14.0) + */ + public FluentIterable transformAndConcat( + Function> function) { + return FluentIterable.concat(transform(function)); + } + + /** + * Returns an {@link Optional} containing the first element in this fluent iterable. If the + * iterable is empty, {@code Optional.absent()} is returned. + * + *

{@code Stream} equivalent: if the goal is to obtain any element, {@link + * Stream#findAny}; if it must specifically be the first element, {@code Stream#findFirst}. + * + * @throws NullPointerException if the first element is null; if this is a possibility, use {@code + * iterator().next()} or {@link Iterables#getFirst} instead. + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final Optional first() { + Iterator iterator = getDelegate().iterator(); + return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.absent(); + } + + /** + * Returns an {@link Optional} containing the last element in this fluent iterable. If the + * iterable is empty, {@code Optional.absent()} is returned. If the underlying {@code iterable} is + * a {@link List} with {@link java.util.RandomAccess} support, then this operation is guaranteed + * to be {@code O(1)}. + * + *

{@code Stream} equivalent: {@code stream.reduce((a, b) -> b)}. + * + * @throws NullPointerException if the last element is null; if this is a possibility, use {@link + * Iterables#getLast} instead. + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final Optional last() { + // Iterables#getLast was inlined here so we don't have to throw/catch a NSEE + + // TODO(kevinb): Support a concurrently modified collection? + Iterable iterable = getDelegate(); + if (iterable instanceof List) { + List list = (List) iterable; + if (list.isEmpty()) { + return Optional.absent(); + } + return Optional.of(list.get(list.size() - 1)); + } + Iterator iterator = iterable.iterator(); + if (!iterator.hasNext()) { + return Optional.absent(); + } + + /* + * TODO(kevinb): consider whether this "optimization" is worthwhile. Users with SortedSets tend + * to know they are SortedSets and probably would not call this method. + */ + if (iterable instanceof SortedSet) { + SortedSet sortedSet = (SortedSet) iterable; + return Optional.of(sortedSet.last()); + } + + while (true) { + E current = iterator.next(); + if (!iterator.hasNext()) { + return Optional.of(current); + } + } + } + + /** + * Returns a view of this fluent iterable that skips its first {@code numberToSkip} elements. If + * this fluent iterable contains fewer than {@code numberToSkip} elements, the returned fluent + * iterable skips all of its elements. + * + *

Modifications to this fluent iterable before a call to {@code iterator()} are reflected in + * the returned fluent iterable. That is, the iterator skips the first {@code numberToSkip} + * elements that exist when the iterator is created, not when {@code skip()} is called. + * + *

The returned fluent iterable's iterator supports {@code remove()} if the {@code Iterator} of + * this fluent iterable supports it. Note that it is not possible to delete the last + * skipped element by immediately calling {@code remove()} on the returned fluent iterable's + * iterator, as the {@code Iterator} contract states that a call to {@code * remove()} before a + * call to {@code next()} will throw an {@link IllegalStateException}. + * + *

{@code Stream} equivalent: {@link Stream#skip} (same). + */ + public final FluentIterable skip(int numberToSkip) { + return from(Iterables.skip(getDelegate(), numberToSkip)); + } + + /** + * Creates a fluent iterable with the first {@code size} elements of this fluent iterable. If this + * fluent iterable does not contain that many elements, the returned fluent iterable will have the + * same behavior as this fluent iterable. The returned fluent iterable's iterator supports {@code + * remove()} if this fluent iterable's iterator does. + * + *

{@code Stream} equivalent: {@link Stream#limit} (same). + * + * @param maxSize the maximum number of elements in the returned fluent iterable + * @throws IllegalArgumentException if {@code size} is negative + */ + public final FluentIterable limit(int maxSize) { + return from(Iterables.limit(getDelegate(), maxSize)); + } + + /** + * Determines whether this fluent iterable is empty. + * + *

{@code Stream} equivalent: {@code !stream.findAny().isPresent()}. + */ + public final boolean isEmpty() { + return !getDelegate().iterator().hasNext(); + } + + /** + * Returns an {@code ImmutableList} containing all of the elements from this fluent iterable in + * proper sequence. + * + *

{@code Stream} equivalent: pass {@link ImmutableList#toImmutableList} to {@code + * stream.collect()}. + * + * @throws NullPointerException if any element is {@code null} + * @since 14.0 (since 12.0 as {@code toImmutableList()}). + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableList toList() { + return ImmutableList.copyOf((Iterable) getDelegate()); + } + + /** + * Returns an {@code ImmutableList} containing all of the elements from this {@code + * FluentIterable} in the order specified by {@code comparator}. To produce an {@code + * ImmutableList} sorted by its natural ordering, use {@code toSortedList(Ordering.natural())}. + * + *

{@code Stream} equivalent: pass {@link ImmutableList#toImmutableList} to {@code + * stream.sorted(comparator).collect()}. + * + * @param comparator the function by which to sort list elements + * @throws NullPointerException if any element of this iterable is {@code null} + * @since 14.0 (since 13.0 as {@code toSortedImmutableList()}). + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableList toSortedList(Comparator comparator) { + return Ordering.from(comparator).immutableSortedCopy((Iterable) getDelegate()); + } + + /** + * Returns an {@code ImmutableSet} containing all of the elements from this fluent iterable with + * duplicates removed. + * + *

{@code Stream} equivalent: pass {@link ImmutableSet#toImmutableSet} to {@code + * stream.collect()}. + * + * @throws NullPointerException if any element is {@code null} + * @since 14.0 (since 12.0 as {@code toImmutableSet()}). + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableSet toSet() { + return ImmutableSet.copyOf((Iterable) getDelegate()); + } + + /** + * Returns an {@code ImmutableSortedSet} containing all of the elements from this {@code + * FluentIterable} in the order specified by {@code comparator}, with duplicates (determined by + * {@code comparator.compare(x, y) == 0}) removed. To produce an {@code ImmutableSortedSet} sorted + * by its natural ordering, use {@code toSortedSet(Ordering.natural())}. + * + *

{@code Stream} equivalent: pass {@link ImmutableSortedSet#toImmutableSortedSet} to + * {@code stream.collect()}. + * + * @param comparator the function by which to sort set elements + * @throws NullPointerException if any element of this iterable is {@code null} + * @since 14.0 (since 12.0 as {@code toImmutableSortedSet()}). + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableSortedSet toSortedSet(Comparator comparator) { + return ImmutableSortedSet.copyOf(comparator, (Iterable) getDelegate()); + } + + /** + * Returns an {@code ImmutableMultiset} containing all of the elements from this fluent iterable. + * + *

{@code Stream} equivalent: pass {@link ImmutableMultiset#toImmutableMultiset} to + * {@code stream.collect()}. + * + * @throws NullPointerException if any element is null + * @since 19.0 + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableMultiset toMultiset() { + return ImmutableMultiset.copyOf((Iterable) getDelegate()); + } + + /** + * Returns an immutable map whose keys are the distinct elements of this {@code FluentIterable} + * and whose value for each key was computed by {@code valueFunction}. The map's iteration order + * is the order of the first appearance of each key in this iterable. + * + *

When there are multiple instances of a key in this iterable, it is unspecified whether + * {@code valueFunction} will be applied to more than one instance of that key and, if it is, + * which result will be mapped to that key in the returned map. + * + *

{@code Stream} equivalent: {@code stream.collect(ImmutableMap.toImmutableMap(k -> k, + * valueFunction))}. + * + * @throws NullPointerException if any element of this iterable is {@code null}, or if {@code + * valueFunction} produces {@code null} for any key + * @since 14.0 + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableMap toMap(Function valueFunction) { + return Maps.toMap((Iterable) getDelegate(), valueFunction); + } + + /** + * Creates an index {@code ImmutableListMultimap} that contains the results of applying a + * specified function to each item in this {@code FluentIterable} of values. Each element of this + * iterable will be stored as a value in the resulting multimap, yielding a multimap with the same + * size as this iterable. The key used to store that value in the multimap will be the result of + * calling the function on that value. The resulting multimap is created as an immutable snapshot. + * In the returned multimap, keys appear in the order they are first encountered, and the values + * corresponding to each key appear in the same order as they are encountered. + * + *

{@code Stream} equivalent: {@code stream.collect(Collectors.groupingBy(keyFunction))} + * behaves similarly, but returns a mutable {@code Map>} instead, and may not preserve + * the order of entries. + * + * @param keyFunction the function used to produce the key for each value + * @throws NullPointerException if any element of this iterable is {@code null}, or if {@code + * keyFunction} produces {@code null} for any key + * @since 14.0 + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableListMultimap index(Function keyFunction) { + return Multimaps.index((Iterable) getDelegate(), keyFunction); + } + + /** + * Returns a map with the contents of this {@code FluentIterable} as its {@code values}, indexed + * by keys derived from those values. In other words, each input value produces an entry in the + * map whose key is the result of applying {@code keyFunction} to that value. These entries appear + * in the same order as they appeared in this fluent iterable. Example usage: + * + *

{@code
+   * Color red = new Color("red", 255, 0, 0);
+   * ...
+   * FluentIterable allColors = FluentIterable.from(ImmutableSet.of(red, green, blue));
+   *
+   * Map colorForName = allColors.uniqueIndex(toStringFunction());
+   * assertThat(colorForName).containsEntry("red", red);
+   * }
+ * + *

If your index may associate multiple values with each key, use {@link #index(Function) + * index}. + * + *

{@code Stream} equivalent: {@code + * stream.collect(ImmutableMap.toImmutableMap(keyFunction, v -> v))}. + * + * @param keyFunction the function used to produce the key for each value + * @return a map mapping the result of evaluating the function {@code keyFunction} on each value + * in this fluent iterable to that value + * @throws IllegalArgumentException if {@code keyFunction} produces the same key for more than one + * value in this fluent iterable + * @throws NullPointerException if any element of this iterable is {@code null}, or if {@code + * keyFunction} produces {@code null} for any key + * @since 14.0 + */ + @SuppressWarnings("nullness") // Unsafe, but we can't do much about it now. + public final ImmutableMap uniqueIndex(Function keyFunction) { + return Maps.uniqueIndex((Iterable) getDelegate(), keyFunction); + } + + /** + * Returns an array containing all of the elements from this fluent iterable in iteration order. + * + *

{@code Stream} equivalent: if an object array is acceptable, use {@code + * stream.toArray()}; if {@code type} is a class literal such as {@code MyType.class}, use {@code + * stream.toArray(MyType[]::new)}. Otherwise use {@code stream.toArray( len -> (E[]) + * Array.newInstance(type, len))}. + * + * @param type the type of the elements + * @return a newly-allocated array into which all the elements of this fluent iterable have been + * copied + */ + @GwtIncompatible // Array.newArray(Class, int) + public final E[] toArray(Class type) { + return Iterables.toArray(getDelegate(), type); + } + + /** + * Copies all the elements from this fluent iterable to {@code collection}. This is equivalent to + * calling {@code Iterables.addAll(collection, this)}. + * + *

{@code Stream} equivalent: {@code stream.forEachOrdered(collection::add)} or {@code + * stream.forEach(collection::add)}. + * + * @param collection the collection to copy elements to + * @return {@code collection}, for convenience + * @since 14.0 + */ + public final > C copyInto(C collection) { + checkNotNull(collection); + Iterable iterable = getDelegate(); + if (iterable instanceof Collection) { + collection.addAll((Collection) iterable); + } else { + for (E item : iterable) { + collection.add(item); + } + } + return collection; + } + + /** + * Returns a {@link String} containing all of the elements of this fluent iterable joined with + * {@code joiner}. + * + *

{@code Stream} equivalent: {@code joiner.join(stream.iterator())}, or, if you are not + * using any optional {@code Joiner} features, {@code + * stream.collect(Collectors.joining(delimiter)}. + * + * @since 18.0 + */ + public final String join(Joiner joiner) { + return joiner.join(this); + } + + /** + * Returns the element at the specified position in this fluent iterable. + * + *

{@code Stream} equivalent: {@code stream.skip(position).findFirst().get()} (but note + * that this throws different exception types, and throws an exception if {@code null} would be + * returned). + * + * @param position position of the element to return + * @return the element at the specified position in this fluent iterable + * @throws IndexOutOfBoundsException if {@code position} is negative or greater than or equal to + * the size of this fluent iterable + */ + @ParametricNullness + public final E get(int position) { + return Iterables.get(getDelegate(), position); + } + + /** + * Returns a stream of this fluent iterable's contents (similar to calling {@link + * Collection#stream} on a collection). + * + *

Note: the earlier in the chain you can switch to {@code Stream} usage (ideally not + * going through {@code FluentIterable} at all), the more performant and idiomatic your code will + * be. This method is a transitional aid, to be used only when really necessary. + * + * @since 21.0 + */ + public final Stream stream() { + return Streams.stream(getDelegate()); + } + + /** Function that transforms {@code Iterable} into a fluent iterable. */ + private static class FromIterableFunction + implements Function, FluentIterable> { + @Override + public FluentIterable apply(Iterable fromObject) { + return FluentIterable.from(fromObject); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingBlockingDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingBlockingDeque.java new file mode 100644 index 0000000000000..5db5cbfb8e67a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingBlockingDeque.java @@ -0,0 +1,140 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.Collection; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.TimeUnit; + +/** + * A {@link BlockingDeque} which forwards all its method calls to another {@code BlockingDeque}. + * Subclasses should override one or more methods to modify the behavior of the backing deque as + * desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingBlockingDeque} forward indiscriminately + * to the methods of the delegate. For example, overriding {@link #add} alone will not change + * the behaviour of {@link #offer} which can lead to unexpected behaviour. In this case, you should + * override {@code offer} as well, either providing your own implementation, or delegating to the + * provided {@code standardOffer} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingBlockingDeque}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even when all of the + * methods that they depend on are thread-safe. + * + * @author Emily Soldal + * @since 14.0 + * @deprecated This class has moved to {@code org.apache.iotdb.google.common.util.concurrent}. + * Please use {@link org.apache.iotdb.google.common.util.concurrent.ForwardingBlockingDeque} + * instead. + */ +@Deprecated +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingBlockingDeque extends ForwardingDeque + implements BlockingDeque { + + /** Constructor for use by subclasses. */ + protected ForwardingBlockingDeque() {} + + @Override + protected abstract BlockingDeque delegate(); + + @Override + public int remainingCapacity() { + return delegate().remainingCapacity(); + } + + @Override + public void putFirst(E e) throws InterruptedException { + delegate().putFirst(e); + } + + @Override + public void putLast(E e) throws InterruptedException { + delegate().putLast(e); + } + + @Override + public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offerFirst(e, timeout, unit); + } + + @Override + public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offerLast(e, timeout, unit); + } + + @Override + public E takeFirst() throws InterruptedException { + return delegate().takeFirst(); + } + + @Override + public E takeLast() throws InterruptedException { + return delegate().takeLast(); + } + + @Override + public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().pollFirst(timeout, unit); + } + + @Override + public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().pollLast(timeout, unit); + } + + @Override + public void put(E e) throws InterruptedException { + delegate().put(e); + } + + @Override + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offer(e, timeout, unit); + } + + @Override + public E take() throws InterruptedException { + return delegate().take(); + } + + @Override + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().poll(timeout, unit); + } + + @Override + public int drainTo(Collection c) { + return delegate().drainTo(c); + } + + @Override + public int drainTo(Collection c, int maxElements) { + return delegate().drainTo(c, maxElements); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingCollection.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingCollection.java new file mode 100644 index 0000000000000..26ab62757b7ff --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingCollection.java @@ -0,0 +1,255 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A collection which forwards all its method calls to another collection. Subclasses should + * override one or more methods to modify the behavior of the backing collection as desired per the + * decorator pattern. + * + *

Warning: The methods of {@code ForwardingCollection} forward indiscriminately to + * the methods of the delegate. For example, overriding {@link #add} alone will not change + * the behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardAddAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingCollection}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even when all of the + * methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingCollection extends ForwardingObject + implements Collection { + // TODO(lowasser): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingCollection() {} + + @Override + protected abstract Collection delegate(); + + @Override + public Iterator iterator() { + return delegate().iterator(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public boolean removeAll(Collection collection) { + return delegate().removeAll(collection); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public boolean contains(Object object) { + return delegate().contains(object); + } + + @Override + public boolean add(@ParametricNullness E element) { + return delegate().add(element); + } + + @Override + public boolean remove(Object object) { + return delegate().remove(object); + } + + @Override + public boolean containsAll(Collection collection) { + return delegate().containsAll(collection); + } + + @Override + public boolean addAll(Collection collection) { + return delegate().addAll(collection); + } + + @Override + public boolean retainAll(Collection collection) { + return delegate().retainAll(collection); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public Object[] toArray() { + return delegate().toArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return delegate().toArray(array); + } + + /** + * A sensible definition of {@link #contains} in terms of {@link #iterator}. If you override + * {@link #iterator}, you may wish to override {@link #contains} to forward to this + * implementation. + * + * @since 7.0 + */ + protected boolean standardContains(Object object) { + return Iterators.contains(iterator(), object); + } + + /** + * A sensible definition of {@link #containsAll} in terms of {@link #contains} . If you override + * {@link #contains}, you may wish to override {@link #containsAll} to forward to this + * implementation. + * + * @since 7.0 + */ + protected boolean standardContainsAll(Collection collection) { + return Collections2.containsAllImpl(this, collection); + } + + /** + * A sensible definition of {@link #addAll} in terms of {@link #add}. If you override {@link + * #add}, you may wish to override {@link #addAll} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardAddAll(Collection collection) { + return Iterators.addAll(this, collection.iterator()); + } + + /** + * A sensible definition of {@link #remove} in terms of {@link #iterator}, using the iterator's + * {@code remove} method. If you override {@link #iterator}, you may wish to override {@link + * #remove} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardRemove(Object object) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + if (Objects.equal(iterator.next(), object)) { + iterator.remove(); + return true; + } + } + return false; + } + + /** + * A sensible definition of {@link #removeAll} in terms of {@link #iterator}, using the iterator's + * {@code remove} method. If you override {@link #iterator}, you may wish to override {@link + * #removeAll} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardRemoveAll(Collection collection) { + return Iterators.removeAll(iterator(), collection); + } + + /** + * A sensible definition of {@link #retainAll} in terms of {@link #iterator}, using the iterator's + * {@code remove} method. If you override {@link #iterator}, you may wish to override {@link + * #retainAll} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardRetainAll(Collection collection) { + return Iterators.retainAll(iterator(), collection); + } + + /** + * A sensible definition of {@link #clear} in terms of {@link #iterator}, using the iterator's + * {@code remove} method. If you override {@link #iterator}, you may wish to override {@link + * #clear} to forward to this implementation. + * + * @since 7.0 + */ + protected void standardClear() { + Iterators.clear(iterator()); + } + + /** + * A sensible definition of {@link #isEmpty} as {@code !iterator().hasNext}. If you override + * {@link #isEmpty}, you may wish to override {@link #isEmpty} to forward to this implementation. + * Alternately, it may be more efficient to implement {@code isEmpty} as {@code size() == 0}. + * + * @since 7.0 + */ + protected boolean standardIsEmpty() { + return !iterator().hasNext(); + } + + /** + * A sensible definition of {@link #toString} in terms of {@link #iterator}. If you override + * {@link #iterator}, you may wish to override {@link #toString} to forward to this + * implementation. + * + * @since 7.0 + */ + protected String standardToString() { + return Collections2.toStringImpl(this); + } + + /** + * A sensible definition of {@link #toArray()} in terms of {@link #toArray(Object[])}. If you + * override {@link #toArray(Object[])}, you may wish to override {@link #toArray} to forward to + * this implementation. + * + * @since 7.0 + */ + protected Object[] standardToArray() { + Object[] newArray = new Object[size()]; + return toArray(newArray); + } + + /** + * A sensible definition of {@link #toArray(Object[])} in terms of {@link #size} and {@link + * #iterator}. If you override either of these methods, you may wish to override {@link #toArray} + * to forward to this implementation. + * + * @since 7.0 + */ + protected T[] standardToArray(T[] array) { + return ObjectArrays.toArrayImpl(this, array); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingConcurrentMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingConcurrentMap.java new file mode 100644 index 0000000000000..9a5ecf779b201 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingConcurrentMap.java @@ -0,0 +1,71 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.ConcurrentMap; + +/** + * A concurrent map which forwards all its method calls to another concurrent map. Subclasses should + * override one or more methods to modify the behavior of the backing map as desired per the decorator pattern. + * + *

{@code default} method warning: This class forwards calls to only some {@code + * default} methods. Specifically, it forwards calls only for methods that existed before + * {@code default} methods were introduced. For newer methods, like {@code forEach}, it inherits + * their default implementations. When those implementations invoke methods, they invoke methods on + * the {@code ForwardingConcurrentMap}. + * + * @author Charles Fry + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingConcurrentMap extends ForwardingMap + implements ConcurrentMap { + + /** Constructor for use by subclasses. */ + protected ForwardingConcurrentMap() {} + + @Override + protected abstract ConcurrentMap delegate(); + + @Override + public V putIfAbsent(K key, V value) { + return delegate().putIfAbsent(key, value); + } + + @Override + public boolean remove(Object key, Object value) { + return delegate().remove(key, value); + } + + @Override + public V replace(K key, V value) { + return delegate().replace(key, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return delegate().replace(key, oldValue, newValue); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingDeque.java new file mode 100644 index 0000000000000..52ca4caea25b5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingDeque.java @@ -0,0 +1,150 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.Deque; +import java.util.Iterator; + +/** + * A deque which forwards all its method calls to another deque. Subclasses should override one or + * more methods to modify the behavior of the backing deque as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingDeque} forward indiscriminately to the + * methods of the delegate. For example, overriding {@link #add} alone will not change the + * behavior of {@link #offer} which can lead to unexpected behavior. In this case, you should + * override {@code offer} as well. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingDeque}. + * + * @author Kurt Alfred Kluever + * @since 12.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingDeque extends ForwardingQueue + implements Deque { + + /** Constructor for use by subclasses. */ + protected ForwardingDeque() {} + + @Override + protected abstract Deque delegate(); + + @Override + public void addFirst(@ParametricNullness E e) { + delegate().addFirst(e); + } + + @Override + public void addLast(@ParametricNullness E e) { + delegate().addLast(e); + } + + @Override + public Iterator descendingIterator() { + return delegate().descendingIterator(); + } + + @Override + @ParametricNullness + public E getFirst() { + return delegate().getFirst(); + } + + @Override + @ParametricNullness + public E getLast() { + return delegate().getLast(); + } + + // TODO(cpovirk): Consider removing this? + @Override + public boolean offerFirst(@ParametricNullness E e) { + return delegate().offerFirst(e); + } + + // TODO(cpovirk): Consider removing this? + @Override + public boolean offerLast(@ParametricNullness E e) { + return delegate().offerLast(e); + } + + @Override + public E peekFirst() { + return delegate().peekFirst(); + } + + @Override + public E peekLast() { + return delegate().peekLast(); + } + + // TODO(cpovirk): Consider removing this? + @Override + public E pollFirst() { + return delegate().pollFirst(); + } + + // TODO(cpovirk): Consider removing this? + @Override + public E pollLast() { + return delegate().pollLast(); + } + + @Override + @ParametricNullness + public E pop() { + return delegate().pop(); + } + + @Override + public void push(@ParametricNullness E e) { + delegate().push(e); + } + + @Override + @ParametricNullness + public E removeFirst() { + return delegate().removeFirst(); + } + + @Override + @ParametricNullness + public E removeLast() { + return delegate().removeLast(); + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return delegate().removeFirstOccurrence(o); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return delegate().removeLastOccurrence(o); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableCollection.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableCollection.java new file mode 100644 index 0000000000000..6fbc2226537dd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableCollection.java @@ -0,0 +1,33 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Dummy class that makes the GWT serialization policy happy. It isn't used on the server-side. + * + * @author Hayward Chan + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +class ForwardingImmutableCollection { + private ForwardingImmutableCollection() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableList.java new file mode 100644 index 0000000000000..195e5d195967d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableList.java @@ -0,0 +1,33 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Unused stub class, unreferenced under Java and manually emulated under GWT. + * + * @author Chris Povirk + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ForwardingImmutableList { + private ForwardingImmutableList() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableMap.java new file mode 100644 index 0000000000000..4f3ce755e8ec7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableMap.java @@ -0,0 +1,33 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Unused stub class, unreferenced under Java and manually emulated under GWT. + * + * @author Chris Povirk + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ForwardingImmutableMap { + private ForwardingImmutableMap() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableSet.java new file mode 100644 index 0000000000000..848df91dbed13 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingImmutableSet.java @@ -0,0 +1,33 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Unused stub class, unreferenced under Java and manually emulated under GWT. + * + * @author Chris Povirk + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ForwardingImmutableSet { + private ForwardingImmutableSet() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingIterator.java new file mode 100644 index 0000000000000..e2d36ca12b7e9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingIterator.java @@ -0,0 +1,67 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Iterator; + +/** + * An iterator which forwards all its method calls to another iterator. Subclasses should override + * one or more methods to modify the behavior of the backing iterator as desired per the decorator pattern. + * + *

{@code default} method warning: This class forwards calls to only some {@code + * default} methods. Specifically, it forwards calls only for methods that existed before {@code default} + * methods were introduced. For newer methods, like {@code forEachRemaining}, it inherits their + * default implementations. When those implementations invoke methods, they invoke methods on the + * {@code ForwardingIterator}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingIterator extends ForwardingObject + implements Iterator { + + /** Constructor for use by subclasses. */ + protected ForwardingIterator() {} + + @Override + protected abstract Iterator delegate(); + + @Override + public boolean hasNext() { + return delegate().hasNext(); + } + + @Override + @ParametricNullness + public T next() { + return delegate().next(); + } + + @Override + public void remove() { + delegate().remove(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingList.java new file mode 100644 index 0000000000000..72a6fabf7c5a0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingList.java @@ -0,0 +1,239 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +/** + * A list which forwards all its method calls to another list. Subclasses should override one or + * more methods to modify the behavior of the backing list as desired per the decorator pattern. + * + *

This class does not implement {@link java.util.RandomAccess}. If the delegate supports random + * access, the {@code ForwardingList} subclass should implement the {@code RandomAccess} interface. + * + *

Warning: The methods of {@code ForwardingList} forward indiscriminately to the + * methods of the delegate. For example, overriding {@link #add} alone will not change the + * behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardAddAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingList}. + * + *

The {@code standard} methods and any collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingList extends ForwardingCollection + implements List { + // TODO(lowasser): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingList() {} + + @Override + protected abstract List delegate(); + + @Override + public void add(int index, @ParametricNullness E element) { + delegate().add(index, element); + } + + @Override + public boolean addAll(int index, Collection elements) { + return delegate().addAll(index, elements); + } + + @Override + @ParametricNullness + public E get(int index) { + return delegate().get(index); + } + + @Override + public int indexOf(Object element) { + return delegate().indexOf(element); + } + + @Override + public int lastIndexOf(Object element) { + return delegate().lastIndexOf(element); + } + + @Override + public ListIterator listIterator() { + return delegate().listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return delegate().listIterator(index); + } + + @Override + @ParametricNullness + public E remove(int index) { + return delegate().remove(index); + } + + @Override + @ParametricNullness + public E set(int index, @ParametricNullness E element) { + return delegate().set(index, element); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return delegate().subList(fromIndex, toIndex); + } + + @Override + public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible default implementation of {@link #add(Object)}, in terms of {@link #add(int, + * Object)}. If you override {@link #add(int, Object)}, you may wish to override {@link + * #add(Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardAdd(@ParametricNullness E element) { + add(size(), element); + return true; + } + + /** + * A sensible default implementation of {@link #addAll(int, Collection)}, in terms of the {@code + * add} method of {@link #listIterator(int)}. If you override {@link #listIterator(int)}, you may + * wish to override {@link #addAll(int, Collection)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardAddAll(int index, Iterable elements) { + return Lists.addAllImpl(this, index, elements); + } + + /** + * A sensible default implementation of {@link #indexOf}, in terms of {@link #listIterator()}. If + * you override {@link #listIterator()}, you may wish to override {@link #indexOf} to forward to + * this implementation. + * + * @since 7.0 + */ + protected int standardIndexOf(Object element) { + return Lists.indexOfImpl(this, element); + } + + /** + * A sensible default implementation of {@link #lastIndexOf}, in terms of {@link + * #listIterator(int)}. If you override {@link #listIterator(int)}, you may wish to override + * {@link #lastIndexOf} to forward to this implementation. + * + * @since 7.0 + */ + protected int standardLastIndexOf(Object element) { + return Lists.lastIndexOfImpl(this, element); + } + + /** + * A sensible default implementation of {@link #iterator}, in terms of {@link #listIterator()}. If + * you override {@link #listIterator()}, you may wish to override {@link #iterator} to forward to + * this implementation. + * + * @since 7.0 + */ + protected Iterator standardIterator() { + return listIterator(); + } + + /** + * A sensible default implementation of {@link #listIterator()}, in terms of {@link + * #listIterator(int)}. If you override {@link #listIterator(int)}, you may wish to override + * {@link #listIterator()} to forward to this implementation. + * + * @since 7.0 + */ + protected ListIterator standardListIterator() { + return listIterator(0); + } + + /** + * A sensible default implementation of {@link #listIterator(int)}, in terms of {@link #size}, + * {@link #get(int)}, {@link #set(int, Object)}, {@link #add(int, Object)}, and {@link + * #remove(int)}. If you override any of these methods, you may wish to override {@link + * #listIterator(int)} to forward to this implementation. + * + * @since 7.0 + */ + protected ListIterator standardListIterator(int start) { + return Lists.listIteratorImpl(this, start); + } + + /** + * A sensible default implementation of {@link #subList(int, int)}. If you override any other + * methods, you may wish to override {@link #subList(int, int)} to forward to this implementation. + * + * @since 7.0 + */ + protected List standardSubList(int fromIndex, int toIndex) { + return Lists.subListImpl(this, fromIndex, toIndex); + } + + /** + * A sensible definition of {@link #equals(Object)} in terms of {@link #size} and {@link + * #iterator}. If you override either of those methods, you may wish to override {@link + * #equals(Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + return Lists.equalsImpl(this, object); + } + + /** + * A sensible definition of {@link #hashCode} in terms of {@link #iterator}. If you override + * {@link #iterator}, you may wish to override {@link #hashCode} to forward to this + * implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + return Lists.hashCodeImpl(this); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingListIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingListIterator.java new file mode 100644 index 0000000000000..e7374dbf59b55 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingListIterator.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.ListIterator; + +/** + * A list iterator which forwards all its method calls to another list iterator. Subclasses should + * override one or more methods to modify the behavior of the backing iterator as desired per the decorator pattern. + * + *

{@code default} method warning: This class forwards calls to only some {@code + * default} methods. Specifically, it forwards calls only for methods that existed before {@code + * default} methods were introduced. For newer methods, like {@code forEachRemaining}, it + * inherits their default implementations. When those implementations invoke methods, they invoke + * methods on the {@code ForwardingListIterator}. + * + * @author Mike Bostock + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingListIterator extends ForwardingIterator + implements ListIterator { + + /** Constructor for use by subclasses. */ + protected ForwardingListIterator() {} + + @Override + protected abstract ListIterator delegate(); + + @Override + public void add(@ParametricNullness E element) { + delegate().add(element); + } + + @Override + public boolean hasPrevious() { + return delegate().hasPrevious(); + } + + @Override + public int nextIndex() { + return delegate().nextIndex(); + } + + @Override + @ParametricNullness + public E previous() { + return delegate().previous(); + } + + @Override + public int previousIndex() { + return delegate().previousIndex(); + } + + @Override + public void set(@ParametricNullness E element) { + delegate().set(element); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingListMultimap.java new file mode 100644 index 0000000000000..b2d5ea09c8ba6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingListMultimap.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.List; + +/** + * A list multimap which forwards all its method calls to another list multimap. Subclasses should + * override one or more methods to modify the behavior of the backing multimap as desired per the decorator pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingListMultimap}. + * + * @author Kurt Alfred Kluever + * @since 3.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingListMultimap + extends ForwardingMultimap implements ListMultimap { + + /** Constructor for use by subclasses. */ + protected ForwardingListMultimap() {} + + @Override + protected abstract ListMultimap delegate(); + + @Override + public List get(@ParametricNullness K key) { + return delegate().get(key); + } + + @Override + public List removeAll(Object key) { + return delegate().removeAll(key); + } + + @Override + public List replaceValues(@ParametricNullness K key, Iterable values) { + return delegate().replaceValues(key, values); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMap.java new file mode 100644 index 0000000000000..7136bdd8eb0f4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMap.java @@ -0,0 +1,301 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A map which forwards all its method calls to another map. Subclasses should override one or more + * methods to modify the behavior of the backing map as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingMap} forward indiscriminately to the + * methods of the delegate. For example, overriding {@link #put} alone will not change the + * behavior of {@link #putAll}, which can lead to unexpected behavior. In this case, you should + * override {@code putAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardPutAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingMap}. + * + *

Each of the {@code standard} methods, where appropriate, use {@link Objects#equal} to test + * equality for both keys and values. This may not be the desired behavior for map implementations + * that use non-standard notions of key equality, such as a {@code SortedMap} whose comparator is + * not consistent with {@code equals}. + * + *

The {@code standard} methods and the collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingMap extends ForwardingObject + implements Map { + // TODO(lowasser): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingMap() {} + + @Override + protected abstract Map delegate(); + + @Override + public int size() { + return delegate().size(); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public V remove(Object key) { + return delegate().remove(key); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public boolean containsKey(Object key) { + return delegate().containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate().containsValue(value); + } + + @Override + public V get(Object key) { + return delegate().get(key); + } + + @Override + public V put(@ParametricNullness K key, @ParametricNullness V value) { + return delegate().put(key, value); + } + + @Override + public void putAll(Map map) { + delegate().putAll(map); + } + + @Override + public Set keySet() { + return delegate().keySet(); + } + + @Override + public Collection values() { + return delegate().values(); + } + + @Override + public Set> entrySet() { + return delegate().entrySet(); + } + + @Override + public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #putAll(Map)} in terms of {@link #put(Object, Object)}. If you + * override {@link #put(Object, Object)}, you may wish to override {@link #putAll(Map)} to forward + * to this implementation. + * + * @since 7.0 + */ + protected void standardPutAll(Map map) { + Maps.putAllImpl(this, map); + } + + /** + * A sensible, albeit inefficient, definition of {@link #remove} in terms of the {@code iterator} + * method of {@link #entrySet}. If you override {@link #entrySet}, you may wish to override {@link + * #remove} to forward to this implementation. + * + *

Alternately, you may wish to override {@link #remove} with {@code keySet().remove}, assuming + * that approach would not lead to an infinite loop. + * + * @since 7.0 + */ + protected V standardRemove(Object key) { + Iterator> entryIterator = entrySet().iterator(); + while (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + if (Objects.equal(entry.getKey(), key)) { + V value = entry.getValue(); + entryIterator.remove(); + return value; + } + } + return null; + } + + /** + * A sensible definition of {@link #clear} in terms of the {@code iterator} method of {@link + * #entrySet}. In many cases, you may wish to override {@link #clear} to forward to this + * implementation. + * + * @since 7.0 + */ + protected void standardClear() { + Iterators.clear(entrySet().iterator()); + } + + /** + * A sensible implementation of {@link Map#keySet} in terms of the following methods: {@link + * ForwardingMap#clear}, {@link ForwardingMap#containsKey}, {@link ForwardingMap#isEmpty}, {@link + * ForwardingMap#remove}, {@link ForwardingMap#size}, and the {@link Set#iterator} method of + * {@link ForwardingMap#entrySet}. In many cases, you may wish to override {@link + * ForwardingMap#keySet} to forward to this implementation or a subclass thereof. + * + * @since 10.0 + */ + protected class StandardKeySet extends Maps.KeySet { + /** Constructor for use by subclasses. */ + public StandardKeySet() { + super(ForwardingMap.this); + } + } + + /** + * A sensible, albeit inefficient, definition of {@link #containsKey} in terms of the {@code + * iterator} method of {@link #entrySet}. If you override {@link #entrySet}, you may wish to + * override {@link #containsKey} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardContainsKey(Object key) { + return Maps.containsKeyImpl(this, key); + } + + /** + * A sensible implementation of {@link Map#values} in terms of the following methods: {@link + * ForwardingMap#clear}, {@link ForwardingMap#containsValue}, {@link ForwardingMap#isEmpty}, + * {@link ForwardingMap#size}, and the {@link Set#iterator} method of {@link + * ForwardingMap#entrySet}. In many cases, you may wish to override {@link ForwardingMap#values} + * to forward to this implementation or a subclass thereof. + * + * @since 10.0 + */ + protected class StandardValues extends Maps.Values { + /** Constructor for use by subclasses. */ + public StandardValues() { + super(ForwardingMap.this); + } + } + + /** + * A sensible definition of {@link #containsValue} in terms of the {@code iterator} method of + * {@link #entrySet}. If you override {@link #entrySet}, you may wish to override {@link + * #containsValue} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardContainsValue(Object value) { + return Maps.containsValueImpl(this, value); + } + + /** + * A sensible implementation of {@link Map#entrySet} in terms of the following methods: {@link + * ForwardingMap#clear}, {@link ForwardingMap#containsKey}, {@link ForwardingMap#get}, {@link + * ForwardingMap#isEmpty}, {@link ForwardingMap#remove}, and {@link ForwardingMap#size}. In many + * cases, you may wish to override {@link #entrySet} to forward to this implementation or a + * subclass thereof. + * + * @since 10.0 + */ + protected abstract class StandardEntrySet extends Maps.EntrySet { + /** Constructor for use by subclasses. */ + protected StandardEntrySet() {} + + @Override + Map map() { + return ForwardingMap.this; + } + } + + /** + * A sensible definition of {@link #isEmpty} in terms of the {@code iterator} method of {@link + * #entrySet}. If you override {@link #entrySet}, you may wish to override {@link #isEmpty} to + * forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardIsEmpty() { + return !entrySet().iterator().hasNext(); + } + + /** + * A sensible definition of {@link #equals} in terms of the {@code equals} method of {@link + * #entrySet}. If you override {@link #entrySet}, you may wish to override {@link #equals} to + * forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + return Maps.equalsImpl(this, object); + } + + /** + * A sensible definition of {@link #hashCode} in terms of the {@code iterator} method of {@link + * #entrySet}. If you override {@link #entrySet}, you may wish to override {@link #hashCode} to + * forward to this implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + return Sets.hashCodeImpl(entrySet()); + } + + /** + * A sensible definition of {@link #toString} in terms of the {@code iterator} method of {@link + * #entrySet}. If you override {@link #entrySet}, you may wish to override {@link #toString} to + * forward to this implementation. + * + * @since 7.0 + */ + protected String standardToString() { + return Maps.toStringImpl(this); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMapEntry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMapEntry.java new file mode 100644 index 0000000000000..ea12de14e0054 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMapEntry.java @@ -0,0 +1,130 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.Map; +import java.util.Map.Entry; + +/** + * A map entry which forwards all its method calls to another map entry. Subclasses should override + * one or more methods to modify the behavior of the backing map entry as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingMapEntry} forward indiscriminately to + * the methods of the delegate. For example, overriding {@link #getValue} alone will not + * change the behavior of {@link #equals}, which can lead to unexpected behavior. In this case, you + * should override {@code equals} as well, either providing your own implementation, or delegating + * to the provided {@code standardEquals} method. + * + *

Each of the {@code standard} methods, where appropriate, use {@link Objects#equal} to test + * equality for both keys and values. This may not be the desired behavior for map implementations + * that use non-standard notions of key equality, such as the entry of a {@code SortedMap} whose + * comparator is not consistent with {@code equals}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even when all of the + * methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingMapEntry + extends ForwardingObject implements Map.Entry { + // TODO(lowasser): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingMapEntry() {} + + @Override + protected abstract Entry delegate(); + + @Override + @ParametricNullness + public K getKey() { + return delegate().getKey(); + } + + @Override + @ParametricNullness + public V getValue() { + return delegate().getValue(); + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V value) { + return delegate().setValue(value); + } + + @Override + public boolean equals(Object object) { + return delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #equals(Object)} in terms of {@link #getKey()} and {@link + * #getValue()}. If you override either of these methods, you may wish to override {@link + * #equals(Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equal(this.getKey(), that.getKey()) + && Objects.equal(this.getValue(), that.getValue()); + } + return false; + } + + /** + * A sensible definition of {@link #hashCode()} in terms of {@link #getKey()} and {@link + * #getValue()}. If you override either of these methods, you may wish to override {@link + * #hashCode()} to forward to this implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + K k = getKey(); + V v = getValue(); + return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); + } + + /** + * A sensible definition of {@link #toString} in terms of {@link #getKey} and {@link #getValue}. + * If you override either of these methods, you may wish to override {@link #equals} to forward to + * this implementation. + * + * @since 7.0 + */ + protected String standardToString() { + return getKey() + "=" + getValue(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMultimap.java new file mode 100644 index 0000000000000..4c41b27aa19c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMultimap.java @@ -0,0 +1,151 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A multimap which forwards all its method calls to another multimap. Subclasses should override + * one or more methods to modify the behavior of the backing multimap as desired per the decorator pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingMultimap}. + * + * @author Robert Konigsberg + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingMultimap + extends ForwardingObject implements Multimap { + + /** Constructor for use by subclasses. */ + protected ForwardingMultimap() {} + + @Override + protected abstract Multimap delegate(); + + @Override + public Map> asMap() { + return delegate().asMap(); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public boolean containsEntry(Object key, Object value) { + return delegate().containsEntry(key, value); + } + + @Override + public boolean containsKey(Object key) { + return delegate().containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate().containsValue(value); + } + + @Override + public Collection> entries() { + return delegate().entries(); + } + + @Override + public Collection get(@ParametricNullness K key) { + return delegate().get(key); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public Multiset keys() { + return delegate().keys(); + } + + @Override + public Set keySet() { + return delegate().keySet(); + } + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + return delegate().put(key, value); + } + + @Override + public boolean putAll(@ParametricNullness K key, Iterable values) { + return delegate().putAll(key, values); + } + + @Override + public boolean putAll(Multimap multimap) { + return delegate().putAll(multimap); + } + + @Override + public boolean remove(Object key, Object value) { + return delegate().remove(key, value); + } + + @Override + public Collection removeAll(Object key) { + return delegate().removeAll(key); + } + + @Override + public Collection replaceValues(@ParametricNullness K key, Iterable values) { + return delegate().replaceValues(key, values); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public Collection values() { + return delegate().values(); + } + + @Override + public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMultiset.java new file mode 100644 index 0000000000000..36396a150a68c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingMultiset.java @@ -0,0 +1,310 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +/** + * A multiset which forwards all its method calls to another multiset. Subclasses should override + * one or more methods to modify the behavior of the backing multiset as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingMultiset} forward indiscriminately to + * the methods of the delegate. For example, overriding {@link #add(Object, int)} alone will + * not change the behavior of {@link #add(Object)}, which can lead to unexpected behavior. In + * this case, you should override {@code add(Object)} as well, either providing your own + * implementation, or delegating to the provided {@code standardAdd} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingMultiset}. + * + *

The {@code standard} methods and any collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingMultiset extends ForwardingCollection + implements Multiset { + + /** Constructor for use by subclasses. */ + protected ForwardingMultiset() {} + + @Override + protected abstract Multiset delegate(); + + @Override + public int count(Object element) { + return delegate().count(element); + } + + @Override + public int add(@ParametricNullness E element, int occurrences) { + return delegate().add(element, occurrences); + } + + @Override + public int remove(Object element, int occurrences) { + return delegate().remove(element, occurrences); + } + + @Override + public Set elementSet() { + return delegate().elementSet(); + } + + @Override + public Set> entrySet() { + return delegate().entrySet(); + } + + @Override + public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + @Override + public int setCount(@ParametricNullness E element, int count) { + return delegate().setCount(element, count); + } + + @Override + public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { + return delegate().setCount(element, oldCount, newCount); + } + + /** + * A sensible definition of {@link #contains} in terms of {@link #count}. If you override {@link + * #count}, you may wish to override {@link #contains} to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardContains(Object object) { + return count(object) > 0; + } + + /** + * A sensible definition of {@link #clear} in terms of the {@code iterator} method of {@link + * #entrySet}. If you override {@link #entrySet}, you may wish to override {@link #clear} to + * forward to this implementation. + * + * @since 7.0 + */ + @Override + protected void standardClear() { + Iterators.clear(entrySet().iterator()); + } + + /** + * A sensible, albeit inefficient, definition of {@link #count} in terms of {@link #entrySet}. If + * you override {@link #entrySet}, you may wish to override {@link #count} to forward to this + * implementation. + * + * @since 7.0 + */ + protected int standardCount(Object object) { + for (Entry entry : this.entrySet()) { + if (Objects.equal(entry.getElement(), object)) { + return entry.getCount(); + } + } + return 0; + } + + /** + * A sensible definition of {@link #add(Object)} in terms of {@link #add(Object, int)}. If you + * override {@link #add(Object, int)}, you may wish to override {@link #add(Object)} to forward to + * this implementation. + * + * @since 7.0 + */ + protected boolean standardAdd(@ParametricNullness E element) { + add(element, 1); + return true; + } + + /** + * A sensible definition of {@link #addAll(Collection)} in terms of {@link #add(Object)} and + * {@link #add(Object, int)}. If you override either of these methods, you may wish to override + * {@link #addAll(Collection)} to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardAddAll(Collection elementsToAdd) { + return Multisets.addAllImpl(this, elementsToAdd); + } + + /** + * A sensible definition of {@link #remove(Object)} in terms of {@link #remove(Object, int)}. If + * you override {@link #remove(Object, int)}, you may wish to override {@link #remove(Object)} to + * forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRemove(Object element) { + return remove(element, 1) > 0; + } + + /** + * A sensible definition of {@link #removeAll} in terms of the {@code removeAll} method of {@link + * #elementSet}. If you override {@link #elementSet}, you may wish to override {@link #removeAll} + * to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRemoveAll(Collection elementsToRemove) { + return Multisets.removeAllImpl(this, elementsToRemove); + } + + /** + * A sensible definition of {@link #retainAll} in terms of the {@code retainAll} method of {@link + * #elementSet}. If you override {@link #elementSet}, you may wish to override {@link #retainAll} + * to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRetainAll(Collection elementsToRetain) { + return Multisets.retainAllImpl(this, elementsToRetain); + } + + /** + * A sensible definition of {@link #setCount(Object, int)} in terms of {@link #count(Object)}, + * {@link #add(Object, int)}, and {@link #remove(Object, int)}. {@link #entrySet()}. If you + * override any of these methods, you may wish to override {@link #setCount(Object, int)} to + * forward to this implementation. + * + * @since 7.0 + */ + protected int standardSetCount(@ParametricNullness E element, int count) { + return Multisets.setCountImpl(this, element, count); + } + + /** + * A sensible definition of {@link #setCount(Object, int, int)} in terms of {@link #count(Object)} + * and {@link #setCount(Object, int)}. If you override either of these methods, you may wish to + * override {@link #setCount(Object, int, int)} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardSetCount(@ParametricNullness E element, int oldCount, int newCount) { + return Multisets.setCountImpl(this, element, oldCount, newCount); + } + + /** + * A sensible implementation of {@link Multiset#elementSet} in terms of the following methods: + * {@link ForwardingMultiset#clear}, {@link ForwardingMultiset#contains}, {@link + * ForwardingMultiset#containsAll}, {@link ForwardingMultiset#count}, {@link + * ForwardingMultiset#isEmpty}, the {@link Set#size} and {@link Set#iterator} methods of {@link + * ForwardingMultiset#entrySet}, and {@link ForwardingMultiset#remove(Object, int)}. In many + * situations, you may wish to override {@link ForwardingMultiset#elementSet} to forward to this + * implementation or a subclass thereof. + * + * @since 10.0 + */ + protected class StandardElementSet extends Multisets.ElementSet { + /** Constructor for use by subclasses. */ + public StandardElementSet() {} + + @Override + Multiset multiset() { + return ForwardingMultiset.this; + } + + @Override + public Iterator iterator() { + return Multisets.elementIterator(multiset().entrySet().iterator()); + } + } + + /** + * A sensible definition of {@link #iterator} in terms of {@link #entrySet} and {@link + * #remove(Object)}. If you override either of these methods, you may wish to override {@link + * #iterator} to forward to this implementation. + * + * @since 7.0 + */ + protected Iterator standardIterator() { + return Multisets.iteratorImpl(this); + } + + /** + * A sensible, albeit inefficient, definition of {@link #size} in terms of {@link #entrySet}. If + * you override {@link #entrySet}, you may wish to override {@link #size} to forward to this + * implementation. + * + * @since 7.0 + */ + protected int standardSize() { + return Multisets.linearTimeSizeImpl(this); + } + + /** + * A sensible, albeit inefficient, definition of {@link #equals} in terms of {@code + * entrySet().size()} and {@link #count}. If you override either of these methods, you may wish to + * override {@link #equals} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + return Multisets.equalsImpl(this, object); + } + + /** + * A sensible definition of {@link #hashCode} as {@code entrySet().hashCode()} . If you override + * {@link #entrySet}, you may wish to override {@link #hashCode} to forward to this + * implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + return entrySet().hashCode(); + } + + /** + * A sensible definition of {@link #toString} as {@code entrySet().toString()} . If you override + * {@link #entrySet}, you may wish to override {@link #toString} to forward to this + * implementation. + * + * @since 7.0 + */ + @Override + protected String standardToString() { + return entrySet().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingNavigableMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingNavigableMap.java new file mode 100644 index 0000000000000..1eeb9c1ea2b56 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingNavigableMap.java @@ -0,0 +1,414 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Iterator; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.SortedMap; +import java.util.function.BiFunction; + +import static org.apache.iotdb.google.common.collect.Maps.keyOrNull; + +/** + * A navigable map which forwards all its method calls to another navigable map. Subclasses should + * override one or more methods to modify the behavior of the backing map as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingNavigableMap} forward indiscriminately + * to the methods of the delegate. For example, overriding {@link #put} alone will not change + * the behavior of {@link #putAll}, which can lead to unexpected behavior. In this case, you should + * override {@code putAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardPutAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingNavigableMap}. + * + *

Each of the {@code standard} methods uses the map's comparator (or the natural ordering of the + * elements, if there is no comparator) to test element equality. As a result, if the comparator is + * not consistent with equals, some of the standard implementations may violate the {@code Map} + * contract. + * + *

The {@code standard} methods and the collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Louis Wasserman + * @since 12.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingNavigableMap + extends ForwardingSortedMap implements NavigableMap { + + /** Constructor for use by subclasses. */ + protected ForwardingNavigableMap() {} + + @Override + protected abstract NavigableMap delegate(); + + @Override + public Entry lowerEntry(@ParametricNullness K key) { + return delegate().lowerEntry(key); + } + + /** + * A sensible definition of {@link #lowerEntry} in terms of the {@code lastEntry()} of {@link + * #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override {@code + * lowerEntry} to forward to this implementation. + */ + protected Entry standardLowerEntry(@ParametricNullness K key) { + return headMap(key, false).lastEntry(); + } + + @Override + public K lowerKey(@ParametricNullness K key) { + return delegate().lowerKey(key); + } + + /** + * A sensible definition of {@link #lowerKey} in terms of {@code lowerEntry}. If you override + * {@link #lowerEntry}, you may wish to override {@code lowerKey} to forward to this + * implementation. + */ + protected K standardLowerKey(@ParametricNullness K key) { + return keyOrNull(lowerEntry(key)); + } + + @Override + public Entry floorEntry(@ParametricNullness K key) { + return delegate().floorEntry(key); + } + + /** + * A sensible definition of {@link #floorEntry} in terms of the {@code lastEntry()} of {@link + * #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override {@code + * floorEntry} to forward to this implementation. + */ + protected Entry standardFloorEntry(@ParametricNullness K key) { + return headMap(key, true).lastEntry(); + } + + @Override + public K floorKey(@ParametricNullness K key) { + return delegate().floorKey(key); + } + + /** + * A sensible definition of {@link #floorKey} in terms of {@code floorEntry}. If you override + * {@code floorEntry}, you may wish to override {@code floorKey} to forward to this + * implementation. + */ + protected K standardFloorKey(@ParametricNullness K key) { + return keyOrNull(floorEntry(key)); + } + + @Override + public Entry ceilingEntry(@ParametricNullness K key) { + return delegate().ceilingEntry(key); + } + + /** + * A sensible definition of {@link #ceilingEntry} in terms of the {@code firstEntry()} of {@link + * #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override {@code + * ceilingEntry} to forward to this implementation. + */ + protected Entry standardCeilingEntry(@ParametricNullness K key) { + return tailMap(key, true).firstEntry(); + } + + @Override + public K ceilingKey(@ParametricNullness K key) { + return delegate().ceilingKey(key); + } + + /** + * A sensible definition of {@link #ceilingKey} in terms of {@code ceilingEntry}. If you override + * {@code ceilingEntry}, you may wish to override {@code ceilingKey} to forward to this + * implementation. + */ + protected K standardCeilingKey(@ParametricNullness K key) { + return keyOrNull(ceilingEntry(key)); + } + + @Override + public Entry higherEntry(@ParametricNullness K key) { + return delegate().higherEntry(key); + } + + /** + * A sensible definition of {@link #higherEntry} in terms of the {@code firstEntry()} of {@link + * #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override {@code + * higherEntry} to forward to this implementation. + */ + protected Entry standardHigherEntry(@ParametricNullness K key) { + return tailMap(key, false).firstEntry(); + } + + @Override + public K higherKey(@ParametricNullness K key) { + return delegate().higherKey(key); + } + + /** + * A sensible definition of {@link #higherKey} in terms of {@code higherEntry}. If you override + * {@code higherEntry}, you may wish to override {@code higherKey} to forward to this + * implementation. + */ + protected K standardHigherKey(@ParametricNullness K key) { + return keyOrNull(higherEntry(key)); + } + + @Override + public Entry firstEntry() { + return delegate().firstEntry(); + } + + /** + * A sensible definition of {@link #firstEntry} in terms of the {@code iterator()} of {@link + * #entrySet}. If you override {@code entrySet}, you may wish to override {@code firstEntry} to + * forward to this implementation. + */ + protected Entry standardFirstEntry() { + return Iterables.>getFirst(entrySet(), null); + } + + /** + * A sensible definition of {@link #firstKey} in terms of {@code firstEntry}. If you override + * {@code firstEntry}, you may wish to override {@code firstKey} to forward to this + * implementation. + */ + protected K standardFirstKey() { + Entry entry = firstEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + public Entry lastEntry() { + return delegate().lastEntry(); + } + + /** + * A sensible definition of {@link #lastEntry} in terms of the {@code iterator()} of the {@link + * #entrySet} of {@link #descendingMap}. If you override {@code descendingMap}, you may wish to + * override {@code lastEntry} to forward to this implementation. + */ + protected Entry standardLastEntry() { + return Iterables.>getFirst(descendingMap().entrySet(), null); + } + + /** + * A sensible definition of {@link #lastKey} in terms of {@code lastEntry}. If you override {@code + * lastEntry}, you may wish to override {@code lastKey} to forward to this implementation. + */ + protected K standardLastKey() { + Entry entry = lastEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Override + public Entry pollFirstEntry() { + return delegate().pollFirstEntry(); + } + + /** + * A sensible definition of {@link #pollFirstEntry} in terms of the {@code iterator} of {@code + * entrySet}. If you override {@code entrySet}, you may wish to override {@code pollFirstEntry} to + * forward to this implementation. + */ + protected Entry standardPollFirstEntry() { + return Iterators.pollNext(entrySet().iterator()); + } + + @Override + public Entry pollLastEntry() { + return delegate().pollLastEntry(); + } + + /** + * A sensible definition of {@link #pollFirstEntry} in terms of the {@code iterator} of the {@code + * entrySet} of {@code descendingMap}. If you override {@code descendingMap}, you may wish to + * override {@code pollFirstEntry} to forward to this implementation. + */ + protected Entry standardPollLastEntry() { + return Iterators.pollNext(descendingMap().entrySet().iterator()); + } + + @Override + public NavigableMap descendingMap() { + return delegate().descendingMap(); + } + + /** + * A sensible implementation of {@link NavigableMap#descendingMap} in terms of the methods of this + * {@code NavigableMap}. In many cases, you may wish to override {@link + * ForwardingNavigableMap#descendingMap} to forward to this implementation or a subclass thereof. + * + *

In particular, this map iterates over entries with repeated calls to {@link + * NavigableMap#lowerEntry}. If a more efficient means of iteration is available, you may wish to + * override the {@code entryIterator()} method of this class. + * + * @since 12.0 + */ + protected class StandardDescendingMap extends Maps.DescendingMap { + /** Constructor for use by subclasses. */ + public StandardDescendingMap() {} + + @Override + NavigableMap forward() { + return ForwardingNavigableMap.this; + } + + @Override + public void replaceAll(BiFunction function) { + forward().replaceAll(function); + } + + @Override + protected Iterator> entryIterator() { + return new Iterator>() { + private Entry toRemove = null; + private Entry nextOrNull = forward().lastEntry(); + + @Override + public boolean hasNext() { + return nextOrNull != null; + } + + @Override + public java.util.Map.Entry next() { + if (nextOrNull == null) { + throw new NoSuchElementException(); + } + try { + return nextOrNull; + } finally { + toRemove = nextOrNull; + nextOrNull = forward().lowerEntry(nextOrNull.getKey()); + } + } + + @Override + public void remove() { + if (toRemove == null) { + throw new IllegalStateException("no calls to next() since the last call to remove()"); + } + forward().remove(toRemove.getKey()); + toRemove = null; + } + }; + } + } + + @Override + public NavigableSet navigableKeySet() { + return delegate().navigableKeySet(); + } + + /** + * A sensible implementation of {@link NavigableMap#navigableKeySet} in terms of the methods of + * this {@code NavigableMap}. In many cases, you may wish to override {@link + * ForwardingNavigableMap#navigableKeySet} to forward to this implementation or a subclass + * thereof. + * + * @since 12.0 + */ + protected class StandardNavigableKeySet extends Maps.NavigableKeySet { + /** Constructor for use by subclasses. */ + public StandardNavigableKeySet() { + super(ForwardingNavigableMap.this); + } + } + + @Override + public NavigableSet descendingKeySet() { + return delegate().descendingKeySet(); + } + + /** + * A sensible definition of {@link #descendingKeySet} as the {@code navigableKeySet} of {@link + * #descendingMap}. (The {@link StandardDescendingMap} implementation implements {@code + * navigableKeySet} on its own, so as not to cause an infinite loop.) If you override {@code + * descendingMap}, you may wish to override {@code descendingKeySet} to forward to this + * implementation. + */ + protected NavigableSet standardDescendingKeySet() { + return descendingMap().navigableKeySet(); + } + + /** + * A sensible definition of {@link #subMap(Object, Object)} in terms of {@link #subMap(Object, + * boolean, Object, boolean)}. If you override {@code subMap(K, boolean, K, boolean)}, you may + * wish to override {@code subMap} to forward to this implementation. + */ + @Override + protected SortedMap standardSubMap( + @ParametricNullness K fromKey, @ParametricNullness K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return delegate().subMap(fromKey, fromInclusive, toKey, toInclusive); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusive) { + return delegate().headMap(toKey, inclusive); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusive) { + return delegate().tailMap(fromKey, inclusive); + } + + /** + * A sensible definition of {@link #headMap(Object)} in terms of {@link #headMap(Object, + * boolean)}. If you override {@code headMap(K, boolean)}, you may wish to override {@code + * headMap} to forward to this implementation. + */ + protected SortedMap standardHeadMap(@ParametricNullness K toKey) { + return headMap(toKey, false); + } + + /** + * A sensible definition of {@link #tailMap(Object)} in terms of {@link #tailMap(Object, + * boolean)}. If you override {@code tailMap(K, boolean)}, you may wish to override {@code + * tailMap} to forward to this implementation. + */ + protected SortedMap standardTailMap(@ParametricNullness K fromKey) { + return tailMap(fromKey, true); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingNavigableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingNavigableSet.java new file mode 100644 index 0000000000000..a400456f72967 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingNavigableSet.java @@ -0,0 +1,246 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; + +/** + * A navigable set which forwards all its method calls to another navigable set. Subclasses should + * override one or more methods to modify the behavior of the backing set as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingNavigableSet} forward indiscriminately + * to the methods of the delegate. For example, overriding {@link #add} alone will not change + * the behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardAddAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingNavigableSet}. + * + *

Each of the {@code standard} methods uses the set's comparator (or the natural ordering of the + * elements, if there is no comparator) to test element equality. As a result, if the comparator is + * not consistent with equals, some of the standard implementations may violate the {@code Set} + * contract. + * + *

The {@code standard} methods and the collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Louis Wasserman + * @since 12.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingNavigableSet extends ForwardingSortedSet + implements NavigableSet { + + /** Constructor for use by subclasses. */ + protected ForwardingNavigableSet() {} + + @Override + protected abstract NavigableSet delegate(); + + @Override + public E lower(@ParametricNullness E e) { + return delegate().lower(e); + } + + /** + * A sensible definition of {@link #lower} in terms of the {@code descendingIterator} method of + * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may + * wish to override {@link #lower} to forward to this implementation. + */ + protected E standardLower(@ParametricNullness E e) { + return Iterators.getNext(headSet(e, false).descendingIterator(), null); + } + + @Override + public E floor(@ParametricNullness E e) { + return delegate().floor(e); + } + + /** + * A sensible definition of {@link #floor} in terms of the {@code descendingIterator} method of + * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may + * wish to override {@link #floor} to forward to this implementation. + */ + protected E standardFloor(@ParametricNullness E e) { + return Iterators.getNext(headSet(e, true).descendingIterator(), null); + } + + @Override + public E ceiling(@ParametricNullness E e) { + return delegate().ceiling(e); + } + + /** + * A sensible definition of {@link #ceiling} in terms of the {@code iterator} method of {@link + * #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may wish to + * override {@link #ceiling} to forward to this implementation. + */ + protected E standardCeiling(@ParametricNullness E e) { + return Iterators.getNext(tailSet(e, true).iterator(), null); + } + + @Override + public E higher(@ParametricNullness E e) { + return delegate().higher(e); + } + + /** + * A sensible definition of {@link #higher} in terms of the {@code iterator} method of {@link + * #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may wish to + * override {@link #higher} to forward to this implementation. + */ + protected E standardHigher(@ParametricNullness E e) { + return Iterators.getNext(tailSet(e, false).iterator(), null); + } + + @Override + public E pollFirst() { + return delegate().pollFirst(); + } + + /** + * A sensible definition of {@link #pollFirst} in terms of the {@code iterator} method. If you + * override {@link #iterator} you may wish to override {@link #pollFirst} to forward to this + * implementation. + */ + protected E standardPollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public E pollLast() { + return delegate().pollLast(); + } + + /** + * A sensible definition of {@link #pollLast} in terms of the {@code descendingIterator} method. + * If you override {@link #descendingIterator} you may wish to override {@link #pollLast} to + * forward to this implementation. + */ + protected E standardPollLast() { + return Iterators.pollNext(descendingIterator()); + } + + @ParametricNullness + protected E standardFirst() { + return iterator().next(); + } + + @ParametricNullness + protected E standardLast() { + return descendingIterator().next(); + } + + @Override + public NavigableSet descendingSet() { + return delegate().descendingSet(); + } + + /** + * A sensible implementation of {@link NavigableSet#descendingSet} in terms of the other methods + * of {@link NavigableSet}, notably including {@link NavigableSet#descendingIterator}. + * + *

In many cases, you may wish to override {@link ForwardingNavigableSet#descendingSet} to + * forward to this implementation or a subclass thereof. + * + * @since 12.0 + */ + protected class StandardDescendingSet extends Sets.DescendingSet { + /** Constructor for use by subclasses. */ + public StandardDescendingSet() { + super(ForwardingNavigableSet.this); + } + } + + @Override + public Iterator descendingIterator() { + return delegate().descendingIterator(); + } + + @Override + public NavigableSet subSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return delegate().subSet(fromElement, fromInclusive, toElement, toInclusive); + } + + /** + * A sensible definition of {@link #subSet(Object, boolean, Object, boolean)} in terms of the + * {@code headSet} and {@code tailSet} methods. In many cases, you may wish to override {@link + * #subSet(Object, boolean, Object, boolean)} to forward to this implementation. + */ + protected NavigableSet standardSubSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive); + } + + /** + * A sensible definition of {@link #subSet(Object, Object)} in terms of the {@link #subSet(Object, + * boolean, Object, boolean)} method. If you override {@link #subSet(Object, boolean, Object, + * boolean)}, you may wish to override {@link #subSet(Object, Object)} to forward to this + * implementation. + */ + @Override + protected SortedSet standardSubSet( + @ParametricNullness E fromElement, @ParametricNullness E toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet headSet(@ParametricNullness E toElement, boolean inclusive) { + return delegate().headSet(toElement, inclusive); + } + + /** + * A sensible definition of {@link #headSet(Object)} in terms of the {@link #headSet(Object, + * boolean)} method. If you override {@link #headSet(Object, boolean)}, you may wish to override + * {@link #headSet(Object)} to forward to this implementation. + */ + protected SortedSet standardHeadSet(@ParametricNullness E toElement) { + return headSet(toElement, false); + } + + @Override + public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclusive) { + return delegate().tailSet(fromElement, inclusive); + } + + /** + * A sensible definition of {@link #tailSet(Object)} in terms of the {@link #tailSet(Object, + * boolean)} method. If you override {@link #tailSet(Object, boolean)}, you may wish to override + * {@link #tailSet(Object)} to forward to this implementation. + */ + protected SortedSet standardTailSet(@ParametricNullness E fromElement) { + return tailSet(fromElement, true); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingObject.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingObject.java new file mode 100644 index 0000000000000..2469ca5ab010a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingObject.java @@ -0,0 +1,72 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** + * An abstract base class for implementing the decorator pattern. The {@link + * #delegate()} method must be overridden to return the instance being decorated. + * + *

This class does not forward the {@code hashCode} and {@code equals} methods through to + * the backing object, but relies on {@code Object}'s implementation. This is necessary to preserve + * the symmetry of {@code equals}. Custom definitions of equality are usually based on an interface, + * such as {@code Set} or {@code List}, so that the implementation of {@code equals} can cast the + * object being tested for equality to the custom interface. {@code ForwardingObject} implements no + * such custom interfaces directly; they are implemented only in subclasses. Therefore, forwarding + * {@code equals} would break symmetry, as the forwarding object might consider itself equal to the + * object being tested, but the reverse could not be true. This behavior is consistent with the + * JDK's collection wrappers, such as {@link java.util.Collections#unmodifiableCollection}. Use an + * interface-specific subclass of {@code ForwardingObject}, such as {@link ForwardingList}, to + * preserve equality behavior, or override {@code equals} directly. + * + *

The {@code toString} method is forwarded to the delegate. Although this class does not + * implement {@link Serializable}, a serializable subclass may be created since this class has a + * parameter-less constructor. + * + * @author Mike Bostock + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingObject { + + /** Constructor for use by subclasses. */ + protected ForwardingObject() {} + + /** + * Returns the backing delegate instance that methods are forwarded to. Abstract subclasses + * generally override this method with an abstract method that has a more specific return type, + * such as {@link ForwardingSet#delegate}. Concrete subclasses override this method to supply the + * instance being decorated. + */ + protected abstract Object delegate(); + + /** Returns the string representation generated by the delegate's {@code toString} method. */ + @Override + public String toString() { + return delegate().toString(); + } + + /* No equals or hashCode. See class comments for details. */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingQueue.java new file mode 100644 index 0000000000000..5197eebe7cccd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingQueue.java @@ -0,0 +1,130 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.NoSuchElementException; +import java.util.Queue; + +/** + * A queue which forwards all its method calls to another queue. Subclasses should override one or + * more methods to modify the behavior of the backing queue as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingQueue} forward indiscriminately to the + * methods of the delegate. For example, overriding {@link #add} alone will not change the + * behavior of {@link #offer} which can lead to unexpected behavior. In this case, you should + * override {@code offer} as well, either providing your own implementation, or delegating to the + * provided {@code standardOffer} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingQueue}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even when all of the + * methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingQueue extends ForwardingCollection + implements Queue { + + /** Constructor for use by subclasses. */ + protected ForwardingQueue() {} + + @Override + protected abstract Queue delegate(); + + // TODO(cpovirk): Consider removing this? + @Override + public boolean offer(@ParametricNullness E o) { + return delegate().offer(o); + } + + // TODO(cpovirk): Consider removing this? + @Override + public E poll() { + return delegate().poll(); + } + + @Override + @ParametricNullness + public E remove() { + return delegate().remove(); + } + + @Override + public E peek() { + return delegate().peek(); + } + + @Override + @ParametricNullness + public E element() { + return delegate().element(); + } + + /** + * A sensible definition of {@link #offer} in terms of {@link #add}. If you override {@link #add}, + * you may wish to override {@link #offer} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardOffer(@ParametricNullness E e) { + try { + return add(e); + } catch (IllegalStateException caught) { + return false; + } + } + + /** + * A sensible definition of {@link #peek} in terms of {@link #element}. If you override {@link + * #element}, you may wish to override {@link #peek} to forward to this implementation. + * + * @since 7.0 + */ + protected E standardPeek() { + try { + return element(); + } catch (NoSuchElementException caught) { + return null; + } + } + + /** + * A sensible definition of {@link #poll} in terms of {@link #remove}. If you override {@link + * #remove}, you may wish to override {@link #poll} to forward to this implementation. + * + * @since 7.0 + */ + protected E standardPoll() { + try { + return remove(); + } catch (NoSuchElementException caught) { + return null; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSet.java new file mode 100644 index 0000000000000..fd36a893594bc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSet.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A set which forwards all its method calls to another set. Subclasses should override one or more + * methods to modify the behavior of the backing set as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingSet} forward indiscriminately to the + * methods of the delegate. For example, overriding {@link #add} alone will not change the + * behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardAddAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingSet}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even when all of the + * methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingSet extends ForwardingCollection + implements Set { + // TODO(lowasser): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingSet() {} + + @Override + protected abstract Set delegate(); + + @Override + public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #removeAll} in terms of {@link #iterator} and {@link #remove}. + * If you override {@code iterator} or {@code remove}, you may wish to override {@link #removeAll} + * to forward to this implementation. + * + * @since 7.0 (this version overrides the {@code ForwardingCollection} version as of 12.0) + */ + @Override + protected boolean standardRemoveAll(Collection collection) { + return Sets.removeAllImpl(this, checkNotNull(collection)); // for GWT + } + + /** + * A sensible definition of {@link #equals} in terms of {@link #size} and {@link #containsAll}. If + * you override either of those methods, you may wish to override {@link #equals} to forward to + * this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + return Sets.equalsImpl(this, object); + } + + /** + * A sensible definition of {@link #hashCode} in terms of {@link #iterator}. If you override + * {@link #iterator}, you may wish to override {@link #equals} to forward to this implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + return Sets.hashCodeImpl(this); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSetMultimap.java new file mode 100644 index 0000000000000..c8fdbf856e71d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSetMultimap.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map.Entry; +import java.util.Set; + +/** + * A set multimap which forwards all its method calls to another set multimap. Subclasses should + * override one or more methods to modify the behavior of the backing multimap as desired per the decorator pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingSetMultimap}. + * + * @author Kurt Alfred Kluever + * @since 3.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingSetMultimap + extends ForwardingMultimap implements SetMultimap { + + @Override + protected abstract SetMultimap delegate(); + + @Override + public Set> entries() { + return delegate().entries(); + } + + @Override + public Set get(@ParametricNullness K key) { + return delegate().get(key); + } + + @Override + public Set removeAll(Object key) { + return delegate().removeAll(key); + } + + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + return delegate().replaceValues(key, values); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedMap.java new file mode 100644 index 0000000000000..ac563ef12842e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedMap.java @@ -0,0 +1,154 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.NoSuchElementException; +import java.util.SortedMap; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * A sorted map which forwards all its method calls to another sorted map. Subclasses should + * override one or more methods to modify the behavior of the backing sorted map as desired per the + * decorator pattern. + * + *

Warning: The methods of {@code ForwardingSortedMap} forward indiscriminately to + * the methods of the delegate. For example, overriding {@link #put} alone will not change + * the behavior of {@link #putAll}, which can lead to unexpected behavior. In this case, you should + * override {@code putAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardPutAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingSortedMap}. + * + *

Each of the {@code standard} methods, where appropriate, use the comparator of the map to test + * equality for both keys and values, unlike {@code ForwardingMap}. + * + *

The {@code standard} methods and the collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingSortedMap + extends ForwardingMap implements SortedMap { + // TODO(lowasser): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingSortedMap() {} + + @Override + protected abstract SortedMap delegate(); + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + @ParametricNullness + public K firstKey() { + return delegate().firstKey(); + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return delegate().headMap(toKey); + } + + @Override + @ParametricNullness + public K lastKey() { + return delegate().lastKey(); + } + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return delegate().subMap(fromKey, toKey); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return delegate().tailMap(fromKey); + } + + /** + * A sensible implementation of {@link SortedMap#keySet} in terms of the methods of {@code + * ForwardingSortedMap}. In many cases, you may wish to override {@link + * ForwardingSortedMap#keySet} to forward to this implementation or a subclass thereof. + * + * @since 15.0 + */ + protected class StandardKeySet extends Maps.SortedKeySet { + /** Constructor for use by subclasses. */ + public StandardKeySet() { + super(ForwardingSortedMap.this); + } + } + + // unsafe, but worst case is a CCE or NPE is thrown, which callers will be expecting + @SuppressWarnings({"unchecked", "nullness"}) + static int unsafeCompare(Comparator comparator, Object o1, Object o2) { + if (comparator == null) { + return ((Comparable) o1).compareTo(o2); + } else { + return ((Comparator) comparator).compare(o1, o2); + } + } + + /** + * A sensible definition of {@link #containsKey} in terms of the {@code firstKey()} method of + * {@link #tailMap}. If you override {@link #tailMap}, you may wish to override {@link + * #containsKey} to forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardContainsKey(Object key) { + try { + // any CCE or NPE will be caught + @SuppressWarnings({"unchecked", "nullness"}) + SortedMap self = (SortedMap) this; + Object ceilingKey = self.tailMap(key).firstKey(); + return unsafeCompare(comparator(), ceilingKey, key) == 0; + } catch (ClassCastException | NoSuchElementException | NullPointerException e) { + return false; + } + } + + /** + * A sensible default implementation of {@link #subMap(Object, Object)} in terms of {@link + * #headMap(Object)} and {@link #tailMap(Object)}. In some situations, you may wish to override + * {@link #subMap(Object, Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected SortedMap standardSubMap(K fromKey, K toKey) { + checkArgument(unsafeCompare(comparator(), fromKey, toKey) <= 0, "fromKey must be <= toKey"); + return tailMap(fromKey).headMap(toKey); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedMultiset.java new file mode 100644 index 0000000000000..9114052edc5e1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedMultiset.java @@ -0,0 +1,237 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; + +/** + * A sorted multiset which forwards all its method calls to another sorted multiset. Subclasses + * should override one or more methods to modify the behavior of the backing multiset as desired per + * the decorator pattern. + * + *

Warning: The methods of {@code ForwardingSortedMultiset} forward + * indiscriminately to the methods of the delegate. For example, overriding {@link + * #add(Object, int)} alone will not change the behavior of {@link #add(Object)}, which can + * lead to unexpected behavior. In this case, you should override {@code add(Object)} as well, + * either providing your own implementation, or delegating to the provided {@code standardAdd} + * method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingSortedMultiset}. + * + *

The {@code standard} methods and any collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Louis Wasserman + * @since 15.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class ForwardingSortedMultiset extends ForwardingMultiset + implements SortedMultiset { + /** Constructor for use by subclasses. */ + protected ForwardingSortedMultiset() {} + + @Override + protected abstract SortedMultiset delegate(); + + @Override + public NavigableSet elementSet() { + return delegate().elementSet(); + } + + /** + * A sensible implementation of {@link SortedMultiset#elementSet} in terms of the following + * methods: {@link SortedMultiset#clear}, {@link SortedMultiset#comparator}, {@link + * SortedMultiset#contains}, {@link SortedMultiset#containsAll}, {@link SortedMultiset#count}, + * {@link SortedMultiset#firstEntry} {@link SortedMultiset#headMultiset}, {@link + * SortedMultiset#isEmpty}, {@link SortedMultiset#lastEntry}, {@link SortedMultiset#subMultiset}, + * {@link SortedMultiset#tailMultiset}, the {@code size()} and {@code iterator()} methods of + * {@link SortedMultiset#entrySet}, and {@link SortedMultiset#remove(Object, int)}. In many + * situations, you may wish to override {@link SortedMultiset#elementSet} to forward to this + * implementation or a subclass thereof. + * + * @since 15.0 + */ + protected class StandardElementSet extends SortedMultisets.NavigableElementSet { + /** Constructor for use by subclasses. */ + public StandardElementSet() { + super(ForwardingSortedMultiset.this); + } + } + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + public SortedMultiset descendingMultiset() { + return delegate().descendingMultiset(); + } + + /** + * A skeleton implementation of a descending multiset view. Normally, {@link + * #descendingMultiset()} will not reflect any changes you make to the behavior of methods such as + * {@link #add(Object)} or {@link #pollFirstEntry}. This skeleton implementation correctly + * delegates each of its operations to the appropriate methods of this {@code + * ForwardingSortedMultiset}. + * + *

In many cases, you may wish to override {@link #descendingMultiset()} to return an instance + * of a subclass of {@code StandardDescendingMultiset}. + * + * @since 15.0 + */ + protected abstract class StandardDescendingMultiset extends DescendingMultiset { + /** Constructor for use by subclasses. */ + public StandardDescendingMultiset() {} + + @Override + SortedMultiset forwardMultiset() { + return ForwardingSortedMultiset.this; + } + } + + @Override + public Entry firstEntry() { + return delegate().firstEntry(); + } + + /** + * A sensible definition of {@link #firstEntry()} in terms of {@code entrySet().iterator()}. + * + *

If you override {@link #entrySet()}, you may wish to override {@link #firstEntry()} to + * forward to this implementation. + */ + protected Entry standardFirstEntry() { + Iterator> entryIterator = entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + return Multisets.immutableEntry(entry.getElement(), entry.getCount()); + } + + @Override + public Entry lastEntry() { + return delegate().lastEntry(); + } + + /** + * A sensible definition of {@link #lastEntry()} in terms of {@code + * descendingMultiset().entrySet().iterator()}. + * + *

If you override {@link #descendingMultiset} or {@link #entrySet()}, you may wish to override + * {@link #firstEntry()} to forward to this implementation. + */ + protected Entry standardLastEntry() { + Iterator> entryIterator = descendingMultiset().entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + return Multisets.immutableEntry(entry.getElement(), entry.getCount()); + } + + @Override + public Entry pollFirstEntry() { + return delegate().pollFirstEntry(); + } + + /** + * A sensible definition of {@link #pollFirstEntry()} in terms of {@code entrySet().iterator()}. + * + *

If you override {@link #entrySet()}, you may wish to override {@link #pollFirstEntry()} to + * forward to this implementation. + */ + protected Entry standardPollFirstEntry() { + Iterator> entryIterator = entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + entry = Multisets.immutableEntry(entry.getElement(), entry.getCount()); + entryIterator.remove(); + return entry; + } + + @Override + public Entry pollLastEntry() { + return delegate().pollLastEntry(); + } + + /** + * A sensible definition of {@link #pollLastEntry()} in terms of {@code + * descendingMultiset().entrySet().iterator()}. + * + *

If you override {@link #descendingMultiset()} or {@link #entrySet()}, you may wish to + * override {@link #pollLastEntry()} to forward to this implementation. + */ + protected Entry standardPollLastEntry() { + Iterator> entryIterator = descendingMultiset().entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } + Entry entry = entryIterator.next(); + entry = Multisets.immutableEntry(entry.getElement(), entry.getCount()); + entryIterator.remove(); + return entry; + } + + @Override + public SortedMultiset headMultiset(@ParametricNullness E upperBound, BoundType boundType) { + return delegate().headMultiset(upperBound, boundType); + } + + @Override + public SortedMultiset subMultiset( + @ParametricNullness E lowerBound, + BoundType lowerBoundType, + @ParametricNullness E upperBound, + BoundType upperBoundType) { + return delegate().subMultiset(lowerBound, lowerBoundType, upperBound, upperBoundType); + } + + /** + * A sensible definition of {@link #subMultiset(Object, BoundType, Object, BoundType)} in terms of + * {@link #headMultiset(Object, BoundType) headMultiset} and {@link #tailMultiset(Object, + * BoundType) tailMultiset}. + * + *

If you override either of these methods, you may wish to override {@link + * #subMultiset(Object, BoundType, Object, BoundType)} to forward to this implementation. + */ + protected SortedMultiset standardSubMultiset( + @ParametricNullness E lowerBound, + BoundType lowerBoundType, + @ParametricNullness E upperBound, + BoundType upperBoundType) { + return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); + } + + @Override + public SortedMultiset tailMultiset(@ParametricNullness E lowerBound, BoundType boundType) { + return delegate().tailMultiset(lowerBound, boundType); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedSet.java new file mode 100644 index 0000000000000..935c55812e91f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedSet.java @@ -0,0 +1,159 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import static org.apache.iotdb.google.common.collect.ForwardingSortedMap.unsafeCompare; + +/** + * A sorted set which forwards all its method calls to another sorted set. Subclasses should + * override one or more methods to modify the behavior of the backing sorted set as desired per the + * decorator pattern. + * + *

Warning: The methods of {@code ForwardingSortedSet} forward indiscriminately to + * the methods of the delegate. For example, overriding {@link #add} alone will not change + * the behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or delegating to the + * provided {@code standardAddAll} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingSortedSet}. + * + *

Each of the {@code standard} methods, where appropriate, uses the set's comparator (or the + * natural ordering of the elements, if there is no comparator) to test element equality. As a + * result, if the comparator is not consistent with equals, some of the standard implementations may + * violate the {@code Set} contract. + * + *

The {@code standard} methods and the collection views they return are not guaranteed to be + * thread-safe, even when all of the methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingSortedSet extends ForwardingSet + implements SortedSet { + + /** Constructor for use by subclasses. */ + protected ForwardingSortedSet() {} + + @Override + protected abstract SortedSet delegate(); + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + @ParametricNullness + public E first() { + return delegate().first(); + } + + @Override + public SortedSet headSet(@ParametricNullness E toElement) { + return delegate().headSet(toElement); + } + + @Override + @ParametricNullness + public E last() { + return delegate().last(); + } + + @Override + public SortedSet subSet(@ParametricNullness E fromElement, @ParametricNullness E toElement) { + return delegate().subSet(fromElement, toElement); + } + + @Override + public SortedSet tailSet(@ParametricNullness E fromElement) { + return delegate().tailSet(fromElement); + } + + /** + * A sensible definition of {@link #contains} in terms of the {@code first()} method of {@link + * #tailSet}. If you override {@link #tailSet}, you may wish to override {@link #contains} to + * forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardContains(Object object) { + try { + // any ClassCastExceptions and NullPointerExceptions are caught + @SuppressWarnings({"unchecked", "nullness"}) + SortedSet self = (SortedSet) this; + Object ceiling = self.tailSet(object).first(); + return unsafeCompare(comparator(), ceiling, object) == 0; + } catch (ClassCastException | NoSuchElementException | NullPointerException e) { + return false; + } + } + + /** + * A sensible definition of {@link #remove} in terms of the {@code iterator()} method of {@link + * #tailSet}. If you override {@link #tailSet}, you may wish to override {@link #remove} to + * forward to this implementation. + * + * @since 7.0 + */ + @Override + protected boolean standardRemove(Object object) { + try { + // any ClassCastExceptions and NullPointerExceptions are caught + @SuppressWarnings({"unchecked", "nullness"}) + SortedSet self = (SortedSet) this; + Iterator iterator = self.tailSet(object).iterator(); + if (iterator.hasNext()) { + Object ceiling = iterator.next(); + if (unsafeCompare(comparator(), ceiling, object) == 0) { + iterator.remove(); + return true; + } + } + } catch (ClassCastException | NullPointerException e) { + return false; + } + return false; + } + + /** + * A sensible default implementation of {@link #subSet(Object, Object)} in terms of {@link + * #headSet(Object)} and {@link #tailSet(Object)}. In some situations, you may wish to override + * {@link #subSet(Object, Object)} to forward to this implementation. + * + * @since 7.0 + */ + protected SortedSet standardSubSet( + @ParametricNullness E fromElement, @ParametricNullness E toElement) { + return tailSet(fromElement).headSet(toElement); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedSetMultimap.java new file mode 100644 index 0000000000000..e28528ffb1196 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingSortedSetMultimap.java @@ -0,0 +1,69 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.SortedSet; + +/** + * A sorted set multimap which forwards all its method calls to another sorted set multimap. + * Subclasses should override one or more methods to modify the behavior of the backing multimap as + * desired per the decorator pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingSortedSetMultimap}. + * + * @author Kurt Alfred Kluever + * @since 3.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingSortedSetMultimap + extends ForwardingSetMultimap implements SortedSetMultimap { + + /** Constructor for use by subclasses. */ + protected ForwardingSortedSetMultimap() {} + + @Override + protected abstract SortedSetMultimap delegate(); + + @Override + public SortedSet get(@ParametricNullness K key) { + return delegate().get(key); + } + + @Override + public SortedSet removeAll(Object key) { + return delegate().removeAll(key); + } + + @Override + public SortedSet replaceValues(@ParametricNullness K key, Iterable values) { + return delegate().replaceValues(key, values); + } + + @Override + public Comparator valueComparator() { + return delegate().valueComparator(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingTable.java new file mode 100644 index 0000000000000..076c845b36e74 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ForwardingTable.java @@ -0,0 +1,151 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A table which forwards all its method calls to another table. Subclasses should override one or + * more methods to modify the behavior of the backing map as desired per the decorator pattern. + * + * @author Gregory Kick + * @since 7.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingTable + extends ForwardingObject implements Table { + /** Constructor for use by subclasses. */ + protected ForwardingTable() {} + + @Override + protected abstract Table delegate(); + + @Override + public Set> cellSet() { + return delegate().cellSet(); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public Map column(@ParametricNullness C columnKey) { + return delegate().column(columnKey); + } + + @Override + public Set columnKeySet() { + return delegate().columnKeySet(); + } + + @Override + public Map> columnMap() { + return delegate().columnMap(); + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return delegate().contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return delegate().containsColumn(columnKey); + } + + @Override + public boolean containsRow(Object rowKey) { + return delegate().containsRow(rowKey); + } + + @Override + public boolean containsValue(Object value) { + return delegate().containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return delegate().get(rowKey, columnKey); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public V put( + @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { + return delegate().put(rowKey, columnKey, value); + } + + @Override + public void putAll(Table table) { + delegate().putAll(table); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + return delegate().remove(rowKey, columnKey); + } + + @Override + public Map row(@ParametricNullness R rowKey) { + return delegate().row(rowKey); + } + + @Override + public Set rowKeySet() { + return delegate().rowKeySet(); + } + + @Override + public Map> rowMap() { + return delegate().rowMap(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public Collection values() { + return delegate().values(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || delegate().equals(obj); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/GeneralRange.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/GeneralRange.java new file mode 100644 index 0000000000000..b4b8e501ff4ca --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/GeneralRange.java @@ -0,0 +1,317 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.io.Serializable; +import java.util.Comparator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.BoundType.CLOSED; +import static org.apache.iotdb.google.common.collect.BoundType.OPEN; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * A generalized interval on any ordering, for internal use. Supports {@code null}. Unlike {@link + * Range}, this allows the use of an arbitrary comparator. This is designed for use in the + * implementation of subcollections of sorted collection types. + * + *

Whenever possible, use {@code Range} instead, which is better supported. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class GeneralRange implements Serializable { + /** Converts a Range to a GeneralRange. */ + static GeneralRange from(Range range) { + T lowerEndpoint = range.hasLowerBound() ? range.lowerEndpoint() : null; + BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : OPEN; + + T upperEndpoint = range.hasUpperBound() ? range.upperEndpoint() : null; + BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : OPEN; + return new GeneralRange<>( + Ordering.natural(), + range.hasLowerBound(), + lowerEndpoint, + lowerBoundType, + range.hasUpperBound(), + upperEndpoint, + upperBoundType); + } + + /** Returns the whole range relative to the specified comparator. */ + static GeneralRange all(Comparator comparator) { + return new GeneralRange<>(comparator, false, null, OPEN, false, null, OPEN); + } + + /** + * Returns everything above the endpoint relative to the specified comparator, with the specified + * endpoint behavior. + */ + static GeneralRange downTo( + Comparator comparator, @ParametricNullness T endpoint, BoundType boundType) { + return new GeneralRange<>(comparator, true, endpoint, boundType, false, null, OPEN); + } + + /** + * Returns everything below the endpoint relative to the specified comparator, with the specified + * endpoint behavior. + */ + static GeneralRange upTo( + Comparator comparator, @ParametricNullness T endpoint, BoundType boundType) { + return new GeneralRange<>(comparator, false, null, OPEN, true, endpoint, boundType); + } + + /** + * Returns everything between the endpoints relative to the specified comparator, with the + * specified endpoint behavior. + */ + static GeneralRange range( + Comparator comparator, + @ParametricNullness T lower, + BoundType lowerType, + @ParametricNullness T upper, + BoundType upperType) { + return new GeneralRange<>(comparator, true, lower, lowerType, true, upper, upperType); + } + + private final Comparator comparator; + private final boolean hasLowerBound; + private final T lowerEndpoint; + private final BoundType lowerBoundType; + private final boolean hasUpperBound; + private final T upperEndpoint; + private final BoundType upperBoundType; + + private GeneralRange( + Comparator comparator, + boolean hasLowerBound, + T lowerEndpoint, + BoundType lowerBoundType, + boolean hasUpperBound, + T upperEndpoint, + BoundType upperBoundType) { + this.comparator = checkNotNull(comparator); + this.hasLowerBound = hasLowerBound; + this.hasUpperBound = hasUpperBound; + this.lowerEndpoint = lowerEndpoint; + this.lowerBoundType = checkNotNull(lowerBoundType); + this.upperEndpoint = upperEndpoint; + this.upperBoundType = checkNotNull(upperBoundType); + + // Trigger any exception that the comparator would throw for the endpoints. + /* + * uncheckedCastNullableTToT is safe as long as the callers are careful to pass a "real" T + * whenever they pass `true` for the matching `has*Bound` parameter. + */ + if (hasLowerBound) { + int unused = + comparator.compare( + uncheckedCastNullableTToT(lowerEndpoint), uncheckedCastNullableTToT(lowerEndpoint)); + } + if (hasUpperBound) { + int unused = + comparator.compare( + uncheckedCastNullableTToT(upperEndpoint), uncheckedCastNullableTToT(upperEndpoint)); + } + + if (hasLowerBound && hasUpperBound) { + int cmp = + comparator.compare( + uncheckedCastNullableTToT(lowerEndpoint), uncheckedCastNullableTToT(upperEndpoint)); + // be consistent with Range + checkArgument( + cmp <= 0, "lowerEndpoint (%s) > upperEndpoint (%s)", lowerEndpoint, upperEndpoint); + if (cmp == 0) { + checkArgument(lowerBoundType != OPEN || upperBoundType != OPEN); + } + } + } + + Comparator comparator() { + return comparator; + } + + boolean hasLowerBound() { + return hasLowerBound; + } + + boolean hasUpperBound() { + return hasUpperBound; + } + + boolean isEmpty() { + // The casts are safe because of the has*Bound() checks. + return (hasUpperBound() && tooLow(uncheckedCastNullableTToT(getUpperEndpoint()))) + || (hasLowerBound() && tooHigh(uncheckedCastNullableTToT(getLowerEndpoint()))); + } + + boolean tooLow(@ParametricNullness T t) { + if (!hasLowerBound()) { + return false; + } + // The cast is safe because of the hasLowerBound() check. + T lbound = uncheckedCastNullableTToT(getLowerEndpoint()); + int cmp = comparator.compare(t, lbound); + return cmp < 0 | (cmp == 0 & getLowerBoundType() == OPEN); + } + + boolean tooHigh(@ParametricNullness T t) { + if (!hasUpperBound()) { + return false; + } + // The cast is safe because of the hasUpperBound() check. + T ubound = uncheckedCastNullableTToT(getUpperEndpoint()); + int cmp = comparator.compare(t, ubound); + return cmp > 0 | (cmp == 0 & getUpperBoundType() == OPEN); + } + + boolean contains(@ParametricNullness T t) { + return !tooLow(t) && !tooHigh(t); + } + + /** + * Returns the intersection of the two ranges, or an empty range if their intersection is empty. + */ + @SuppressWarnings("nullness") // TODO(cpovirk): Add casts as needed. Will be noisy and annoying... + GeneralRange intersect(GeneralRange other) { + checkNotNull(other); + checkArgument(comparator.equals(other.comparator)); + + boolean hasLowBound = this.hasLowerBound; + T lowEnd = getLowerEndpoint(); + BoundType lowType = getLowerBoundType(); + if (!hasLowerBound()) { + hasLowBound = other.hasLowerBound; + lowEnd = other.getLowerEndpoint(); + lowType = other.getLowerBoundType(); + } else if (other.hasLowerBound()) { + int cmp = comparator.compare(getLowerEndpoint(), other.getLowerEndpoint()); + if (cmp < 0 || (cmp == 0 && other.getLowerBoundType() == OPEN)) { + lowEnd = other.getLowerEndpoint(); + lowType = other.getLowerBoundType(); + } + } + + boolean hasUpBound = this.hasUpperBound; + T upEnd = getUpperEndpoint(); + BoundType upType = getUpperBoundType(); + if (!hasUpperBound()) { + hasUpBound = other.hasUpperBound; + upEnd = other.getUpperEndpoint(); + upType = other.getUpperBoundType(); + } else if (other.hasUpperBound()) { + int cmp = comparator.compare(getUpperEndpoint(), other.getUpperEndpoint()); + if (cmp > 0 || (cmp == 0 && other.getUpperBoundType() == OPEN)) { + upEnd = other.getUpperEndpoint(); + upType = other.getUpperBoundType(); + } + } + + if (hasLowBound && hasUpBound) { + int cmp = comparator.compare(lowEnd, upEnd); + if (cmp > 0 || (cmp == 0 && lowType == OPEN && upType == OPEN)) { + // force allowed empty range + lowEnd = upEnd; + lowType = OPEN; + upType = CLOSED; + } + } + + return new GeneralRange<>(comparator, hasLowBound, lowEnd, lowType, hasUpBound, upEnd, upType); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof GeneralRange) { + GeneralRange r = (GeneralRange) obj; + return comparator.equals(r.comparator) + && hasLowerBound == r.hasLowerBound + && hasUpperBound == r.hasUpperBound + && getLowerBoundType().equals(r.getLowerBoundType()) + && getUpperBoundType().equals(r.getUpperBoundType()) + && Objects.equal(getLowerEndpoint(), r.getLowerEndpoint()) + && Objects.equal(getUpperEndpoint(), r.getUpperEndpoint()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode( + comparator, + getLowerEndpoint(), + getLowerBoundType(), + getUpperEndpoint(), + getUpperBoundType()); + } + + private transient GeneralRange reverse; + + /** Returns the same range relative to the reversed comparator. */ + GeneralRange reverse() { + GeneralRange result = reverse; + if (result == null) { + result = + new GeneralRange<>( + Ordering.from(comparator).reverse(), + hasUpperBound, + getUpperEndpoint(), + getUpperBoundType(), + hasLowerBound, + getLowerEndpoint(), + getLowerBoundType()); + result.reverse = this; + return this.reverse = result; + } + return result; + } + + @Override + public String toString() { + return comparator + + ":" + + (lowerBoundType == CLOSED ? '[' : '(') + + (hasLowerBound ? lowerEndpoint : "-\u221e") + + ',' + + (hasUpperBound ? upperEndpoint : "\u221e") + + (upperBoundType == CLOSED ? ']' : ')'); + } + + T getLowerEndpoint() { + return lowerEndpoint; + } + + BoundType getLowerBoundType() { + return lowerBoundType; + } + + T getUpperEndpoint() { + return upperEndpoint; + } + + BoundType getUpperBoundType() { + return upperBoundType; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/GwtTransient.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/GwtTransient.java new file mode 100644 index 0000000000000..1efb1be21833d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/GwtTransient.java @@ -0,0 +1,41 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Private replacement for {@link com.google.gwt.user.client.rpc.GwtTransient} to work around + * build-system quirks. This annotation should be used only in {@code + * org.apache.iotdb.google.common.collect}. + */ +@Documented +@GwtCompatible +@Retention(RUNTIME) +@Target(FIELD) +@ElementTypesAreNonnullByDefault +@interface GwtTransient {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashBasedTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashBasedTable.java new file mode 100644 index 0000000000000..68f1e7037dbd3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashBasedTable.java @@ -0,0 +1,110 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Implementation of {@link Table} using linked hash tables. This guarantees predictable iteration + * order of the various views. + * + *

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link #columnMap()} have + * iterators that don't support {@code remove()}. Otherwise, all optional operations are supported. + * Null row keys, columns keys, and values are not supported. + * + *

Lookups by row key are often faster than lookups by column key, because the data is stored in + * a {@code Map>}. A method call like {@code column(columnKey).get(rowKey)} still runs + * quickly, since the row key is provided. However, {@code column(columnKey).size()} takes longer, + * since an iteration across all row keys occurs. + * + *

Note that this implementation is not synchronized. If multiple threads access this table + * concurrently and one of the threads modifies the table, it must be synchronized externally. + * + *

See the Guava User Guide article on {@code Table}. + * + * @author Jared Levy + * @since 7.0 + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +public class HashBasedTable extends StandardTable { + private static class Factory implements Supplier>, Serializable { + final int expectedSize; + + Factory(int expectedSize) { + this.expectedSize = expectedSize; + } + + @Override + public Map get() { + return Maps.newLinkedHashMapWithExpectedSize(expectedSize); + } + + private static final long serialVersionUID = 0; + } + + /** Creates an empty {@code HashBasedTable}. */ + public static HashBasedTable create() { + return new HashBasedTable<>(new LinkedHashMap>(), new Factory(0)); + } + + /** + * Creates an empty {@code HashBasedTable} with the specified map sizes. + * + * @param expectedRows the expected number of distinct row keys + * @param expectedCellsPerRow the expected number of column key / value mappings in each row + * @throws IllegalArgumentException if {@code expectedRows} or {@code expectedCellsPerRow} is + * negative + */ + public static HashBasedTable create( + int expectedRows, int expectedCellsPerRow) { + checkNonnegative(expectedCellsPerRow, "expectedCellsPerRow"); + Map> backingMap = Maps.newLinkedHashMapWithExpectedSize(expectedRows); + return new HashBasedTable<>(backingMap, new Factory(expectedCellsPerRow)); + } + + /** + * Creates a {@code HashBasedTable} with the same mappings as the specified table. + * + * @param table the table to copy + * @throws NullPointerException if any of the row keys, column keys, or values in {@code table} is + * null + */ + public static HashBasedTable create( + Table table) { + HashBasedTable result = create(); + result.putAll(table); + return result; + } + + HashBasedTable(Map> backingMap, Factory factory) { + super(backingMap, factory); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashBiMap.java new file mode 100644 index 0000000000000..e1f43f8e4d03f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashBiMap.java @@ -0,0 +1,794 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Maps.IteratorBasedAbstractMap; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.Hashing.smearedHash; + +/** + * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A + * {@code HashBiMap} and its inverse are both serializable. + * + *

This implementation guarantees insertion-based iteration order of its keys. + * + *

See the Guava User Guide article on {@code BiMap} . + * + * @author Louis Wasserman + * @author Mike Bostock + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class HashBiMap + extends IteratorBasedAbstractMap implements BiMap, Serializable { + + /** Returns a new, empty {@code HashBiMap} with the default initial capacity (16). */ + public static HashBiMap create() { + return create(16); + } + + /** + * Constructs a new, empty bimap with the specified expected size. + * + * @param expectedSize the expected number of entries + * @throws IllegalArgumentException if the specified expected size is negative + */ + public static HashBiMap create(int expectedSize) { + return new HashBiMap<>(expectedSize); + } + + /** + * Constructs a new bimap containing initial values from {@code map}. The bimap is created with an + * initial capacity sufficient to hold the mappings in the specified map. + */ + public static HashBiMap create( + Map map) { + HashBiMap bimap = create(map.size()); + bimap.putAll(map); + return bimap; + } + + private static final class BiEntry + extends ImmutableEntry { + final int keyHash; + final int valueHash; + + // All BiEntry instances are strongly reachable from owning HashBiMap through + // "HashBiMap.hashTableKToV" and "BiEntry.nextInKToVBucket" references. + // Under that assumption, the remaining references can be safely marked as @Weak. + // Using @Weak is necessary to avoid retain-cycles between BiEntry instances on iOS, + // which would cause memory leaks when non-empty HashBiMap with cyclic BiEntry + // instances is deallocated. + BiEntry nextInKToVBucket; + BiEntry nextInVToKBucket; + + BiEntry nextInKeyInsertionOrder; + BiEntry prevInKeyInsertionOrder; + + BiEntry(@ParametricNullness K key, int keyHash, @ParametricNullness V value, int valueHash) { + super(key, value); + this.keyHash = keyHash; + this.valueHash = valueHash; + } + } + + private static final double LOAD_FACTOR = 1.0; + + /* + * The following two arrays may *contain* nulls, but they are never *themselves* null: Even though + * they are not initialized inline in the constructor, they are initialized from init(), which the + * constructor calls (as does readObject()). + */ + @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (see above) + private transient BiEntry[] hashTableKToV; + + @SuppressWarnings("nullness:initialization.field.uninitialized") // For J2KT (see above) + private transient BiEntry[] hashTableVToK; + + private transient BiEntry firstInKeyInsertionOrder; + private transient BiEntry lastInKeyInsertionOrder; + private transient int size; + private transient int mask; + private transient int modCount; + + private HashBiMap(int expectedSize) { + init(expectedSize); + } + + private void init(int expectedSize) { + checkNonnegative(expectedSize, "expectedSize"); + int tableSize = Hashing.closedTableSize(expectedSize, LOAD_FACTOR); + this.hashTableKToV = createTable(tableSize); + this.hashTableVToK = createTable(tableSize); + this.firstInKeyInsertionOrder = null; + this.lastInKeyInsertionOrder = null; + this.size = 0; + this.mask = tableSize - 1; + this.modCount = 0; + } + + /** + * Finds and removes {@code entry} from the bucket linked lists in both the key-to-value direction + * and the value-to-key direction. + */ + private void delete(BiEntry entry) { + int keyBucket = entry.keyHash & mask; + BiEntry prevBucketEntry = null; + for (BiEntry bucketEntry = hashTableKToV[keyBucket]; + true; + bucketEntry = bucketEntry.nextInKToVBucket) { + if (bucketEntry == entry) { + if (prevBucketEntry == null) { + hashTableKToV[keyBucket] = entry.nextInKToVBucket; + } else { + prevBucketEntry.nextInKToVBucket = entry.nextInKToVBucket; + } + break; + } + prevBucketEntry = bucketEntry; + } + + int valueBucket = entry.valueHash & mask; + prevBucketEntry = null; + for (BiEntry bucketEntry = hashTableVToK[valueBucket]; + true; + bucketEntry = bucketEntry.nextInVToKBucket) { + if (bucketEntry == entry) { + if (prevBucketEntry == null) { + hashTableVToK[valueBucket] = entry.nextInVToKBucket; + } else { + prevBucketEntry.nextInVToKBucket = entry.nextInVToKBucket; + } + break; + } + prevBucketEntry = bucketEntry; + } + + if (entry.prevInKeyInsertionOrder == null) { + firstInKeyInsertionOrder = entry.nextInKeyInsertionOrder; + } else { + entry.prevInKeyInsertionOrder.nextInKeyInsertionOrder = entry.nextInKeyInsertionOrder; + } + + if (entry.nextInKeyInsertionOrder == null) { + lastInKeyInsertionOrder = entry.prevInKeyInsertionOrder; + } else { + entry.nextInKeyInsertionOrder.prevInKeyInsertionOrder = entry.prevInKeyInsertionOrder; + } + + size--; + modCount++; + } + + private void insert(BiEntry entry, BiEntry oldEntryForKey) { + int keyBucket = entry.keyHash & mask; + entry.nextInKToVBucket = hashTableKToV[keyBucket]; + hashTableKToV[keyBucket] = entry; + + int valueBucket = entry.valueHash & mask; + entry.nextInVToKBucket = hashTableVToK[valueBucket]; + hashTableVToK[valueBucket] = entry; + + if (oldEntryForKey == null) { + entry.prevInKeyInsertionOrder = lastInKeyInsertionOrder; + entry.nextInKeyInsertionOrder = null; + if (lastInKeyInsertionOrder == null) { + firstInKeyInsertionOrder = entry; + } else { + lastInKeyInsertionOrder.nextInKeyInsertionOrder = entry; + } + lastInKeyInsertionOrder = entry; + } else { + entry.prevInKeyInsertionOrder = oldEntryForKey.prevInKeyInsertionOrder; + if (entry.prevInKeyInsertionOrder == null) { + firstInKeyInsertionOrder = entry; + } else { + entry.prevInKeyInsertionOrder.nextInKeyInsertionOrder = entry; + } + entry.nextInKeyInsertionOrder = oldEntryForKey.nextInKeyInsertionOrder; + if (entry.nextInKeyInsertionOrder == null) { + lastInKeyInsertionOrder = entry; + } else { + entry.nextInKeyInsertionOrder.prevInKeyInsertionOrder = entry; + } + } + + size++; + modCount++; + } + + private BiEntry seekByKey(Object key, int keyHash) { + for (BiEntry entry = hashTableKToV[keyHash & mask]; + entry != null; + entry = entry.nextInKToVBucket) { + if (keyHash == entry.keyHash && Objects.equal(key, entry.key)) { + return entry; + } + } + return null; + } + + private BiEntry seekByValue(Object value, int valueHash) { + for (BiEntry entry = hashTableVToK[valueHash & mask]; + entry != null; + entry = entry.nextInVToKBucket) { + if (valueHash == entry.valueHash && Objects.equal(value, entry.value)) { + return entry; + } + } + return null; + } + + @Override + public boolean containsKey(Object key) { + return seekByKey(key, smearedHash(key)) != null; + } + + /** + * Returns {@code true} if this BiMap contains an entry whose value is equal to {@code value} (or, + * equivalently, if this inverse view contains a key that is equal to {@code value}). + * + *

Due to the property that values in a BiMap are unique, this will tend to execute in + * faster-than-linear time. + * + * @param value the object to search for in the values of this BiMap + * @return true if a mapping exists from a key to the specified value + */ + @Override + public boolean containsValue(Object value) { + return seekByValue(value, smearedHash(value)) != null; + } + + @Override + public V get(Object key) { + return Maps.valueOrNull(seekByKey(key, smearedHash(key))); + } + + @Override + public V put(@ParametricNullness K key, @ParametricNullness V value) { + return put(key, value, false); + } + + private V put(@ParametricNullness K key, @ParametricNullness V value, boolean force) { + int keyHash = smearedHash(key); + int valueHash = smearedHash(value); + + BiEntry oldEntryForKey = seekByKey(key, keyHash); + if (oldEntryForKey != null + && valueHash == oldEntryForKey.valueHash + && Objects.equal(value, oldEntryForKey.value)) { + return value; + } + + BiEntry oldEntryForValue = seekByValue(value, valueHash); + if (oldEntryForValue != null) { + if (force) { + delete(oldEntryForValue); + } else { + throw new IllegalArgumentException("value already present: " + value); + } + } + + BiEntry newEntry = new BiEntry<>(key, keyHash, value, valueHash); + if (oldEntryForKey != null) { + delete(oldEntryForKey); + insert(newEntry, oldEntryForKey); + oldEntryForKey.prevInKeyInsertionOrder = null; + oldEntryForKey.nextInKeyInsertionOrder = null; + return oldEntryForKey.value; + } else { + insert(newEntry, null); + rehashIfNecessary(); + return null; + } + } + + @Override + public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + return put(key, value, true); + } + + private K putInverse(@ParametricNullness V value, @ParametricNullness K key, boolean force) { + int valueHash = smearedHash(value); + int keyHash = smearedHash(key); + + BiEntry oldEntryForValue = seekByValue(value, valueHash); + BiEntry oldEntryForKey = seekByKey(key, keyHash); + if (oldEntryForValue != null + && keyHash == oldEntryForValue.keyHash + && Objects.equal(key, oldEntryForValue.key)) { + return key; + } else if (oldEntryForKey != null && !force) { + throw new IllegalArgumentException("key already present: " + key); + } + + /* + * The ordering here is important: if we deleted the key entry and then the value entry, + * the key entry's prev or next pointer might point to the dead value entry, and when we + * put the new entry in the key entry's position in iteration order, it might invalidate + * the linked list. + */ + + if (oldEntryForValue != null) { + delete(oldEntryForValue); + } + + if (oldEntryForKey != null) { + delete(oldEntryForKey); + } + + BiEntry newEntry = new BiEntry<>(key, keyHash, value, valueHash); + insert(newEntry, oldEntryForKey); + + if (oldEntryForKey != null) { + oldEntryForKey.prevInKeyInsertionOrder = null; + oldEntryForKey.nextInKeyInsertionOrder = null; + } + if (oldEntryForValue != null) { + oldEntryForValue.prevInKeyInsertionOrder = null; + oldEntryForValue.nextInKeyInsertionOrder = null; + } + rehashIfNecessary(); + return Maps.keyOrNull(oldEntryForValue); + } + + private void rehashIfNecessary() { + BiEntry[] oldKToV = hashTableKToV; + if (Hashing.needsResizing(size, oldKToV.length, LOAD_FACTOR)) { + int newTableSize = oldKToV.length * 2; + + this.hashTableKToV = createTable(newTableSize); + this.hashTableVToK = createTable(newTableSize); + this.mask = newTableSize - 1; + this.size = 0; + + for (BiEntry entry = firstInKeyInsertionOrder; + entry != null; + entry = entry.nextInKeyInsertionOrder) { + insert(entry, entry); + } + this.modCount++; + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + private BiEntry[] createTable(int length) { + return new BiEntry[length]; + } + + @Override + public V remove(Object key) { + BiEntry entry = seekByKey(key, smearedHash(key)); + if (entry == null) { + return null; + } else { + delete(entry); + entry.prevInKeyInsertionOrder = null; + entry.nextInKeyInsertionOrder = null; + return entry.value; + } + } + + @Override + public void clear() { + size = 0; + Arrays.fill(hashTableKToV, null); + Arrays.fill(hashTableVToK, null); + firstInKeyInsertionOrder = null; + lastInKeyInsertionOrder = null; + modCount++; + } + + @Override + public int size() { + return size; + } + + abstract class Itr implements Iterator { + BiEntry next = firstInKeyInsertionOrder; + BiEntry toRemove = null; + int expectedModCount = modCount; + int remaining = size(); + + @Override + public boolean hasNext() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + return next != null && remaining > 0; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + // requireNonNull is safe because of the hasNext check. + BiEntry entry = requireNonNull(next); + next = entry.nextInKeyInsertionOrder; + toRemove = entry; + remaining--; + return output(entry); + } + + @Override + public void remove() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + if (toRemove == null) { + throw new IllegalStateException("no calls to next() since the last call to remove()"); + } + delete(toRemove); + expectedModCount = modCount; + toRemove = null; + } + + abstract T output(BiEntry entry); + } + + @Override + public Set keySet() { + return new KeySet(); + } + + private final class KeySet extends Maps.KeySet { + KeySet() { + super(HashBiMap.this); + } + + @Override + public Iterator iterator() { + return new Itr() { + @Override + @ParametricNullness + K output(BiEntry entry) { + return entry.key; + } + }; + } + + @Override + public boolean remove(Object o) { + BiEntry entry = seekByKey(o, smearedHash(o)); + if (entry == null) { + return false; + } else { + delete(entry); + entry.prevInKeyInsertionOrder = null; + entry.nextInKeyInsertionOrder = null; + return true; + } + } + } + + @Override + public Set values() { + return inverse().keySet(); + } + + @Override + Iterator> entryIterator() { + return new Itr>() { + @Override + Entry output(BiEntry entry) { + return new MapEntry(entry); + } + + class MapEntry extends AbstractMapEntry { + BiEntry delegate; + + MapEntry(BiEntry entry) { + this.delegate = entry; + } + + @Override + @ParametricNullness + public K getKey() { + return delegate.key; + } + + @Override + @ParametricNullness + public V getValue() { + return delegate.value; + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V value) { + V oldValue = delegate.value; + int valueHash = smearedHash(value); + if (valueHash == delegate.valueHash && Objects.equal(value, oldValue)) { + return value; + } + checkArgument(seekByValue(value, valueHash) == null, "value already present: %s", value); + delete(delegate); + BiEntry newEntry = new BiEntry<>(delegate.key, delegate.keyHash, value, valueHash); + insert(newEntry, delegate); + delegate.prevInKeyInsertionOrder = null; + delegate.nextInKeyInsertionOrder = null; + expectedModCount = modCount; + if (toRemove == delegate) { + toRemove = newEntry; + } + delegate = newEntry; + return oldValue; + } + } + }; + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + for (BiEntry entry = firstInKeyInsertionOrder; + entry != null; + entry = entry.nextInKeyInsertionOrder) { + action.accept(entry.key, entry.value); + } + } + + @Override + public void replaceAll(BiFunction function) { + checkNotNull(function); + BiEntry oldFirst = firstInKeyInsertionOrder; + clear(); + for (BiEntry entry = oldFirst; entry != null; entry = entry.nextInKeyInsertionOrder) { + put(entry.key, function.apply(entry.key, entry.value)); + } + } + + private transient BiMap inverse; + + @Override + public BiMap inverse() { + BiMap result = inverse; + return (result == null) ? inverse = new Inverse() : result; + } + + private final class Inverse extends IteratorBasedAbstractMap + implements BiMap, Serializable { + BiMap forward() { + return HashBiMap.this; + } + + @Override + public int size() { + return size; + } + + @Override + public void clear() { + forward().clear(); + } + + @Override + public boolean containsKey(Object value) { + return forward().containsValue(value); + } + + @Override + public K get(Object value) { + return Maps.keyOrNull(seekByValue(value, smearedHash(value))); + } + + @Override + public K put(@ParametricNullness V value, @ParametricNullness K key) { + return putInverse(value, key, false); + } + + @Override + public K forcePut(@ParametricNullness V value, @ParametricNullness K key) { + return putInverse(value, key, true); + } + + @Override + public K remove(Object value) { + BiEntry entry = seekByValue(value, smearedHash(value)); + if (entry == null) { + return null; + } else { + delete(entry); + entry.prevInKeyInsertionOrder = null; + entry.nextInKeyInsertionOrder = null; + return entry.key; + } + } + + @Override + public BiMap inverse() { + return forward(); + } + + @Override + public Set keySet() { + return new InverseKeySet(); + } + + private final class InverseKeySet extends Maps.KeySet { + InverseKeySet() { + super(Inverse.this); + } + + @Override + public boolean remove(Object o) { + BiEntry entry = seekByValue(o, smearedHash(o)); + if (entry == null) { + return false; + } else { + delete(entry); + return true; + } + } + + @Override + public Iterator iterator() { + return new Itr() { + @Override + @ParametricNullness + V output(BiEntry entry) { + return entry.value; + } + }; + } + } + + @Override + public Set values() { + return forward().keySet(); + } + + @Override + Iterator> entryIterator() { + return new Itr>() { + @Override + Entry output(BiEntry entry) { + return new InverseEntry(entry); + } + + class InverseEntry extends AbstractMapEntry { + BiEntry delegate; + + InverseEntry(BiEntry entry) { + this.delegate = entry; + } + + @Override + @ParametricNullness + public V getKey() { + return delegate.value; + } + + @Override + @ParametricNullness + public K getValue() { + return delegate.key; + } + + @Override + @ParametricNullness + public K setValue(@ParametricNullness K key) { + K oldKey = delegate.key; + int keyHash = smearedHash(key); + if (keyHash == delegate.keyHash && Objects.equal(key, oldKey)) { + return key; + } + checkArgument(seekByKey(key, keyHash) == null, "value already present: %s", key); + delete(delegate); + BiEntry newEntry = + new BiEntry<>(key, keyHash, delegate.value, delegate.valueHash); + delegate = newEntry; + insert(newEntry, null); + expectedModCount = modCount; + return oldKey; + } + } + }; + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + HashBiMap.this.forEach((k, v) -> action.accept(v, k)); + } + + @Override + public void replaceAll(BiFunction function) { + checkNotNull(function); + BiEntry oldFirst = firstInKeyInsertionOrder; + clear(); + for (BiEntry entry = oldFirst; entry != null; entry = entry.nextInKeyInsertionOrder) { + put(entry.value, function.apply(entry.value, entry.key)); + } + } + + Object writeReplace() { + return new InverseSerializedForm<>(HashBiMap.this); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use InverseSerializedForm"); + } + } + + private static final class InverseSerializedForm + implements Serializable { + private final HashBiMap bimap; + + InverseSerializedForm(HashBiMap bimap) { + this.bimap = bimap; + } + + Object readResolve() { + return bimap.inverse(); + } + } + + /** + * @serialData the number of entries, first key, first value, second key, second value, and so on. + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMap(this, stream); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int size = Serialization.readCount(stream); + init(16); // resist hostile attempts to allocate gratuitous heap + Serialization.populateMap(this, stream, size); + } + + @GwtIncompatible // Not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultimap.java new file mode 100644 index 0000000000000..2caf5823cfe15 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultimap.java @@ -0,0 +1,157 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Implementation of {@link Multimap} using hash tables. + * + *

The multimap does not store duplicate key-value pairs. Adding a new key-value pair equal to an + * existing key-value pair has no effect. + * + *

Keys and values may be null. All optional multimap methods are supported, and all returned + * views are modifiable. + * + *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent + * read operations will work correctly if the last write happens-before any reads. To allow + * concurrent update operations, wrap your multimap with a call to {@link + * Multimaps#synchronizedSetMultimap}. + * + *

Warning: Do not modify either a key or a value of a {@code HashMultimap} in a + * way that affects its {@link Object#equals} behavior. Undefined behavior and bugs will result. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public final class HashMultimap + extends HashMultimapGwtSerializationDependencies { + private static final int DEFAULT_VALUES_PER_KEY = 2; + + @VisibleForTesting transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + + /** + * Creates a new, empty {@code HashMultimap} with the default initial capacities. + * + *

This method will soon be deprecated in favor of {@code + * MultimapBuilder.hashKeys().hashSetValues().build()}. + */ + public static HashMultimap create() { + return new HashMultimap<>(); + } + + /** + * Constructs an empty {@code HashMultimap} with enough capacity to hold the specified numbers of + * keys and values without rehashing. + * + *

This method will soon be deprecated in favor of {@code + * MultimapBuilder.hashKeys(expectedKeys).hashSetValues(expectedValuesPerKey).build()}. + * + * @param expectedKeys the expected number of distinct keys + * @param expectedValuesPerKey the expected average number of values per key + * @throws IllegalArgumentException if {@code expectedKeys} or {@code expectedValuesPerKey} is + * negative + */ + public static HashMultimap create( + int expectedKeys, int expectedValuesPerKey) { + return new HashMultimap<>(expectedKeys, expectedValuesPerKey); + } + + /** + * Constructs a {@code HashMultimap} with the same mappings as the specified multimap. If a + * key-value mapping appears multiple times in the input multimap, it only appears once in the + * constructed multimap. + * + *

This method will soon be deprecated in favor of {@code + * MultimapBuilder.hashKeys().hashSetValues().build(multimap)}. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static HashMultimap create( + Multimap multimap) { + return new HashMultimap<>(multimap); + } + + private HashMultimap() { + this(12, DEFAULT_VALUES_PER_KEY); + } + + private HashMultimap(int expectedKeys, int expectedValuesPerKey) { + super(Platform.>newHashMapWithExpectedSize(expectedKeys)); + Preconditions.checkArgument(expectedValuesPerKey >= 0); + this.expectedValuesPerKey = expectedValuesPerKey; + } + + private HashMultimap(Multimap multimap) { + super(Platform.>newHashMapWithExpectedSize(multimap.keySet().size())); + putAll(multimap); + } + + /** + * {@inheritDoc} + * + *

Creates an empty {@code HashSet} for a collection of values for one key. + * + * @return a new {@code HashSet} containing a collection of values for one key + */ + @Override + Set createCollection() { + return Platform.newHashSetWithExpectedSize(expectedValuesPerKey); + } + + /** + * @serialData expectedValuesPerKey, number of distinct keys, and then for each distinct key: the + * key, number of values for that key, and the key's values + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + int distinctKeys = Serialization.readCount(stream); + Map> map = Platform.newHashMapWithExpectedSize(12); + setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } + + @GwtIncompatible // Not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultimapGwtSerializationDependencies.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultimapGwtSerializationDependencies.java new file mode 100644 index 0000000000000..0f4ae7e0a3e82 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultimapGwtSerializationDependencies.java @@ -0,0 +1,41 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Map; + +/** + * A dummy superclass to support GWT serialization of the element types of a {@link HashMultimap}. + * The GWT supersource for this class contains a field for each type. + * + *

For details about this hack, see {@code GwtSerializationDependencies}, which takes the same + * approach but with a subclass rather than a superclass. + * + *

TODO(cpovirk): Consider applying this subclass approach to our other types. + */ +@GwtCompatible(emulated = true) +abstract class HashMultimapGwtSerializationDependencies extends AbstractSetMultimap { + HashMultimapGwtSerializationDependencies(Map> map) { + super(map); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultiset.java new file mode 100644 index 0000000000000..66f482195bdab --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/HashMultiset.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; + +/** + * Multiset implementation backed by a {@link HashMap}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public final class HashMultiset extends AbstractMapBasedMultiset { + + /** Creates a new, empty {@code HashMultiset} using the default initial capacity. */ + public static HashMultiset create() { + return new HashMultiset(); + } + + /** + * Creates a new, empty {@code HashMultiset} with the specified expected number of distinct + * elements. + * + * @param distinctElements the expected number of distinct elements + * @throws IllegalArgumentException if {@code distinctElements} is negative + */ + public static HashMultiset create(int distinctElements) { + return new HashMultiset(distinctElements); + } + + /** + * Creates a new {@code HashMultiset} containing the specified elements. + * + *

This implementation is highly efficient when {@code elements} is itself a {@link Multiset}. + * + * @param elements the elements that the multiset should contain + */ + public static HashMultiset create(Iterable elements) { + HashMultiset multiset = create(Multisets.inferDistinctElements(elements)); + Iterables.addAll(multiset, elements); + return multiset; + } + + private HashMultiset() { + super(new HashMap()); + } + + private HashMultiset(int distinctElements) { + super(Maps.newHashMapWithExpectedSize(distinctElements)); + } + + /** + * @serialData the number of distinct elements, the first element, its count, the second element, + * its count, and so on + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int distinctElements = Serialization.readCount(stream); + setBackingMap(Maps.newHashMap()); + Serialization.populateMultiset(this, stream, distinctElements); + } + + @GwtIncompatible // Not needed in emulated source. + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Hashing.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Hashing.java new file mode 100644 index 0000000000000..3e4a2707fe6af --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Hashing.java @@ -0,0 +1,78 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.primitives.Ints; + +/** + * Static methods for implementing hash-based collections. + * + * @author Kevin Bourrillion + * @author Jesse Wilson + * @author Austin Appleby + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class Hashing { + private Hashing() {} + + /* + * These should be ints, but we need to use longs to force GWT to do the multiplications with + * enough precision. + */ + private static final long C1 = 0xcc9e2d51; + private static final long C2 = 0x1b873593; + + /* + * This method was rewritten in Java from an intermediate step of the Murmur hash function in + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the + * following header: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author + * hereby disclaims copyright to this source code. + */ + static int smear(int hashCode) { + return (int) (C2 * Integer.rotateLeft((int) (hashCode * C1), 15)); + } + + static int smearedHash(Object o) { + return smear((o == null) ? 0 : o.hashCode()); + } + + private static final int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; + + static int closedTableSize(int expectedEntries, double loadFactor) { + // Get the recommended table size. + // Round down to the nearest power of 2. + expectedEntries = Math.max(expectedEntries, 2); + int tableSize = Integer.highestOneBit(expectedEntries); + // Check to make sure that we will not exceed the maximum load factor. + if (expectedEntries > (int) (loadFactor * tableSize)) { + tableSize <<= 1; + return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE; + } + return tableSize; + } + + static boolean needsResizing(int size, int tableSize, double loadFactor) { + return size > loadFactor * tableSize && tableSize < MAX_TABLE_SIZE; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/IgnoreJRERequirement.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/IgnoreJRERequirement.java new file mode 100644 index 0000000000000..93fcd02b1425c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/IgnoreJRERequirement.java @@ -0,0 +1,35 @@ +/* + * 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.iotdb.google.common.collect; + +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@ElementTypesAreNonnullByDefault +@interface IgnoreJRERequirement {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableAsList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableAsList.java new file mode 100644 index 0000000000000..86c4cc57ea877 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableAsList.java @@ -0,0 +1,94 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +/** + * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks to the + * backing collection. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") +@ElementTypesAreNonnullByDefault +abstract class ImmutableAsList extends ImmutableList { + abstract ImmutableCollection delegateCollection(); + + @Override + public boolean contains(Object target) { + // The collection's contains() is at least as fast as ImmutableList's + // and is often faster. + return delegateCollection().contains(target); + } + + @Override + public int size() { + return delegateCollection().size(); + } + + @Override + public boolean isEmpty() { + return delegateCollection().isEmpty(); + } + + @Override + boolean isPartialView() { + return delegateCollection().isPartialView(); + } + + /** Serialized form that leads to the same performance as the original list. */ + @GwtIncompatible // serialization + @J2ktIncompatible + static class SerializedForm implements Serializable { + final ImmutableCollection collection; + + SerializedForm(ImmutableCollection collection) { + this.collection = collection; + } + + Object readResolve() { + return collection.asList(); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + @Override + Object writeReplace() { + return new SerializedForm(delegateCollection()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableBiMap.java new file mode 100644 index 0000000000000..010703859a54d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableBiMap.java @@ -0,0 +1,635 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * A {@link BiMap} whose contents will never change, with many other important properties detailed + * at {@link ImmutableCollection}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class ImmutableBiMap extends ImmutableBiMapFauxverideShim + implements BiMap { + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableBiMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. + * Entries appear in the result {@code ImmutableBiMap} in encounter order. + * + *

If the mapped keys or values contain duplicates (according to {@link + * Object#equals(Object)}), an {@code IllegalArgumentException} is thrown when the collection + * operation is performed. (This differs from the {@code Collector} returned by {@link + * Collectors#toMap(Function, Function)}, which throws an {@code IllegalStateException}.) + * + * @since 21.0 + */ + public static Collector> toImmutableBiMap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableBiMap(keyFunction, valueFunction); + } + + /** + * Returns the empty bimap. + * + *

Performance note: the instance returned is a singleton. + */ + // Casting to any type is safe because the set will never hold any elements. + @SuppressWarnings("unchecked") + public static ImmutableBiMap of() { + return (ImmutableBiMap) RegularImmutableBiMap.EMPTY; + } + + /** Returns an immutable bimap containing a single entry. */ + public static ImmutableBiMap of(K k1, V v1) { + return new SingletonImmutableBiMap<>(k1, v1); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + */ + public static ImmutableBiMap of(K k1, V v1, K k2, V v2) { + return RegularImmutableBiMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + */ + public static ImmutableBiMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return RegularImmutableBiMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + */ + public static ImmutableBiMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + */ + public static ImmutableBiMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + * @since 31.0 + */ + public static ImmutableBiMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + * @since 31.0 + */ + public static ImmutableBiMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + * @since 31.0 + */ + public static ImmutableBiMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + * @since 31.0 + */ + public static ImmutableBiMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8), + entryOf(k9, v9)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + * @since 31.0 + */ + public static ImmutableBiMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9, + K k10, + V v10) { + return RegularImmutableBiMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8), + entryOf(k9, v9), + entryOf(k10, v10)); + } + + // looking for of() with > 10 entries? Use the builder or ofEntries instead. + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are provided + * @since 31.0 + */ + @SafeVarargs + public static ImmutableBiMap ofEntries(Entry... entries) { + @SuppressWarnings("unchecked") // we will only ever read these + Entry[] entries2 = (Entry[]) entries; + return RegularImmutableBiMap.fromEntries(entries2); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * Returns a new builder, expecting the specified number of entries to be added. + * + *

If {@code expectedSize} is exactly the number of entries added to the builder before {@link + * Builder#build} is called, the builder is likely to perform better than an unsized {@link + * #builder()} would have. + * + *

It is not specified if any performance benefits apply if {@code expectedSize} is close to, + * but not exactly, the number of entries added to the builder. + * + * @since 23.1 + */ + public static Builder builderWithExpectedSize(int expectedSize) { + checkNonnegative(expectedSize, "expectedSize"); + return new Builder<>(expectedSize); + } + + /** + * A builder for creating immutable bimap instances, especially {@code public static final} bimaps + * ("constant bimaps"). Example: + * + *

{@code
+   * static final ImmutableBiMap WORD_TO_INT =
+   *     new ImmutableBiMap.Builder()
+   *         .put("one", 1)
+   *         .put("two", 2)
+   *         .put("three", 3)
+   *         .buildOrThrow();
+   * }
+ * + *

For small immutable bimaps, the {@code ImmutableBiMap.of()} methods are even more + * convenient. + * + *

By default, a {@code Builder} will generate bimaps that iterate over entries in the order + * they were inserted into the builder. For example, in the above example, {@code + * WORD_TO_INT.entrySet()} is guaranteed to iterate over the entries in the order {@code "one"=1, + * "two"=2, "three"=3}, and {@code keySet()} and {@code values()} respect the same order. If you + * want a different order, consider using {@link #orderEntriesByValue(Comparator)}, which changes + * this builder to sort entries by value. + * + *

Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to + * build multiple bimaps in series. Each bimap is a superset of the bimaps created before it. + * + * @since 2.0 + */ + public static final class Builder extends ImmutableMap.Builder { + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableBiMap#builder}. + */ + public Builder() {} + + Builder(int size) { + super(size); + } + + /** + * Associates {@code key} with {@code value} in the built bimap. Duplicate keys or values are + * not allowed, and will cause {@link #build} to fail. + */ + @Override + public Builder put(K key, V value) { + super.put(key, value); + return this; + } + + /** + * Adds the given {@code entry} to the bimap. Duplicate keys or values are not allowed, and will + * cause {@link #build} to fail. + * + * @since 19.0 + */ + @Override + public Builder put(Entry entry) { + super.put(entry); + return this; + } + + /** + * Associates all of the given map's keys and values in the built bimap. Duplicate keys or + * values are not allowed, and will cause {@link #build} to fail. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + @Override + public Builder putAll(Map map) { + super.putAll(map); + return this; + } + + /** + * Adds all of the given entries to the built bimap. Duplicate keys or values are not allowed, + * and will cause {@link #build} to fail. + * + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + @Override + public Builder putAll(Iterable> entries) { + super.putAll(entries); + return this; + } + + /** + * Configures this {@code Builder} to order entries by value according to the specified + * comparator. + * + *

The sort order is stable, that is, if two entries have values that compare as equivalent, + * the entry that was inserted first will be first in the built map's iteration order. + * + * @throws IllegalStateException if this method was already called + * @since 19.0 + */ + @Override + public Builder orderEntriesByValue(Comparator valueComparator) { + super.orderEntriesByValue(valueComparator); + return this; + } + + @Override + Builder combine(ImmutableMap.Builder builder) { + super.combine(builder); + return this; + } + + /** + * Returns a newly-created immutable bimap. The iteration order of the returned bimap is the + * order in which entries were inserted into the builder, unless {@link #orderEntriesByValue} + * was called, in which case entries are sorted by value. + * + *

Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method + * will throw an exception if there are duplicate keys or values. The {@code build()} method + * will soon be deprecated. + * + * @throws IllegalArgumentException if duplicate keys or values were added + */ + @Override + public ImmutableBiMap build() { + return buildOrThrow(); + } + + /** + * Returns a newly-created immutable bimap, or throws an exception if any key or value was added + * more than once. The iteration order of the returned bimap is the order in which entries were + * inserted into the builder, unless {@link #orderEntriesByValue} was called, in which case + * entries are sorted by value. + * + * @throws IllegalArgumentException if duplicate keys or values were added + * @since 31.0 + */ + @Override + public ImmutableBiMap buildOrThrow() { + switch (size) { + case 0: + return of(); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entries[0]); + return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + /* + * If entries is full, or if hash flooding is detected, then this implementation may end + * up using the entries array directly and writing over the entry objects with + * non-terminal entries, but this is safe; if this Builder is used further, it will grow + * the entries array (so it can't affect the original array), and future build() calls + * will always copy any entry objects that cannot be safely reused. + */ + if (valueComparator != null) { + if (entriesUsed) { + entries = Arrays.copyOf(entries, size); + } + Arrays.sort( + (Entry[]) entries, // Entries up to size are not null + 0, + size, + Ordering.from(valueComparator).onResultOf(Maps.valueFunction())); + } + entriesUsed = true; + return RegularImmutableBiMap.fromEntryArray(size, entries); + } + } + + /** + * Throws {@link UnsupportedOperationException}. This method is inherited from {@link + * ImmutableMap.Builder}, but it does not make sense for bimaps. + * + * @throws UnsupportedOperationException always + * @deprecated This method does not make sense for bimaps and should not be called. + * @since 31.1 + */ + @Deprecated + @Override + public ImmutableBiMap buildKeepingLast() { + throw new UnsupportedOperationException("Not supported for bimaps"); + } + + @Override + @VisibleForTesting + ImmutableBiMap buildJdkBacked() { + checkState( + valueComparator == null, + "buildJdkBacked is for tests only, doesn't support orderEntriesByValue"); + switch (size) { + case 0: + return of(); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entries[0]); + return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + entriesUsed = true; + return RegularImmutableBiMap.fromEntryArray(size, entries); + } + } + } + + /** + * Returns an immutable bimap containing the same entries as {@code map}. If {@code map} somehow + * contains entries with duplicate keys (for example, if it is a {@code SortedMap} whose + * comparator is not consistent with equals), the results of this method are undefined. + * + *

The returned {@code BiMap} iterates over entries in the same order as the {@code entrySet} + * of the original map. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws IllegalArgumentException if two keys have the same value or two values have the same + * key + * @throws NullPointerException if any key or value in {@code map} is null + */ + public static ImmutableBiMap copyOf(Map map) { + if (map instanceof ImmutableBiMap) { + @SuppressWarnings("unchecked") // safe since map is not writable + ImmutableBiMap bimap = (ImmutableBiMap) map; + // TODO(lowasser): if we need to make a copy of a BiMap because the + // forward map is a view, don't make a copy of the non-view delegate map + if (!bimap.isPartialView()) { + return bimap; + } + } + return copyOf(map.entrySet()); + } + + /** + * Returns an immutable bimap containing the given entries. The returned bimap iterates over + * entries in the same order as the original iterable. + * + * @throws IllegalArgumentException if two keys have the same value or two values have the same + * key + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + public static ImmutableBiMap copyOf( + Iterable> entries) { + @SuppressWarnings("unchecked") // we'll only be using getKey and getValue, which are covariant + Entry[] entryArray = (Entry[]) Iterables.toArray(entries, EMPTY_ENTRY_ARRAY); + switch (entryArray.length) { + case 0: + return of(); + case 1: + Entry entry = entryArray[0]; + return of(entry.getKey(), entry.getValue()); + default: + /* + * The current implementation will end up using entryArray directly, though it will write + * over the (arbitrary, potentially mutable) Entry objects actually stored in entryArray. + */ + return RegularImmutableBiMap.fromEntries(entryArray); + } + } + + ImmutableBiMap() {} + + /** + * {@inheritDoc} + * + *

The inverse of an {@code ImmutableBiMap} is another {@code ImmutableBiMap}. + */ + @Override + public abstract ImmutableBiMap inverse(); + + /** + * Returns an immutable set of the values in this map, in the same order they appear in {@link + * #entrySet}. + */ + @Override + public ImmutableSet values() { + return inverse().keySet(); + } + + @Override + final ImmutableSet createValues() { + throw new AssertionError("should never be called"); + } + + /** + * Guaranteed to throw an exception and leave the bimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V forcePut(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Serialized type for all ImmutableBiMap instances. It captures the logical contents and they are + * reconstructed using public factory methods. This ensures that the implementation types remain + * as implementation details. + * + *

Since the bimap is immutable, ImmutableBiMap doesn't require special logic for keeping the + * bimap and its inverse in sync during serialization, the way AbstractBiMap does. + */ + @J2ktIncompatible // serialization + private static class SerializedForm extends ImmutableMap.SerializedForm { + SerializedForm(ImmutableBiMap bimap) { + super(bimap); + } + + @Override + Builder makeBuilder(int size) { + return new Builder<>(size); + } + + private static final long serialVersionUID = 0; + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm<>(this); + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableBiMapFauxverideShim.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableBiMapFauxverideShim.java new file mode 100644 index 0000000000000..f02080d5bb2b0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableBiMapFauxverideShim.java @@ -0,0 +1,68 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; + +/** + * "Overrides" the {@link ImmutableMap} static methods that lack {@link ImmutableBiMap} equivalents + * with deprecated, exception-throwing versions. See {@link ImmutableSortedSetFauxverideShim} for + * details. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class ImmutableBiMapFauxverideShim extends ImmutableMap { + /** + * Not supported. Use {@link ImmutableBiMap#toImmutableBiMap} instead. This method exists only to + * hide {@link ImmutableMap#toImmutableMap(Function, Function)} from consumers of {@code + * ImmutableBiMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableBiMap#toImmutableBiMap}. + */ + @Deprecated + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. This method does not make sense for {@code BiMap}. This method exists only to + * hide {@link ImmutableMap#toImmutableMap(Function, Function, BinaryOperator)} from consumers of + * {@code ImmutableBiMap}. + * + * @throws UnsupportedOperationException always + * @deprecated + */ + @Deprecated + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableClassToInstanceMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableClassToInstanceMap.java new file mode 100644 index 0000000000000..ffd6faa834f7f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableClassToInstanceMap.java @@ -0,0 +1,196 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.primitives.Primitives; + +import java.io.Serializable; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link ClassToInstanceMap} whose contents will never change, with many other important + * properties detailed at {@link ImmutableCollection}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +// TODO(b/278589132): Remove the redundant "@NonNull" on B once it's no longer required by J2KT. +public final class ImmutableClassToInstanceMap extends ForwardingMap, B> + implements ClassToInstanceMap, Serializable { + + private static final ImmutableClassToInstanceMap EMPTY = + new ImmutableClassToInstanceMap<>(ImmutableMap., Object>of()); + + /** + * Returns an empty {@code ImmutableClassToInstanceMap}. + * + *

Performance note: the instance returned is a singleton. + * + * @since 19.0 + */ + @SuppressWarnings("unchecked") + public static ImmutableClassToInstanceMap of() { + return (ImmutableClassToInstanceMap) EMPTY; + } + + /** + * Returns an {@code ImmutableClassToInstanceMap} containing a single entry. + * + * @since 19.0 + */ + public static ImmutableClassToInstanceMap of(Class type, T value) { + ImmutableMap, B> map = ImmutableMap., B>of(type, value); + return new ImmutableClassToInstanceMap<>(map); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * A builder for creating immutable class-to-instance maps. Example: + * + *

{@code
+   * static final ImmutableClassToInstanceMap HANDLERS =
+   *     new ImmutableClassToInstanceMap.Builder()
+   *         .put(FooHandler.class, new FooHandler())
+   *         .put(BarHandler.class, new SubBarHandler())
+   *         .put(Handler.class, new QuuxHandler())
+   *         .build();
+   * }
+ * + *

After invoking {@link #build()} it is still possible to add more entries and build again. + * Thus each map generated by this builder will be a superset of any map generated before it. + * + * @since 2.0 + */ + public static final class Builder { + private final ImmutableMap.Builder, B> mapBuilder = ImmutableMap.builder(); + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed, + * and will cause {@link #build} to fail. + */ + public Builder put(Class key, T value) { + mapBuilder.put(key, value); + return this; + } + + /** + * Associates all of {@code map's} keys and values in the built map. Duplicate keys are not + * allowed, and will cause {@link #build} to fail. + * + * @throws NullPointerException if any key or value in {@code map} is null + * @throws ClassCastException if any value is not an instance of the type specified by its key + */ + public Builder putAll(Map, ? extends T> map) { + for (Entry, ? extends T> entry : map.entrySet()) { + Class type = entry.getKey(); + T value = entry.getValue(); + mapBuilder.put(type, cast(type, value)); + } + return this; + } + + private static T cast(Class type, Object value) { + return Primitives.wrap(type).cast(value); + } + + /** + * Returns a new immutable class-to-instance map containing the entries provided to this + * builder. + * + * @throws IllegalArgumentException if duplicate keys were added + */ + public ImmutableClassToInstanceMap build() { + ImmutableMap, B> map = mapBuilder.buildOrThrow(); + if (map.isEmpty()) { + return of(); + } else { + return new ImmutableClassToInstanceMap<>(map); + } + } + } + + /** + * Returns an immutable map containing the same entries as {@code map}. If {@code map} somehow + * contains entries with duplicate keys (for example, if it is a {@code SortedMap} whose + * comparator is not consistent with equals), the results of this method are undefined. + * + *

Note: Despite what the method name suggests, if {@code map} is an {@code + * ImmutableClassToInstanceMap}, no copy will actually be performed. + * + * @throws NullPointerException if any key or value in {@code map} is null + * @throws ClassCastException if any value is not an instance of the type specified by its key + */ + public static ImmutableClassToInstanceMap copyOf( + Map, ? extends S> map) { + if (map instanceof ImmutableClassToInstanceMap) { + @SuppressWarnings("rawtypes") // JDT-based J2KT Java frontend does not permit the direct cast + Map rawMap = map; + @SuppressWarnings("unchecked") // covariant casts safe (unmodifiable) + ImmutableClassToInstanceMap cast = (ImmutableClassToInstanceMap) rawMap; + return cast; + } + return new Builder().putAll(map).build(); + } + + private final ImmutableMap, B> delegate; + + private ImmutableClassToInstanceMap(ImmutableMap, B> delegate) { + this.delegate = delegate; + } + + @Override + protected Map, B> delegate() { + return delegate; + } + + @Override + @SuppressWarnings("unchecked") // value could not get in if not a T + public T getInstance(Class type) { + return (T) delegate.get(checkNotNull(type)); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public T putInstance(Class type, T value) { + throw new UnsupportedOperationException(); + } + + Object readResolve() { + return isEmpty() ? of() : this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableCollection.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableCollection.java new file mode 100644 index 0000000000000..d550cedd1996b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableCollection.java @@ -0,0 +1,485 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Predicate; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Collection} whose contents will never change, and which offers a few additional + * guarantees detailed below. + * + *

Warning: avoid direct usage of {@link ImmutableCollection} as a type (just as + * with {@link Collection} itself). Prefer subtypes such as {@link ImmutableSet} or {@link + * ImmutableList}, which have well-defined {@link #equals} semantics, thus avoiding a common source + * of bugs and confusion. + * + *

About all {@code Immutable-} collections

+ * + *

The remainder of this documentation applies to every public {@code Immutable-} type in this + * package, whether it is a subtype of {@code ImmutableCollection} or not. + * + *

Guarantees

+ * + *

Each makes the following guarantees: + * + *

    + *
  • Shallow immutability. Elements can never be added, removed or replaced in this + * collection. This is a stronger guarantee than that of {@link + * Collections#unmodifiableCollection}, whose contents change whenever the wrapped collection + * is modified. + *
  • Null-hostility. This collection will never contain a null element. + *
  • Deterministic iteration. The iteration order is always well-defined, depending on + * how the collection was created. Typically this is insertion order unless an explicit + * ordering is otherwise specified (e.g. {@link ImmutableSortedSet#naturalOrder}). See the + * appropriate factory method for details. View collections such as {@link + * ImmutableMultiset#elementSet} iterate in the same order as the parent, except as noted. + *
  • Thread safety. It is safe to access this collection concurrently from multiple + * threads. + *
  • Integrity. This type cannot be subclassed outside this package (which would allow + * these guarantees to be violated). + *
+ * + *

"Interfaces", not implementations

+ * + *

These are classes instead of interfaces to prevent external subtyping, but should be thought + * of as interfaces in every important sense. Each public class such as {@link ImmutableSet} is a + * type offering meaningful behavioral guarantees. This is substantially different from the + * case of (say) {@link HashSet}, which is an implementation, with semantics that were + * largely defined by its supertype. + * + *

For field types and method return types, you should generally use the immutable type (such as + * {@link ImmutableList}) instead of the general collection interface type (such as {@link List}). + * This communicates to your callers all of the semantic guarantees listed above, which is almost + * always very useful information. + * + *

On the other hand, a parameter type of {@link ImmutableList} is generally a nuisance to + * callers. Instead, accept {@link Iterable} and have your method or constructor body pass it to the + * appropriate {@code copyOf} method itself. + * + *

Expressing the immutability guarantee directly in the type that user code references is a + * powerful advantage. Although Java offers certain immutable collection factory methods, such as + * {@link Collections#singleton(Object)} and {@code Set.of}, + * we recommend using these classes instead for this reason (as well as for consistency). + * + *

Creation

+ * + *

Except for logically "abstract" types like {@code ImmutableCollection} itself, each {@code + * Immutable} type provides the static operations you need to obtain instances of that type. These + * usually include: + * + *

    + *
  • Static methods named {@code of}, accepting an explicit list of elements or entries. + *
  • Static methods named {@code copyOf} (or {@code copyOfSorted}), accepting an existing + * collection whose contents should be copied. + *
  • A static nested {@code Builder} class which can be used to populate a new immutable + * instance. + *
+ * + *

Warnings

+ * + *
    + *
  • Warning: as with any collection, it is almost always a bad idea to modify an element + * (in a way that affects its {@link Object#equals} behavior) while it is contained in a + * collection. Undefined behavior and bugs will result. It's generally best to avoid using + * mutable objects as elements at all, as many users may expect your "immutable" object to be + * deeply immutable. + *
+ * + *

Performance notes

+ * + *
    + *
  • Implementations can be generally assumed to prioritize memory efficiency, then speed of + * access, and lastly speed of creation. + *
  • The {@code copyOf} methods will sometimes recognize that the actual copy operation is + * unnecessary; for example, {@code copyOf(copyOf(anArrayList))} should copy the data only + * once. This reduces the expense of habitually making defensive copies at API boundaries. + * However, the precise conditions for skipping the copy operation are undefined. + *
  • Warning: a view collection such as {@link ImmutableMap#keySet} or {@link + * ImmutableList#subList} may retain a reference to the entire data set, preventing it from + * being garbage collected. If some of the data is no longer reachable through other means, + * this constitutes a memory leak. Pass the view collection to the appropriate {@code copyOf} + * method to obtain a correctly-sized copy. + *
  • The performance of using the associated {@code Builder} class can be assumed to be no + * worse, and possibly better, than creating a mutable collection and copying it. + *
  • Implementations generally do not cache hash codes. If your element or key type has a slow + * {@code hashCode} implementation, it should cache it itself. + *
+ * + *

Example usage

+ * + *
{@code
+ * class Foo {
+ *   private static final ImmutableSet RESERVED_CODES =
+ *       ImmutableSet.of("AZ", "CQ", "ZX");
+ *
+ *   private final ImmutableSet codes;
+ *
+ *   public Foo(Iterable codes) {
+ *     this.codes = ImmutableSet.copyOf(codes);
+ *     checkArgument(Collections.disjoint(this.codes, RESERVED_CODES));
+ *   }
+ * }
+ * }
+ * + *

See also

+ * + *

See the Guava User Guide article on immutable collections. + * + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +// TODO(kevinb): I think we should push everything down to "BaseImmutableCollection" or something, +// just to do everything we can to emphasize the "practically an interface" nature of this class. +public abstract class ImmutableCollection extends AbstractCollection implements Serializable { + /* + * We expect SIZED (and SUBSIZED, if applicable) to be added by the spliterator factory methods. + * These are properties of the collection as a whole; SIZED and SUBSIZED are more properties of + * the spliterator implementation. + */ + static final int SPLITERATOR_CHARACTERISTICS = + Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED; + + ImmutableCollection() {} + + /** Returns an unmodifiable iterator across the elements in this collection. */ + @Override + public abstract UnmodifiableIterator iterator(); + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, SPLITERATOR_CHARACTERISTICS); + } + + private static final Object[] EMPTY_ARRAY = {}; + + @Override + @J2ktIncompatible // Incompatible return type change. Use inherited (unoptimized) implementation + public final Object[] toArray() { + return toArray(EMPTY_ARRAY); + } + + @Override + /* + * This suppression is here for two reasons: + * + * 1. b/192354773 in our checker affects toArray declarations. + * + * 2. `other[size] = null` is unsound. We could "fix" this by requiring callers to pass in an + * array with a nullable element type. But probably they usually want an array with a non-nullable + * type. That said, we could *accept* a `@Nullable T[]` (which, given that we treat arrays as + * covariant, would still permit a plain `T[]`) and return a plain `T[]`. But of course that would + * require its own suppression, since it is also unsound. toArray(T[]) is just a mess from a + * nullness perspective. The signature below at least has the virtue of being relatively simple. + */ + @SuppressWarnings("nullness") + public final T[] toArray(T[] other) { + checkNotNull(other); + int size = size(); + + if (other.length < size) { + Object[] internal = internalArray(); + if (internal != null) { + return Platform.copy(internal, internalArrayStart(), internalArrayEnd(), other); + } + other = ObjectArrays.newArray(other, size); + } else if (other.length > size) { + other[size] = null; + } + copyIntoArray(other, 0); + return other; + } + + /** If this collection is backed by an array of its elements in insertion order, returns it. */ + Object[] internalArray() { + return null; + } + + /** + * If this collection is backed by an array of its elements in insertion order, returns the offset + * where this collection's elements start. + */ + int internalArrayStart() { + throw new UnsupportedOperationException(); + } + + /** + * If this collection is backed by an array of its elements in insertion order, returns the offset + * where this collection's elements end. + */ + int internalArrayEnd() { + throw new UnsupportedOperationException(); + } + + @Override + public abstract boolean contains(Object object); + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean add(E e) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean remove(Object object) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean addAll(Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean removeAll(Collection oldElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean retainAll(Collection elementsToKeep) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an {@code ImmutableList} containing the same elements, in the same order, as this + * collection. + * + *

Performance note: in most cases this method can return quickly without actually + * copying anything. The exact circumstances under which the copy is performed are undefined and + * subject to change. + * + * @since 2.0 + */ + public ImmutableList asList() { + switch (size()) { + case 0: + return ImmutableList.of(); + case 1: + return ImmutableList.of(iterator().next()); + default: + return new RegularImmutableAsList(this, toArray()); + } + } + + /** + * Returns {@code true} if this immutable collection's implementation contains references to + * user-created objects that aren't accessible via this collection's methods. This is generally + * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid + * memory leaks. + */ + abstract boolean isPartialView(); + + /** + * Copies the contents of this immutable collection into the specified array at the specified + * offset. Returns {@code offset + size()}. + */ + int copyIntoArray(Object[] dst, int offset) { + for (E e : this) { + dst[offset++] = e; + } + return offset; + } + + @J2ktIncompatible // serialization + Object writeReplace() { + // We serialize by default to ImmutableList, the simplest thing that works. + return new ImmutableList.SerializedForm(toArray()); + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** + * Abstract base class for builders of {@link ImmutableCollection} types. + * + * @since 10.0 + */ + public abstract static class Builder { + static final int DEFAULT_INITIAL_CAPACITY = 4; + + static int expandedCapacity(int oldCapacity, int minCapacity) { + if (minCapacity < 0) { + throw new AssertionError("cannot store more than MAX_VALUE elements"); + } + // careful of overflow! + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; + // guaranteed to be >= newCapacity + } + return newCapacity; + } + + Builder() {} + + /** + * Adds {@code element} to the {@code ImmutableCollection} being built. + * + *

Note that each builder class covariantly returns its own type from this method. + * + * @param element the element to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code element} is null + */ + public abstract Builder add(E element); + + /** + * Adds each element of {@code elements} to the {@code ImmutableCollection} being built. + * + *

Note that each builder class overrides this method in order to covariantly return its own + * type. + * + * @param elements the elements to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + public Builder add(E... elements) { + for (E element : elements) { + add(element); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableCollection} being built. + * + *

Note that each builder class overrides this method in order to covariantly return its own + * type. + * + * @param elements the elements to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + public Builder addAll(Iterable elements) { + for (E element : elements) { + add(element); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableCollection} being built. + * + *

Note that each builder class overrides this method in order to covariantly return its own + * type. + * + * @param elements the elements to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + public Builder addAll(Iterator elements) { + while (elements.hasNext()) { + add(elements.next()); + } + return this; + } + + /** + * Returns a newly-created {@code ImmutableCollection} of the appropriate type, containing the + * elements provided to this builder. + * + *

Note that each builder class covariantly returns the appropriate type of {@code + * ImmutableCollection} from this method. + */ + public abstract ImmutableCollection build(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEntry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEntry.java new file mode 100644 index 0000000000000..46dec521e9b08 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEntry.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** + * An immutable {@code Map.Entry}, used both by {@link + * org.apache.iotdb.google.common.collect.Maps#immutableEntry(Object, Object)} and by other parts of + * {@code common.collect} as a superclass. + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +class ImmutableEntry extends AbstractMapEntry + implements Serializable { + @ParametricNullness final K key; + @ParametricNullness final V value; + + ImmutableEntry(@ParametricNullness K key, @ParametricNullness V value) { + this.key = key; + this.value = value; + } + + @Override + @ParametricNullness + public final K getKey() { + return key; + } + + @Override + @ParametricNullness + public final V getValue() { + return value; + } + + @Override + @ParametricNullness + public final V setValue(@ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEnumMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEnumMap.java new file mode 100644 index 0000000000000..ee819b0328224 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEnumMap.java @@ -0,0 +1,148 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ImmutableMap.IteratorBasedImmutableMap; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.EnumMap; +import java.util.Spliterator; +import java.util.function.BiConsumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * Implementation of {@link ImmutableMap} backed by a non-empty {@link java.util.EnumMap}. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +final class ImmutableEnumMap, V> extends IteratorBasedImmutableMap { + static , V> ImmutableMap asImmutable(EnumMap map) { + switch (map.size()) { + case 0: + return ImmutableMap.of(); + case 1: + Entry entry = Iterables.getOnlyElement(map.entrySet()); + return ImmutableMap.of(entry.getKey(), entry.getValue()); + default: + return new ImmutableEnumMap<>(map); + } + } + + private final transient EnumMap delegate; + + private ImmutableEnumMap(EnumMap delegate) { + this.delegate = delegate; + checkArgument(!delegate.isEmpty()); + } + + @Override + UnmodifiableIterator keyIterator() { + return Iterators.unmodifiableIterator(delegate.keySet().iterator()); + } + + @Override + Spliterator keySpliterator() { + return delegate.keySet().spliterator(); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ImmutableEnumMap) { + object = ((ImmutableEnumMap) object).delegate; + } + return delegate.equals(object); + } + + @Override + UnmodifiableIterator> entryIterator() { + return Maps.unmodifiableEntryIterator(delegate.entrySet().iterator()); + } + + @Override + Spliterator> entrySpliterator() { + return CollectSpliterators.map(delegate.entrySet().spliterator(), Maps::unmodifiableEntry); + } + + @Override + public void forEach(BiConsumer action) { + delegate.forEach(action); + } + + @Override + boolean isPartialView() { + return false; + } + + // All callers of the constructor are restricted to >. + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new EnumSerializedForm<>(delegate); + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use EnumSerializedForm"); + } + + /* + * This class is used to serialize ImmutableEnumMap instances. + */ + @J2ktIncompatible // serialization + private static class EnumSerializedForm, V> implements Serializable { + final EnumMap delegate; + + EnumSerializedForm(EnumMap delegate) { + this.delegate = delegate; + } + + Object readResolve() { + return new ImmutableEnumMap<>(delegate); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEnumSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEnumSet.java new file mode 100644 index 0000000000000..31678c31bc60f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableEnumSet.java @@ -0,0 +1,169 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Spliterator; +import java.util.function.Consumer; + +/** + * Implementation of {@link ImmutableSet} backed by a non-empty {@link java.util.EnumSet}. + * + * @author Jared Levy + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +final class ImmutableEnumSet> extends ImmutableSet { + static > ImmutableSet asImmutable(EnumSet set) { + switch (set.size()) { + case 0: + return ImmutableSet.of(); + case 1: + return ImmutableSet.of(Iterables.getOnlyElement(set)); + default: + return new ImmutableEnumSet(set); + } + } + + /* + * Notes on EnumSet and >: + * + * This class isn't an arbitrary ForwardingImmutableSet because we need to + * know that calling {@code clone()} during deserialization will return an + * object that no one else has a reference to, allowing us to guarantee + * immutability. Hence, we support only {@link EnumSet}. + */ + private final transient EnumSet delegate; + + private ImmutableEnumSet(EnumSet delegate) { + this.delegate = delegate; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.unmodifiableIterator(delegate.iterator()); + } + + @Override + public Spliterator spliterator() { + return delegate.spliterator(); + } + + @Override + public void forEach(Consumer action) { + delegate.forEach(action); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean contains(Object object) { + return delegate.contains(object); + } + + @Override + public boolean containsAll(Collection collection) { + if (collection instanceof ImmutableEnumSet) { + collection = ((ImmutableEnumSet) collection).delegate; + } + return delegate.containsAll(collection); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ImmutableEnumSet) { + object = ((ImmutableEnumSet) object).delegate; + } + return delegate.equals(object); + } + + @Override + boolean isHashCodeFast() { + return true; + } + + private transient int hashCode; + + @Override + public int hashCode() { + int result = hashCode; + return (result == 0) ? hashCode = delegate.hashCode() : result; + } + + @Override + public String toString() { + return delegate.toString(); + } + + // All callers of the constructor are restricted to >. + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new EnumSerializedForm(delegate); + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /* + * This class is used to serialize ImmutableEnumSet instances. + */ + @J2ktIncompatible // serialization + private static class EnumSerializedForm> implements Serializable { + final EnumSet delegate; + + EnumSerializedForm(EnumSet delegate) { + this.delegate = delegate; + } + + Object readResolve() { + // EJ2 #76: Write readObject() methods defensively. + return new ImmutableEnumSet(delegate.clone()); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableList.java new file mode 100644 index 0000000000000..cd1df74dc2a51 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableList.java @@ -0,0 +1,888 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.stream.Collector; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.ObjectArrays.checkElementsNotNull; +import static org.apache.iotdb.google.common.collect.RegularImmutableList.EMPTY; + +/** + * A {@link List} whose contents will never change, with many other important properties detailed at + * {@link ImmutableCollection}. + * + *

See the Guava User Guide article on immutable collections. + * + * @see ImmutableMap + * @see ImmutableSet + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +public abstract class ImmutableList extends ImmutableCollection + implements List, RandomAccess { + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableList}, in encounter order. + * + * @since 21.0 + */ + public static Collector> toImmutableList() { + return CollectCollectors.toImmutableList(); + } + + /** + * Returns the empty immutable list. This list behaves and performs comparably to {@link + * Collections#emptyList}, and is preferable mainly for consistency and maintainability of your + * code. + * + *

Performance note: the instance returned is a singleton. + */ + // Casting to any type is safe because the list will never hold any elements. + @SuppressWarnings("unchecked") + public static ImmutableList of() { + return (ImmutableList) EMPTY; + } + + /** + * Returns an immutable list containing a single element. This list behaves and performs + * comparably to {@link Collections#singletonList}, but will not accept a null element. It is + * preferable mainly for consistency and maintainability of your code. + * + * @throws NullPointerException if {@code element} is null + */ + public static ImmutableList of(E element) { + return new SingletonImmutableList(element); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2) { + return construct(e1, e2); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3) { + return construct(e1, e2, e3); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4) { + return construct(e1, e2, e3, e4); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5) { + return construct(e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6) { + return construct(e1, e2, e3, e4, e5, e6); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + return construct(e1, e2, e3, e4, e5, e6, e7); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of( + E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of( + E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11); + } + + // These go up to eleven. After that, you just get the varargs form, and + // whatever warnings might come along with it. :( + + /** + * Returns an immutable list containing the given elements, in order. + * + *

The array {@code others} must not be longer than {@code Integer.MAX_VALUE - 12}. + * + * @throws NullPointerException if any element is null + * @since 3.0 (source-compatible since 2.0) + */ + @SafeVarargs // For Eclipse. For internal javac we have disabled this pointless type of warning. + public static ImmutableList of( + E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11, E e12, E... others) { + checkArgument( + others.length <= Integer.MAX_VALUE - 12, "the total number of elements must fit in an int"); + Object[] array = new Object[12 + others.length]; + array[0] = e1; + array[1] = e2; + array[2] = e3; + array[3] = e4; + array[4] = e5; + array[5] = e6; + array[6] = e7; + array[7] = e8; + array[8] = e9; + array[9] = e10; + array[10] = e11; + array[11] = e12; + System.arraycopy(others, 0, array, 12, others.length); + return construct(array); + } + + /** + * Returns an immutable list containing the given elements, in order. If {@code elements} is a + * {@link Collection}, this method behaves exactly as {@link #copyOf(Collection)}; otherwise, it + * behaves exactly as {@code copyOf(elements.iterator()}. + * + * @throws NullPointerException if {@code elements} contains a null element + */ + public static ImmutableList copyOf(Iterable elements) { + checkNotNull(elements); // TODO(kevinb): is this here only for GWT? + return (elements instanceof Collection) + ? copyOf((Collection) elements) + : copyOf(elements.iterator()); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

Note that if {@code list} is a {@code List}, then {@code ImmutableList.copyOf(list)} + * returns an {@code ImmutableList} containing each of the strings in {@code list}, while + * {@code ImmutableList.of(list)} returns an {@code ImmutableList>} containing one + * element (the given list itself). + * + *

This method is safe to use even when {@code elements} is a synchronized or concurrent + * collection that is currently being modified by another thread. + * + * @throws NullPointerException if {@code elements} contains a null element + */ + public static ImmutableList copyOf(Collection elements) { + if (elements instanceof ImmutableCollection) { + @SuppressWarnings("unchecked") // all supported methods are covariant + ImmutableList list = ((ImmutableCollection) elements).asList(); + return list.isPartialView() ? ImmutableList.asImmutableList(list.toArray()) : list; + } + return construct(elements.toArray()); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if {@code elements} contains a null element + */ + public static ImmutableList copyOf(Iterator elements) { + // We special-case for 0 or 1 elements, but going further is madness. + if (!elements.hasNext()) { + return of(); + } + E first = elements.next(); + if (!elements.hasNext()) { + return of(first); + } else { + return new ImmutableList.Builder().add(first).addAll(elements).build(); + } + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if {@code elements} contains a null element + * @since 3.0 + */ + public static ImmutableList copyOf(E[] elements) { + switch (elements.length) { + case 0: + return of(); + case 1: + return of(elements[0]); + default: + return construct(elements.clone()); + } + } + + /** + * Returns an immutable list containing the given elements, sorted according to their natural + * order. The sorting algorithm used is stable, so elements that compare as equal will stay in the + * order in which they appear in the input. + * + *

If your data has no duplicates, or you wish to deduplicate elements, use {@code + * ImmutableSortedSet.copyOf(elements)}; if you want a {@code List} you can use its {@code + * asList()} view. + * + *

Java 8 users: If you want to convert a {@link java.util.stream.Stream} to a sorted + * {@code ImmutableList}, use {@code stream.sorted().collect(toImmutableList())}. + * + * @throws NullPointerException if any element in the input is null + * @since 21.0 + */ + public static > ImmutableList sortedCopyOf( + Iterable elements) { + Comparable[] array = Iterables.toArray(elements, new Comparable[0]); + checkElementsNotNull((Object[]) array); + Arrays.sort(array); + return asImmutableList(array); + } + + /** + * Returns an immutable list containing the given elements, in sorted order relative to the + * specified comparator. The sorting algorithm used is stable, so elements that compare as equal + * will stay in the order in which they appear in the input. + * + *

If your data has no duplicates, or you wish to deduplicate elements, use {@code + * ImmutableSortedSet.copyOf(comparator, elements)}; if you want a {@code List} you can use its + * {@code asList()} view. + * + *

Java 8 users: If you want to convert a {@link java.util.stream.Stream} to a sorted + * {@code ImmutableList}, use {@code stream.sorted(comparator).collect(toImmutableList())}. + * + * @throws NullPointerException if any element in the input is null + * @since 21.0 + */ + public static ImmutableList sortedCopyOf( + Comparator comparator, Iterable elements) { + checkNotNull(comparator); + @SuppressWarnings("unchecked") // all supported methods are covariant + E[] array = (E[]) Iterables.toArray(elements); + checkElementsNotNull(array); + Arrays.sort(array, comparator); + return asImmutableList(array); + } + + /** Views the array as an immutable list. Checks for nulls; does not copy. */ + private static ImmutableList construct(Object... elements) { + return asImmutableList(checkElementsNotNull(elements)); + } + + /** + * Views the array as an immutable list. Does not check for nulls; does not copy. + * + *

The array must be internally created. + */ + static ImmutableList asImmutableList(Object[] elements) { + return asImmutableList(elements, elements.length); + } + + /** + * Views the array as an immutable list. Copies if the specified range does not cover the complete + * array. Does not check for nulls. + */ + static ImmutableList asImmutableList(Object[] elements, int length) { + switch (length) { + case 0: + return of(); + case 1: + /* + * requireNonNull is safe because the callers promise to put non-null objects in the first + * `length` array elements. + */ + @SuppressWarnings("unchecked") // our callers put only E instances into the array + E onlyElement = (E) requireNonNull(elements[0]); + return of(onlyElement); + default: + /* + * The suppression is safe because the callers promise to put non-null objects in the first + * `length` array elements. + */ + @SuppressWarnings("nullness") + Object[] elementsWithoutTrailingNulls = + length < elements.length ? Arrays.copyOf(elements, length) : elements; + return new RegularImmutableList(elementsWithoutTrailingNulls); + } + } + + ImmutableList() {} + + // This declaration is needed to make List.iterator() and + // ImmutableCollection.iterator() consistent. + @Override + public UnmodifiableIterator iterator() { + return listIterator(); + } + + @Override + public UnmodifiableListIterator listIterator() { + return listIterator(0); + } + + @Override + public UnmodifiableListIterator listIterator(int index) { + return new AbstractIndexedListIterator(size(), index) { + @Override + protected E get(int index) { + return ImmutableList.this.get(index); + } + }; + } + + @Override + public void forEach(Consumer consumer) { + checkNotNull(consumer); + int n = size(); + for (int i = 0; i < n; i++) { + consumer.accept(get(i)); + } + } + + @Override + public int indexOf(Object object) { + return (object == null) ? -1 : Lists.indexOfImpl(this, object); + } + + @Override + public int lastIndexOf(Object object) { + return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object); + } + + @Override + public boolean contains(Object object) { + return indexOf(object) >= 0; + } + + // constrain the return type to ImmutableList + + /** + * Returns an immutable list of the elements between the specified {@code fromIndex}, inclusive, + * and {@code toIndex}, exclusive. (If {@code fromIndex} and {@code toIndex} are equal, the empty + * immutable list is returned.) + */ + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + int length = toIndex - fromIndex; + if (length == size()) { + return this; + } else if (length == 0) { + return of(); + } else if (length == 1) { + return of(get(fromIndex)); + } else { + return subListUnchecked(fromIndex, toIndex); + } + } + + /** + * Called by the default implementation of {@link #subList} when {@code toIndex - fromIndex > 1}, + * after index validation has already been performed. + */ + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return new SubList(fromIndex, toIndex - fromIndex); + } + + class SubList extends ImmutableList { + final transient int offset; + final transient int length; + + SubList(int offset, int length) { + this.offset = offset; + this.length = length; + } + + @Override + public int size() { + return length; + } + + @Override + public E get(int index) { + checkElementIndex(index, length); + return ImmutableList.this.get(index + offset); + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, length); + return ImmutableList.this.subList(fromIndex + offset, toIndex + offset); + } + + @Override + boolean isPartialView() { + return true; + } + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean addAll(int index, Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final E set(int index, E element) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void add(int index, E element) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final E remove(int index) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void replaceAll(UnaryOperator operator) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void sort(Comparator c) { + throw new UnsupportedOperationException(); + } + + /** + * Returns this list instance. + * + * @since 2.0 + * @deprecated There is no reason to use this; it always returns {@code this}. + */ + @Deprecated + @Override + public final ImmutableList asList() { + return this; + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.indexed(size(), SPLITERATOR_CHARACTERISTICS, this::get); + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + // this loop is faster for RandomAccess instances, which ImmutableLists are + int size = size(); + for (int i = 0; i < size; i++) { + dst[offset + i] = get(i); + } + return offset + size; + } + + /** + * Returns a view of this immutable list in reverse order. For example, {@code ImmutableList.of(1, + * 2, 3).reverse()} is equivalent to {@code ImmutableList.of(3, 2, 1)}. + * + * @return a view of this immutable list in reverse order + * @since 7.0 + */ + public ImmutableList reverse() { + return (size() <= 1) ? this : new ReverseImmutableList(this); + } + + private static class ReverseImmutableList extends ImmutableList { + private final transient ImmutableList forwardList; + + ReverseImmutableList(ImmutableList backingList) { + this.forwardList = backingList; + } + + private int reverseIndex(int index) { + return (size() - 1) - index; + } + + private int reversePosition(int index) { + return size() - index; + } + + @Override + public ImmutableList reverse() { + return forwardList; + } + + @Override + public boolean contains(Object object) { + return forwardList.contains(object); + } + + @Override + public int indexOf(Object object) { + int index = forwardList.lastIndexOf(object); + return (index >= 0) ? reverseIndex(index) : -1; + } + + @Override + public int lastIndexOf(Object object) { + int index = forwardList.indexOf(object); + return (index >= 0) ? reverseIndex(index) : -1; + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + return forwardList.subList(reversePosition(toIndex), reversePosition(fromIndex)).reverse(); + } + + @Override + public E get(int index) { + checkElementIndex(index, size()); + return forwardList.get(reverseIndex(index)); + } + + @Override + public int size() { + return forwardList.size(); + } + + @Override + boolean isPartialView() { + return forwardList.isPartialView(); + } + } + + @Override + public boolean equals(Object obj) { + return Lists.equalsImpl(this, obj); + } + + @Override + public int hashCode() { + int hashCode = 1; + int n = size(); + for (int i = 0; i < n; i++) { + hashCode = 31 * hashCode + get(i).hashCode(); + + hashCode = ~~hashCode; + // needed to deal with GWT integer overflow + } + return hashCode; + } + + /* + * Serializes ImmutableLists as their logical contents. This ensures that + * implementation types do not leak into the serialized representation. + */ + @J2ktIncompatible // serialization + static class SerializedForm implements Serializable { + final Object[] elements; + + SerializedForm(Object[] elements) { + this.elements = elements; + } + + Object readResolve() { + return copyOf(elements); + } + + private static final long serialVersionUID = 0; + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm(toArray()); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a new builder, expecting the specified number of elements to be added. + * + *

If {@code expectedSize} is exactly the number of elements added to the builder before {@link + * Builder#build} is called, the builder is likely to perform better than an unsized {@link + * #builder()} would have. + * + *

It is not specified if any performance benefits apply if {@code expectedSize} is close to, + * but not exactly, the number of elements added to the builder. + * + * @since 23.1 + */ + public static Builder builderWithExpectedSize(int expectedSize) { + checkNonnegative(expectedSize, "expectedSize"); + return new ImmutableList.Builder(expectedSize); + } + + /** + * A builder for creating immutable list instances, especially {@code public static final} lists + * ("constant lists"). Example: + * + *

{@code
+   * public static final ImmutableList GOOGLE_COLORS
+   *     = new ImmutableList.Builder()
+   *         .addAll(WEBSAFE_COLORS)
+   *         .add(new Color(0, 191, 255))
+   *         .build();
+   * }
+ * + *

Elements appear in the resulting list in the same order they were added to the builder. + * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple lists in series. Each new list contains all the elements of the ones created before + * it. + * + * @since 2.0 + */ + public static final class Builder extends ImmutableCollection.Builder { + // The first `size` elements are non-null. + @VisibleForTesting Object[] contents; + private int size; + private boolean forceCopy; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableList#builder}. + */ + public Builder() { + this(DEFAULT_INITIAL_CAPACITY); + } + + Builder(int capacity) { + this.contents = new Object[capacity]; + this.size = 0; + } + + private void getReadyToExpandTo(int minCapacity) { + if (contents.length < minCapacity) { + this.contents = Arrays.copyOf(contents, expandedCapacity(contents.length, minCapacity)); + forceCopy = false; + } else if (forceCopy) { + contents = Arrays.copyOf(contents, contents.length); + forceCopy = false; + } + } + + /** + * Adds {@code element} to the {@code ImmutableList}. + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override + public Builder add(E element) { + checkNotNull(element); + getReadyToExpandTo(size + 1); + contents[size++] = element; + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableList}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableList} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder add(E... elements) { + checkElementsNotNull(elements); + add(elements, elements.length); + return this; + } + + private void add(Object[] elements, int n) { + getReadyToExpandTo(size + n); + /* + * The following call is not statically checked, since arraycopy accepts plain Object for its + * parameters. If it were statically checked, the checker would still be OK with it, since + * we're copying into a `contents` array whose type allows it to contain nulls. Still, it's + * worth noting that we promise not to put nulls into the array in the first `size` elements. + * We uphold that promise here because our callers promise that `elements` will not contain + * nulls in its first `n` elements. + */ + System.arraycopy(elements, 0, contents, size, n); + size += n; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableList}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableList} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterable elements) { + checkNotNull(elements); + if (elements instanceof Collection) { + Collection collection = (Collection) elements; + getReadyToExpandTo(size + collection.size()); + if (collection instanceof ImmutableCollection) { + ImmutableCollection immutableCollection = (ImmutableCollection) collection; + size = immutableCollection.copyIntoArray(contents, size); + return this; + } + } + super.addAll(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableList}. + * + * @param elements the {@code Iterator} to add to the {@code ImmutableList} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + Builder combine(Builder builder) { + checkNotNull(builder); + add(builder.contents, builder.size); + return this; + } + + /** + * Returns a newly-created {@code ImmutableList} based on the contents of the {@code Builder}. + */ + @Override + public ImmutableList build() { + forceCopy = true; + return asImmutableList(contents, size); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableListMultimap.java new file mode 100644 index 0000000000000..eb4179dccdbb1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableListMultimap.java @@ -0,0 +1,489 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; + +/** + * A {@link ListMultimap} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public class ImmutableListMultimap extends ImmutableMultimap + implements ListMultimap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableListMultimap} + * whose keys and values are the result of applying the provided mapping functions to the input + * elements. + * + *

For streams with defined encounter order (as defined in the Ordering section of the {@link + * java.util.stream} Javadoc), that order is preserved, but entries are grouped by key. + * + *

Example: + * + *

{@code
+   * static final Multimap FIRST_LETTER_MULTIMAP =
+   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
+   *         .collect(toImmutableListMultimap(str -> str.charAt(0), str -> str.substring(1)));
+   *
+   * // is equivalent to
+   *
+   * static final Multimap FIRST_LETTER_MULTIMAP =
+   *     new ImmutableListMultimap.Builder()
+   *         .put('b', "anana")
+   *         .putAll('a', "pple", "sparagus")
+   *         .putAll('c', "arrot", "herry")
+   *         .build();
+   * }
+ * + * @since 21.0 + */ + public static + Collector> toImmutableListMultimap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableListMultimap(keyFunction, valueFunction); + } + + /** + * Returns a {@code Collector} accumulating entries into an {@code ImmutableListMultimap}. Each + * input element is mapped to a key and a stream of values, each of which are put into the + * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the + * streams of values. + * + *

Example: + * + *

{@code
+   * static final ImmutableListMultimap FIRST_LETTER_MULTIMAP =
+   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
+   *         .collect(
+   *             flatteningToImmutableListMultimap(
+   *                  str -> str.charAt(0),
+   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
+   *
+   * // is equivalent to
+   *
+   * static final ImmutableListMultimap FIRST_LETTER_MULTIMAP =
+   *     ImmutableListMultimap.builder()
+   *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
+   *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
+   *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
+   *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
+   *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
+   *         .build();
+   * }
+   * }
+ * + * @since 21.0 + */ + public static + Collector> flatteningToImmutableListMultimap( + Function keyFunction, + Function> valuesFunction) { + return CollectCollectors.flatteningToImmutableListMultimap(keyFunction, valuesFunction); + } + + /** + * Returns the empty multimap. + * + *

Performance note: the instance returned is a singleton. + */ + // Casting is safe because the multimap will never hold any elements. + @SuppressWarnings("unchecked") + public static ImmutableListMultimap of() { + return (ImmutableListMultimap) EmptyImmutableListMultimap.INSTANCE; + } + + /** Returns an immutable multimap containing a single entry. */ + public static ImmutableListMultimap of(K k1, V v1) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + return builder.build(); + } + + /** Returns an immutable multimap containing the given entries, in order. */ + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + return builder.build(); + } + + /** Returns an immutable multimap containing the given entries, in order. */ + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + return builder.build(); + } + + /** Returns an immutable multimap containing the given entries, in order. */ + public static ImmutableListMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + return builder.build(); + } + + /** Returns an immutable multimap containing the given entries, in order. */ + public static ImmutableListMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + builder.put(k5, v5); + return builder.build(); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * A builder for creating immutable {@code ListMultimap} instances, especially {@code public + * static final} multimaps ("constant multimaps"). Example: + * + *

{@code
+   * static final Multimap STRING_TO_INTEGER_MULTIMAP =
+   *     new ImmutableListMultimap.Builder()
+   *         .put("one", 1)
+   *         .putAll("several", 1, 2, 3)
+   *         .putAll("many", 1, 2, 3, 4, 5)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple multimaps in series. Each multimap contains the key-value mappings in the previously + * created multimaps. + * + * @since 2.0 + */ + public static final class Builder extends ImmutableMultimap.Builder { + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableListMultimap#builder}. + */ + public Builder() {} + + @Override + public Builder put(K key, V value) { + super.put(key, value); + return this; + } + + /** + * {@inheritDoc} + * + * @since 11.0 + */ + @Override + public Builder put(Entry entry) { + super.put(entry); + return this; + } + + /** + * {@inheritDoc} + * + * @since 19.0 + */ + @Override + public Builder putAll(Iterable> entries) { + super.putAll(entries); + return this; + } + + @Override + public Builder putAll(K key, Iterable values) { + super.putAll(key, values); + return this; + } + + @Override + public Builder putAll(K key, V... values) { + super.putAll(key, values); + return this; + } + + @Override + public Builder putAll(Multimap multimap) { + super.putAll(multimap); + return this; + } + + @Override + Builder combine(ImmutableMultimap.Builder other) { + super.combine(other); + return this; + } + + /** + * {@inheritDoc} + * + * @since 8.0 + */ + @Override + public Builder orderKeysBy(Comparator keyComparator) { + super.orderKeysBy(keyComparator); + return this; + } + + /** + * {@inheritDoc} + * + * @since 8.0 + */ + @Override + public Builder orderValuesBy(Comparator valueComparator) { + super.orderValuesBy(valueComparator); + return this; + } + + /** Returns a newly-created immutable list multimap. */ + @Override + public ImmutableListMultimap build() { + return (ImmutableListMultimap) super.build(); + } + } + + /** + * Returns an immutable multimap containing the same mappings as {@code multimap}. The generated + * multimap's key and value orderings correspond to the iteration ordering of the {@code + * multimap.asMap()} view. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code multimap} is null + */ + public static ImmutableListMultimap copyOf( + Multimap multimap) { + if (multimap.isEmpty()) { + return of(); + } + + // TODO(lowasser): copy ImmutableSetMultimap by using asList() on the sets + if (multimap instanceof ImmutableListMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableListMultimap kvMultimap = (ImmutableListMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + return fromMapEntries(multimap.asMap().entrySet(), null); + } + + /** + * Returns an immutable multimap containing the specified entries. The returned multimap iterates + * over keys in the order they were first encountered in the input, and the values for each key + * are iterated in the order they were encountered. + * + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + public static ImmutableListMultimap copyOf( + Iterable> entries) { + return new Builder().putAll(entries).build(); + } + + /** Creates an ImmutableListMultimap from an asMap.entrySet. */ + static ImmutableListMultimap fromMapEntries( + Collection>> mapEntries, + Comparator valueComparator) { + if (mapEntries.isEmpty()) { + return of(); + } + ImmutableMap.Builder> builder = + new ImmutableMap.Builder<>(mapEntries.size()); + int size = 0; + + for (Entry> entry : mapEntries) { + K key = entry.getKey(); + Collection values = entry.getValue(); + ImmutableList list = + (valueComparator == null) + ? ImmutableList.copyOf(values) + : ImmutableList.sortedCopyOf(valueComparator, values); + if (!list.isEmpty()) { + builder.put(key, list); + size += list.size(); + } + } + + return new ImmutableListMultimap<>(builder.buildOrThrow(), size); + } + + ImmutableListMultimap(ImmutableMap> map, int size) { + super(map, size); + } + + // views + + /** + * Returns an immutable list of the values for the given key. If no mappings in the multimap have + * the provided key, an empty immutable list is returned. The values are in the same order as the + * parameters used to build this multimap. + */ + @Override + public ImmutableList get(K key) { + // This cast is safe as its type is known in constructor. + ImmutableList list = (ImmutableList) map.get(key); + return (list == null) ? ImmutableList.of() : list; + } + + private transient ImmutableListMultimap inverse; + + /** + * {@inheritDoc} + * + *

Because an inverse of a list multimap can contain multiple pairs with the same key and + * value, this method returns an {@code ImmutableListMultimap} rather than the {@code + * ImmutableMultimap} specified in the {@code ImmutableMultimap} class. + * + * @since 11.0 + */ + @Override + public ImmutableListMultimap inverse() { + ImmutableListMultimap result = inverse; + return (result == null) ? (inverse = invert()) : result; + } + + private ImmutableListMultimap invert() { + Builder builder = builder(); + for (Entry entry : entries()) { + builder.put(entry.getValue(), entry.getKey()); + } + ImmutableListMultimap invertedMultimap = builder.build(); + invertedMultimap.inverse = this; + return invertedMultimap; + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final ImmutableList removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final ImmutableList replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * @serialData number of distinct keys, and then for each distinct key: the key, the number of + * values for that key, and the key's values + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int keyCount = stream.readInt(); + if (keyCount < 0) { + throw new InvalidObjectException("Invalid key count " + keyCount); + } + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int tmpSize = 0; + + for (int i = 0; i < keyCount; i++) { + Object key = stream.readObject(); + int valueCount = stream.readInt(); + if (valueCount <= 0) { + throw new InvalidObjectException("Invalid value count " + valueCount); + } + + ImmutableList.Builder valuesBuilder = ImmutableList.builder(); + for (int j = 0; j < valueCount; j++) { + valuesBuilder.add(stream.readObject()); + } + builder.put(key, valuesBuilder.build()); + tmpSize += valueCount; + } + + ImmutableMap> tmpMap; + try { + tmpMap = builder.buildOrThrow(); + } catch (IllegalArgumentException e) { + throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); + } + + FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); + FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); + } + + @GwtIncompatible // Not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMap.java new file mode 100644 index 0000000000000..dbe9c3f4077ab --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMap.java @@ -0,0 +1,1245 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * A {@link Map} whose contents will never change, with many other important properties detailed at + * {@link ImmutableCollection}. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +public abstract class ImmutableMap implements Map, Serializable { + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. + * Entries appear in the result {@code ImmutableMap} in encounter order. + * + *

If the mapped keys contain duplicates (according to {@link Object#equals(Object)}, an {@code + * IllegalArgumentException} is thrown when the collection operation is performed. (This differs + * from the {@code Collector} returned by {@link Collectors#toMap(Function, Function)}, which + * throws an {@code IllegalStateException}.) + * + * @since 21.0 + */ + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableMap(keyFunction, valueFunction); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. + * + *

If the mapped keys contain duplicates (according to {@link Object#equals(Object)}), the + * values are merged using the specified merging function. Entries will appear in the encounter + * order of the first occurrence of the key. + * + * @since 21.0 + */ + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + return CollectCollectors.toImmutableMap(keyFunction, valueFunction, mergeFunction); + } + + /** + * Returns the empty map. This map behaves and performs comparably to {@link + * Collections#emptyMap}, and is preferable mainly for consistency and maintainability of your + * code. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") + public static ImmutableMap of() { + return (ImmutableMap) RegularImmutableMap.EMPTY; + } + + /** + * Returns an immutable map containing a single entry. This map behaves and performs comparably to + * {@link Collections#singletonMap} but will not accept a null key or value. It is preferable + * mainly for consistency and maintainability of your code. + */ + public static ImmutableMap of(K k1, V v1) { + return ImmutableBiMap.of(k1, v1); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of(K k1, V v1, K k2, V v2) { + return RegularImmutableMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return RegularImmutableMap.fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + * @since 31.0 + */ + public static ImmutableMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + * @since 31.0 + */ + public static ImmutableMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + * @since 31.0 + */ + public static ImmutableMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + * @since 31.0 + */ + public static ImmutableMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8), + entryOf(k9, v9)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + * @since 31.0 + */ + public static ImmutableMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9, + K k10, + V v10) { + return RegularImmutableMap.fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8), + entryOf(k9, v9), + entryOf(k10, v10)); + } + + // looking for of() with > 10 entries? Use the builder or ofEntries instead. + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + * @since 31.0 + */ + @SafeVarargs + public static ImmutableMap ofEntries(Entry... entries) { + @SuppressWarnings("unchecked") // we will only ever read these + Entry[] entries2 = (Entry[]) entries; + return RegularImmutableMap.fromEntries(entries2); + } + + /** + * Verifies that {@code key} and {@code value} are non-null, and returns a new immutable entry + * with those values. + * + *

A call to {@link Entry#setValue} on the returned entry will always throw {@link + * UnsupportedOperationException}. + */ + static Entry entryOf(K key, V value) { + return new ImmutableMapEntry<>(key, value); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * Returns a new builder, expecting the specified number of entries to be added. + * + *

If {@code expectedSize} is exactly the number of entries added to the builder before {@link + * Builder#build} is called, the builder is likely to perform better than an unsized {@link + * #builder()} would have. + * + *

It is not specified if any performance benefits apply if {@code expectedSize} is close to, + * but not exactly, the number of entries added to the builder. + * + * @since 23.1 + */ + public static Builder builderWithExpectedSize(int expectedSize) { + checkNonnegative(expectedSize, "expectedSize"); + return new Builder<>(expectedSize); + } + + static void checkNoConflict( + boolean safe, String conflictDescription, Object entry1, Object entry2) { + if (!safe) { + throw conflictException(conflictDescription, entry1, entry2); + } + } + + static IllegalArgumentException conflictException( + String conflictDescription, Object entry1, Object entry2) { + return new IllegalArgumentException( + "Multiple entries with same " + conflictDescription + ": " + entry1 + " and " + entry2); + } + + /** + * A builder for creating immutable map instances, especially {@code public static final} maps + * ("constant maps"). Example: + * + *

{@code
+   * static final ImmutableMap WORD_TO_INT =
+   *     new ImmutableMap.Builder()
+   *         .put("one", 1)
+   *         .put("two", 2)
+   *         .put("three", 3)
+   *         .buildOrThrow();
+   * }
+ * + *

For small immutable maps, the {@code ImmutableMap.of()} methods are even more + * convenient. + * + *

By default, a {@code Builder} will generate maps that iterate over entries in the order they + * were inserted into the builder, equivalently to {@code LinkedHashMap}. For example, in the + * above example, {@code WORD_TO_INT.entrySet()} is guaranteed to iterate over the entries in the + * order {@code "one"=1, "two"=2, "three"=3}, and {@code keySet()} and {@code values()} respect + * the same order. If you want a different order, consider using {@link ImmutableSortedMap} to + * sort by keys, or call {@link #orderEntriesByValue(Comparator)}, which changes this builder to + * sort entries by value. + * + *

Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to + * build multiple maps in series. Each map is a superset of the maps created before it. + * + * @since 2.0 + */ + public static class Builder { + Comparator valueComparator; + Entry[] entries; + int size; + boolean entriesUsed; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableMap#builder}. + */ + public Builder() { + this(ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + Builder(int initialCapacity) { + this.entries = new Entry[initialCapacity]; + this.size = 0; + this.entriesUsed = false; + } + + private void ensureCapacity(int minCapacity) { + if (minCapacity > entries.length) { + entries = + Arrays.copyOf( + entries, ImmutableCollection.Builder.expandedCapacity(entries.length, minCapacity)); + entriesUsed = false; + } + } + + /** + * Associates {@code key} with {@code value} in the built map. If the same key is put more than + * once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep the last + * value put for that key. + */ + public Builder put(K key, V value) { + ensureCapacity(size + 1); + Entry entry = entryOf(key, value); + // don't inline this: we want to fail atomically if key or value is null + entries[size++] = entry; + return this; + } + + /** + * Adds the given {@code entry} to the map, making it immutable if necessary. If the same key is + * put more than once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will + * keep the last value put for that key. + * + * @since 11.0 + */ + public Builder put(Entry entry) { + return put(entry.getKey(), entry.getValue()); + } + + /** + * Associates all of the given map's keys and values in the built map. If the same key is put + * more than once, {@link #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep + * the last value put for that key. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + public Builder putAll(Map map) { + return putAll(map.entrySet()); + } + + /** + * Adds all of the given entries to the built map. If the same key is put more than once, {@link + * #buildOrThrow} will fail, while {@link #buildKeepingLast} will keep the last value put for + * that key. + * + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + public Builder putAll(Iterable> entries) { + if (entries instanceof Collection) { + ensureCapacity(size + ((Collection) entries).size()); + } + for (Entry entry : entries) { + put(entry); + } + return this; + } + + /** + * Configures this {@code Builder} to order entries by value according to the specified + * comparator. + * + *

The sort order is stable, that is, if two entries have values that compare as equivalent, + * the entry that was inserted first will be first in the built map's iteration order. + * + * @throws IllegalStateException if this method was already called + * @since 19.0 + */ + public Builder orderEntriesByValue(Comparator valueComparator) { + checkState(this.valueComparator == null, "valueComparator was already set"); + this.valueComparator = checkNotNull(valueComparator, "valueComparator"); + return this; + } + + Builder combine(Builder other) { + checkNotNull(other); + ensureCapacity(this.size + other.size); + System.arraycopy(other.entries, 0, this.entries, this.size, other.size); + this.size += other.size; + return this; + } + + private ImmutableMap build(boolean throwIfDuplicateKeys) { + /* + * If entries is full, or if hash flooding is detected, then this implementation may end up + * using the entries array directly and writing over the entry objects with non-terminal + * entries, but this is safe; if this Builder is used further, it will grow the entries array + * (so it can't affect the original array), and future build() calls will always copy any + * entry objects that cannot be safely reused. + */ + switch (size) { + case 0: + return of(); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entries[0]); + return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + break; + } + // localEntries is an alias for the entries field, except if we end up removing duplicates in + // a copy of the entries array. Likewise, localSize is the same as size except in that case. + // It's possible to keep using this Builder after calling buildKeepingLast(), so we need to + // ensure that its state is not corrupted by removing duplicates that should cause a later + // buildOrThrow() to fail, or by changing the size. + Entry[] localEntries; + int localSize = size; + if (valueComparator == null) { + localEntries = entries; + } else { + if (entriesUsed) { + entries = Arrays.copyOf(entries, size); + } + @SuppressWarnings("nullness") // entries 0..localSize-1 are non-null + Entry[] nonNullEntries = (Entry[]) entries; + if (!throwIfDuplicateKeys) { + // We want to retain only the last-put value for any given key, before sorting. + // This could be improved, but orderEntriesByValue is rather rarely used anyway. + nonNullEntries = lastEntryForEachKey(nonNullEntries, size); + localSize = nonNullEntries.length; + } + Arrays.sort( + nonNullEntries, + 0, + localSize, + Ordering.from(valueComparator).onResultOf(Maps.valueFunction())); + localEntries = (Entry[]) nonNullEntries; + } + entriesUsed = true; + return RegularImmutableMap.fromEntryArray(localSize, localEntries, throwIfDuplicateKeys); + } + + /** + * Returns a newly-created immutable map. The iteration order of the returned map is the order + * in which entries were inserted into the builder, unless {@link #orderEntriesByValue} was + * called, in which case entries are sorted by value. + * + *

Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method + * will throw an exception if there are duplicate keys. The {@code build()} method will soon be + * deprecated. + * + * @throws IllegalArgumentException if duplicate keys were added + */ + public ImmutableMap build() { + return buildOrThrow(); + } + + /** + * Returns a newly-created immutable map, or throws an exception if any key was added more than + * once. The iteration order of the returned map is the order in which entries were inserted + * into the builder, unless {@link #orderEntriesByValue} was called, in which case entries are + * sorted by value. + * + * @throws IllegalArgumentException if duplicate keys were added + * @since 31.0 + */ + public ImmutableMap buildOrThrow() { + return build(true); + } + + /** + * Returns a newly-created immutable map, using the last value for any key that was added more + * than once. The iteration order of the returned map is the order in which entries were + * inserted into the builder, unless {@link #orderEntriesByValue} was called, in which case + * entries are sorted by value. If a key was added more than once, it appears in iteration order + * based on the first time it was added, again unless {@link #orderEntriesByValue} was called. + * + *

In the current implementation, all values associated with a given key are stored in the + * {@code Builder} object, even though only one of them will be used in the built map. If there + * can be many repeated keys, it may be more space-efficient to use a {@link + * java.util.LinkedHashMap LinkedHashMap} and {@link ImmutableMap#copyOf(Map)} rather than + * {@code ImmutableMap.Builder}. + * + * @since 31.1 + */ + public ImmutableMap buildKeepingLast() { + return build(false); + } + + @VisibleForTesting // only for testing JDK backed implementation + ImmutableMap buildJdkBacked() { + checkState( + valueComparator == null, "buildJdkBacked is only for testing; can't use valueComparator"); + switch (size) { + case 0: + return of(); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entries[0]); + return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + entriesUsed = true; + return JdkBackedImmutableMap.create(size, entries, /* throwIfDuplicateKeys= */ true); + } + } + + private static Entry[] lastEntryForEachKey(Entry[] entries, int size) { + Set seen = new HashSet<>(); + BitSet dups = new BitSet(); // slots that are overridden by a later duplicate key + for (int i = size - 1; i >= 0; i--) { + if (!seen.add(entries[i].getKey())) { + dups.set(i); + } + } + if (dups.isEmpty()) { + return entries; + } + @SuppressWarnings({"rawtypes", "unchecked"}) + Entry[] newEntries = new Entry[size - dups.cardinality()]; + for (int inI = 0, outI = 0; inI < size; inI++) { + if (!dups.get(inI)) { + newEntries[outI++] = entries[inI]; + } + } + return newEntries; + } + } + + /** + * Returns an immutable map containing the same entries as {@code map}. The returned map iterates + * over entries in the same order as the {@code entrySet} of the original map. If {@code map} + * somehow contains entries with duplicate keys (for example, if it is a {@code SortedMap} whose + * comparator is not consistent with equals), the results of this method are undefined. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + public static ImmutableMap copyOf(Map map) { + if ((map instanceof ImmutableMap) && !(map instanceof SortedMap)) { + @SuppressWarnings("unchecked") // safe since map is not writable + ImmutableMap kvMap = (ImmutableMap) map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } else if (map instanceof EnumMap) { + @SuppressWarnings("unchecked") // safe since map is not writable + ImmutableMap kvMap = + (ImmutableMap) + copyOfEnumMap( + (EnumMap) map); // hide K (violates bounds) from J2KT, preserve V. + return kvMap; + } + return copyOf(map.entrySet()); + } + + /** + * Returns an immutable map containing the specified entries. The returned map iterates over + * entries in the same order as the original iterable. + * + * @throws NullPointerException if any key, value, or entry is null + * @throws IllegalArgumentException if two entries have the same key + * @since 19.0 + */ + public static ImmutableMap copyOf( + Iterable> entries) { + @SuppressWarnings("unchecked") // we'll only be using getKey and getValue, which are covariant + Entry[] entryArray = (Entry[]) Iterables.toArray(entries, EMPTY_ENTRY_ARRAY); + switch (entryArray.length) { + case 0: + return of(); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entryArray[0]); + return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + /* + * The current implementation will end up using entryArray directly, though it will write + * over the (arbitrary, potentially mutable) Entry objects actually stored in entryArray. + */ + return RegularImmutableMap.fromEntries(entryArray); + } + } + + private static , V> ImmutableMap copyOfEnumMap( + EnumMap original) { + EnumMap copy = new EnumMap<>((EnumMap) original); + for (Entry entry : copy.entrySet()) { + checkEntryNotNull(entry.getKey(), entry.getValue()); + } + return ImmutableEnumMap.asImmutable(copy); + } + + static final Entry[] EMPTY_ENTRY_ARRAY = new Entry[0]; + + abstract static class IteratorBasedImmutableMap extends ImmutableMap { + abstract UnmodifiableIterator> entryIterator(); + + Spliterator> entrySpliterator() { + return Spliterators.spliterator( + entryIterator(), + size(), + Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + + @Override + ImmutableSet createKeySet() { + return new ImmutableMapKeySet<>(this); + } + + @Override + ImmutableSet> createEntrySet() { + class EntrySetImpl extends ImmutableMapEntrySet { + @Override + ImmutableMap map() { + return IteratorBasedImmutableMap.this; + } + + @Override + public UnmodifiableIterator> iterator() { + return entryIterator(); + } + } + return new EntrySetImpl(); + } + + @Override + ImmutableCollection createValues() { + return new ImmutableMapValues<>(this); + } + } + + ImmutableMap() {} + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V put(K k, V v) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V putIfAbsent(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean replace(K key, V oldValue, V newValue) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V replace(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V computeIfAbsent(K key, Function mappingFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V computeIfPresent( + K key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V compute(K key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V merge(K key, V value, BiFunction function) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void putAll(Map map) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void replaceAll(BiFunction function) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V remove(Object o) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public boolean containsValue(Object value) { + return values().contains(value); + } + + // Overriding to mark it Nullable + @Override + public abstract V get(Object key); + + /** + * @since 21.0 (but only since 23.5 in the Android flavor). + * Note, however, that Java 8 users can call this method with any version and flavor of Guava. + */ + @Override + public final V getOrDefault(Object key, V defaultValue) { + /* + * Even though it's weird to pass a defaultValue that is null, some callers do so. Those who + * pass a literal "null" should probably just use `get`, but I would expect other callers to + * pass an expression that *might* be null. This could happen with: + * + * - a `getFooOrDefault(@CheckForNull Foo defaultValue)` method that returns + * `map.getOrDefault(FOO_KEY, defaultValue)` + * + * - a call that consults a chain of maps, as in `mapA.getOrDefault(key, mapB.getOrDefault(key, + * ...))` + * + * So it makes sense for the parameter (and thus the return type) to be @CheckForNull. + * + * Two other points: + * + * 1. We'll want to use something like @PolyNull once we can make that work for the various + * platforms we target. + * + * 2. Kotlin's Map type has a getOrDefault method that accepts and returns a "plain V," in + * contrast to the "V?" type that we're using. As a result, Kotlin sees a conflict between the + * nullness annotations in ImmutableMap and those in its own Map type. In response, it considers + * the parameter and return type both to be platform types. As a result, Kotlin permits calls + * that can lead to NullPointerException. That's unfortunate. But hopefully most Kotlin callers + * use `get(key) ?: defaultValue` instead of this method, anyway. + */ + V result = get(key); + // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. + if (result != null) { + return result; + } else { + return defaultValue; + } + } + + private transient ImmutableSet> entrySet; + + /** + * Returns an immutable set of the mappings in this map. The iteration order is specified by the + * method used to create this map. Typically, this is insertion order. + */ + @Override + public ImmutableSet> entrySet() { + ImmutableSet> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + abstract ImmutableSet> createEntrySet(); + + private transient ImmutableSet keySet; + + /** + * Returns an immutable set of the keys in this map, in the same order that they appear in {@link + * #entrySet}. + */ + @Override + public ImmutableSet keySet() { + ImmutableSet result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + /* + * This could have a good default implementation of return new ImmutableKeySet(this), + * but ProGuard can't figure out how to eliminate that default when RegularImmutableMap + * overrides it. + */ + abstract ImmutableSet createKeySet(); + + UnmodifiableIterator keyIterator() { + final UnmodifiableIterator> entryIterator = entrySet().iterator(); + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public K next() { + return entryIterator.next().getKey(); + } + }; + } + + Spliterator keySpliterator() { + return CollectSpliterators.map(entrySet().spliterator(), Entry::getKey); + } + + private transient ImmutableCollection values; + + /** + * Returns an immutable collection of the values in this map, in the same order that they appear + * in {@link #entrySet}. + */ + @Override + public ImmutableCollection values() { + ImmutableCollection result = values; + return (result == null) ? values = createValues() : result; + } + + /* + * This could have a good default implementation of {@code return new + * ImmutableMapValues(this)}, but ProGuard can't figure out how to eliminate that default + * when RegularImmutableMap overrides it. + */ + abstract ImmutableCollection createValues(); + + // cached so that this.multimapView().inverse() only computes inverse once + private transient ImmutableSetMultimap multimapView; + + /** + * Returns a multimap view of the map. + * + * @since 14.0 + */ + public ImmutableSetMultimap asMultimap() { + if (isEmpty()) { + return ImmutableSetMultimap.of(); + } + ImmutableSetMultimap result = multimapView; + return (result == null) + ? (multimapView = + new ImmutableSetMultimap<>(new MapViewOfValuesAsSingletonSets(), size(), null)) + : result; + } + + private final class MapViewOfValuesAsSingletonSets + extends IteratorBasedImmutableMap> { + + @Override + public int size() { + return ImmutableMap.this.size(); + } + + @Override + ImmutableSet createKeySet() { + return ImmutableMap.this.keySet(); + } + + @Override + public boolean containsKey(Object key) { + return ImmutableMap.this.containsKey(key); + } + + @Override + public ImmutableSet get(Object key) { + V outerValue = ImmutableMap.this.get(key); + return (outerValue == null) ? null : ImmutableSet.of(outerValue); + } + + @Override + boolean isPartialView() { + return ImmutableMap.this.isPartialView(); + } + + @Override + public int hashCode() { + // ImmutableSet.of(value).hashCode() == value.hashCode(), so the hashes are the same + return ImmutableMap.this.hashCode(); + } + + @Override + boolean isHashCodeFast() { + return ImmutableMap.this.isHashCodeFast(); + } + + @Override + UnmodifiableIterator>> entryIterator() { + final Iterator> backingIterator = ImmutableMap.this.entrySet().iterator(); + return new UnmodifiableIterator>>() { + @Override + public boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override + public Entry> next() { + final Entry backingEntry = backingIterator.next(); + return new AbstractMapEntry>() { + @Override + public K getKey() { + return backingEntry.getKey(); + } + + @Override + public ImmutableSet getValue() { + return ImmutableSet.of(backingEntry.getValue()); + } + }; + } + }; + } + } + + @Override + public boolean equals(Object object) { + return Maps.equalsImpl(this, object); + } + + abstract boolean isPartialView(); + + @Override + public int hashCode() { + return Sets.hashCodeImpl(entrySet()); + } + + boolean isHashCodeFast() { + return false; + } + + @Override + public String toString() { + return Maps.toStringImpl(this); + } + + /** + * Serialized type for all ImmutableMap instances. It captures the logical contents and they are + * reconstructed using public factory methods. This ensures that the implementation types remain + * as implementation details. + */ + @J2ktIncompatible // serialization + static class SerializedForm implements Serializable { + // This object retains references to collections returned by keySet() and value(). This saves + // bytes when the both the map and its keySet or value collection are written to the same + // instance of ObjectOutputStream. + + // TODO(b/160980469): remove support for the old serialization format after some time + private static final boolean USE_LEGACY_SERIALIZATION = true; + + private final Object keys; + private final Object values; + + SerializedForm(ImmutableMap map) { + if (USE_LEGACY_SERIALIZATION) { + Object[] keys = new Object[map.size()]; + Object[] values = new Object[map.size()]; + int i = 0; + // "extends Object" works around https://github.com/typetools/checker-framework/issues/3013 + for (Entry entry : map.entrySet()) { + keys[i] = entry.getKey(); + values[i] = entry.getValue(); + i++; + } + this.keys = keys; + this.values = values; + return; + } + this.keys = map.keySet(); + this.values = map.values(); + } + + @SuppressWarnings("unchecked") + final Object readResolve() { + if (!(this.keys instanceof ImmutableSet)) { + return legacyReadResolve(); + } + + ImmutableSet keySet = (ImmutableSet) this.keys; + ImmutableCollection values = (ImmutableCollection) this.values; + + Builder builder = makeBuilder(keySet.size()); + + UnmodifiableIterator keyIter = keySet.iterator(); + UnmodifiableIterator valueIter = values.iterator(); + + while (keyIter.hasNext()) { + builder.put(keyIter.next(), valueIter.next()); + } + + return builder.buildOrThrow(); + } + + @SuppressWarnings("unchecked") + final Object legacyReadResolve() { + K[] keys = (K[]) this.keys; + V[] values = (V[]) this.values; + + Builder builder = makeBuilder(keys.length); + + for (int i = 0; i < keys.length; i++) { + builder.put(keys[i], values[i]); + } + return builder.buildOrThrow(); + } + + /** + * Returns a builder that builds the unserialized type. Subclasses should override this method. + */ + Builder makeBuilder(int size) { + return new Builder<>(size); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a serializable form of this object. Non-public subclasses should not override this + * method. Publicly-accessible subclasses must override this method and should return a subclass + * of SerializedForm whose readResolve() method returns objects of the subclass type. + */ + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm<>(this); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapEntry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapEntry.java new file mode 100644 index 0000000000000..a7c962775ec0f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapEntry.java @@ -0,0 +1,123 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; + +/** + * Implementation of {@code Entry} for {@link ImmutableMap} that adds extra methods to traverse hash + * buckets for the key and the value. This allows reuse in {@link RegularImmutableMap} and {@link + * RegularImmutableBiMap}, which don't have to recopy the entries created by their {@code Builder} + * implementations. + * + *

This base implementation has no key or value pointers, so instances of ImmutableMapEntry (but + * not its subclasses) can be reused when copied from one ImmutableMap to another. + * + * @author Louis Wasserman + */ +@GwtIncompatible // unnecessary +@ElementTypesAreNonnullByDefault +class ImmutableMapEntry extends ImmutableEntry { + /** + * Creates an {@code ImmutableMapEntry} array to hold parameterized entries. The result must never + * be upcast back to ImmutableMapEntry[] (or Object[], etc.), or allowed to escape the class. + * + *

The returned array has all its elements set to their initial null values. However, we don't + * declare it as {@code @Nullable ImmutableMapEntry[]} because our checker doesn't require newly + * created arrays to have a {@code @Nullable} element type even when they're created directly with + * {@code new ImmutableMapEntry[...]}, so it seems silly to insist on that only here. + */ + @SuppressWarnings("unchecked") // Safe as long as the javadocs are followed + static ImmutableMapEntry[] createEntryArray(int size) { + return new ImmutableMapEntry[size]; + } + + ImmutableMapEntry(K key, V value) { + super(key, value); + checkEntryNotNull(key, value); + } + + ImmutableMapEntry(ImmutableMapEntry contents) { + super(contents.getKey(), contents.getValue()); + // null check would be redundant + } + + ImmutableMapEntry getNextInKeyBucket() { + return null; + } + + ImmutableMapEntry getNextInValueBucket() { + return null; + } + + /** + * Returns true if this entry has no bucket links and can safely be reused as a terminal entry in + * a bucket in another map. + */ + boolean isReusable() { + return true; + } + + static class NonTerminalImmutableMapEntry extends ImmutableMapEntry { + /* + * Yes, we sometimes set nextInKeyBucket to null, even for this "non-terminal" entry. We don't + * do that with a plain NonTerminalImmutableMapEntry, but we do it with the BiMap-specific + * subclass below. That's because the Entry might be non-terminal in the key bucket but terminal + * in the value bucket (or vice versa). + */ + private final transient ImmutableMapEntry nextInKeyBucket; + + NonTerminalImmutableMapEntry(K key, V value, ImmutableMapEntry nextInKeyBucket) { + super(key, value); + this.nextInKeyBucket = nextInKeyBucket; + } + + @Override + final ImmutableMapEntry getNextInKeyBucket() { + return nextInKeyBucket; + } + + @Override + final boolean isReusable() { + return false; + } + } + + static final class NonTerminalImmutableBiMapEntry + extends NonTerminalImmutableMapEntry { + private final transient ImmutableMapEntry nextInValueBucket; + + NonTerminalImmutableBiMapEntry( + K key, + V value, + ImmutableMapEntry nextInKeyBucket, + ImmutableMapEntry nextInValueBucket) { + super(key, value, nextInKeyBucket); + this.nextInValueBucket = nextInValueBucket; + } + + @Override + ImmutableMapEntry getNextInValueBucket() { + return nextInValueBucket; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapEntrySet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapEntrySet.java new file mode 100644 index 0000000000000..f1954580ea3fc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapEntrySet.java @@ -0,0 +1,150 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Map.Entry; +import java.util.Spliterator; +import java.util.function.Consumer; + +/** + * {@code entrySet()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ImmutableMapEntrySet extends ImmutableSet.CachingAsList> { + static final class RegularEntrySet extends ImmutableMapEntrySet { + private final transient ImmutableMap map; + private final transient ImmutableList> entries; + + RegularEntrySet(ImmutableMap map, Entry[] entries) { + this(map, ImmutableList.>asImmutableList(entries)); + } + + RegularEntrySet(ImmutableMap map, ImmutableList> entries) { + this.map = map; + this.entries = entries; + } + + @Override + ImmutableMap map() { + return map; + } + + @Override + @GwtIncompatible("not used in GWT") + int copyIntoArray(Object[] dst, int offset) { + return entries.copyIntoArray(dst, offset); + } + + @Override + public UnmodifiableIterator> iterator() { + return entries.iterator(); + } + + @Override + public Spliterator> spliterator() { + return entries.spliterator(); + } + + @Override + public void forEach(Consumer> action) { + entries.forEach(action); + } + + @Override + ImmutableList> createAsList() { + return new RegularImmutableAsList<>(this, entries); + } + } + + ImmutableMapEntrySet() {} + + abstract ImmutableMap map(); + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + V value = map().get(entry.getKey()); + return value != null && value.equals(entry.getValue()); + } + return false; + } + + @Override + boolean isPartialView() { + return map().isPartialView(); + } + + @Override + @GwtIncompatible // not used in GWT + boolean isHashCodeFast() { + return map().isHashCodeFast(); + } + + @Override + public int hashCode() { + return map().hashCode(); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + @Override + Object writeReplace() { + return new EntrySetSerializedForm<>(map()); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use EntrySetSerializedForm"); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private static class EntrySetSerializedForm implements Serializable { + final ImmutableMap map; + + EntrySetSerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return map.entrySet(); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapKeySet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapKeySet.java new file mode 100644 index 0000000000000..3c31e9db8f30c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapKeySet.java @@ -0,0 +1,100 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Serializable; +import java.util.Spliterator; +import java.util.function.Consumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * {@code keySet()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class ImmutableMapKeySet extends IndexedImmutableSet { + private final ImmutableMap map; + + ImmutableMapKeySet(ImmutableMap map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return map.keyIterator(); + } + + @Override + public Spliterator spliterator() { + return map.keySpliterator(); + } + + @Override + public boolean contains(Object object) { + return map.containsKey(object); + } + + @Override + K get(int index) { + return map.entrySet().asList().get(index).getKey(); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + map.forEach((k, v) -> action.accept(k)); + } + + @Override + boolean isPartialView() { + return true; + } + + // No longer used for new writes, but kept so that old data can still be read. + @GwtIncompatible // serialization + @J2ktIncompatible + @SuppressWarnings("unused") + private static class KeySetSerializedForm implements Serializable { + final ImmutableMap map; + + KeySetSerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return map.keySet(); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapValues.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapValues.java new file mode 100644 index 0000000000000..64d1efb6a3b0e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMapValues.java @@ -0,0 +1,125 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Serializable; +import java.util.Map.Entry; +import java.util.Spliterator; +import java.util.function.Consumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * {@code values()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class ImmutableMapValues extends ImmutableCollection { + private final ImmutableMap map; + + ImmutableMapValues(ImmutableMap map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return new UnmodifiableIterator() { + final UnmodifiableIterator> entryItr = map.entrySet().iterator(); + + @Override + public boolean hasNext() { + return entryItr.hasNext(); + } + + @Override + public V next() { + return entryItr.next().getValue(); + } + }; + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.map(map.entrySet().spliterator(), Entry::getValue); + } + + @Override + public boolean contains(Object object) { + return object != null && Iterators.contains(iterator(), object); + } + + @Override + boolean isPartialView() { + return true; + } + + @Override + public ImmutableList asList() { + final ImmutableList> entryList = map.entrySet().asList(); + return new ImmutableAsList() { + @Override + public V get(int index) { + return entryList.get(index).getValue(); + } + + @Override + ImmutableCollection delegateCollection() { + return ImmutableMapValues.this; + } + }; + } + + @GwtIncompatible // serialization + @Override + public void forEach(Consumer action) { + checkNotNull(action); + map.forEach((k, v) -> action.accept(v)); + } + + // No longer used for new writes, but kept so that old data can still be read. + @GwtIncompatible // serialization + @J2ktIncompatible + @SuppressWarnings("unused") + private static class SerializedForm implements Serializable { + final ImmutableMap map; + + SerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return map.values(); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultimap.java new file mode 100644 index 0000000000000..baed6727178dc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultimap.java @@ -0,0 +1,765 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.BiConsumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static org.apache.iotdb.google.common.collect.Maps.immutableEntry; + +/** + * A {@link Multimap} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

Warning: avoid direct usage of {@link ImmutableMultimap} as a type (as with + * {@link Multimap} itself). Prefer subtypes such as {@link ImmutableSetMultimap} or {@link + * ImmutableListMultimap}, which have well-defined {@link #equals} semantics, thus avoiding a common + * source of bugs and confusion. + * + *

Note: every {@link ImmutableMultimap} offers an {@link #inverse} view, so there is no + * need for a distinct {@code ImmutableBiMultimap} type. + * + *

+ * + *

Key-grouped iteration. All view collections follow the same iteration order. In all + * current implementations, the iteration order always keeps multiple entries with the same key + * together. Any creation method that would customarily respect insertion order (such as {@link + * #copyOf(Multimap)}) instead preserves key-grouped order by inserting entries for an existing key + * immediately after the last entry having that key. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class ImmutableMultimap extends BaseImmutableMultimap + implements Serializable { + + /** + * Returns an empty multimap. + * + *

Performance note: the instance returned is a singleton. + */ + public static ImmutableMultimap of() { + return ImmutableListMultimap.of(); + } + + /** Returns an immutable multimap containing a single entry. */ + public static ImmutableMultimap of(K k1, V v1) { + return ImmutableListMultimap.of(k1, v1); + } + + /** Returns an immutable multimap containing the given entries, in order. */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2) { + return ImmutableListMultimap.of(k1, v1, k2, v2); + } + + /** + * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion + * order described in the class documentation. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); + } + + /** + * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion + * order described in the class documentation. + */ + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); + } + + /** + * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion + * order described in the class documentation. + */ + public static ImmutableMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * A builder for creating immutable multimap instances, especially {@code public static final} + * multimaps ("constant multimaps"). Example: + * + *

{@code
+   * static final Multimap STRING_TO_INTEGER_MULTIMAP =
+   *     new ImmutableMultimap.Builder()
+   *         .put("one", 1)
+   *         .putAll("several", 1, 2, 3)
+   *         .putAll("many", 1, 2, 3, 4, 5)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple multimaps in series. Each multimap contains the key-value mappings in the previously + * created multimaps. + * + * @since 2.0 + */ + public static class Builder { + final Map> builderMap; + Comparator keyComparator; + Comparator valueComparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableMultimap#builder}. + */ + public Builder() { + this.builderMap = Platform.preservesInsertionOrderOnPutsMap(); + } + + Collection newMutableValueCollection() { + return new ArrayList<>(); + } + + /** Adds a key-value mapping to the built multimap. */ + public Builder put(K key, V value) { + checkEntryNotNull(key, value); + Collection valueCollection = builderMap.get(key); + if (valueCollection == null) { + builderMap.put(key, valueCollection = newMutableValueCollection()); + } + valueCollection.add(value); + return this; + } + + /** + * Adds an entry to the built multimap. + * + * @since 11.0 + */ + public Builder put(Entry entry) { + return put(entry.getKey(), entry.getValue()); + } + + /** + * Adds entries to the built multimap. + * + * @since 19.0 + */ + public Builder putAll(Iterable> entries) { + for (Entry entry : entries) { + put(entry); + } + return this; + } + + /** + * Stores a collection of values with the same key in the built multimap. + * + * @throws NullPointerException if {@code key}, {@code values}, or any element in {@code values} + * is null. The builder is left in an invalid state. + */ + public Builder putAll(K key, Iterable values) { + if (key == null) { + throw new NullPointerException("null key in entry: null=" + Iterables.toString(values)); + } + Collection valueCollection = builderMap.get(key); + if (valueCollection != null) { + for (V value : values) { + checkEntryNotNull(key, value); + valueCollection.add(value); + } + return this; + } + Iterator valuesItr = values.iterator(); + if (!valuesItr.hasNext()) { + return this; + } + valueCollection = newMutableValueCollection(); + while (valuesItr.hasNext()) { + V value = valuesItr.next(); + checkEntryNotNull(key, value); + valueCollection.add(value); + } + builderMap.put(key, valueCollection); + return this; + } + + /** + * Stores an array of values with the same key in the built multimap. + * + * @throws NullPointerException if the key or any value is null. The builder is left in an + * invalid state. + */ + public Builder putAll(K key, V... values) { + return putAll(key, Arrays.asList(values)); + } + + /** + * Stores another multimap's entries in the built multimap. The generated multimap's key and + * value orderings correspond to the iteration ordering of the {@code multimap.asMap()} view, + * with new keys and values following any existing keys and values. + * + * @throws NullPointerException if any key or value in {@code multimap} is null. The builder is + * left in an invalid state. + */ + public Builder putAll(Multimap multimap) { + for (Entry> entry : + multimap.asMap().entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + /** + * Specifies the ordering of the generated multimap's keys. + * + * @since 8.0 + */ + public Builder orderKeysBy(Comparator keyComparator) { + this.keyComparator = checkNotNull(keyComparator); + return this; + } + + /** + * Specifies the ordering of the generated multimap's values for each key. + * + * @since 8.0 + */ + public Builder orderValuesBy(Comparator valueComparator) { + this.valueComparator = checkNotNull(valueComparator); + return this; + } + + Builder combine(Builder other) { + for (Map.Entry> entry : other.builderMap.entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + /** Returns a newly-created immutable multimap. */ + public ImmutableMultimap build() { + Collection>> mapEntries = builderMap.entrySet(); + if (keyComparator != null) { + mapEntries = Ordering.from(keyComparator).onKeys().immutableSortedCopy(mapEntries); + } + return ImmutableListMultimap.fromMapEntries(mapEntries, valueComparator); + } + } + + /** + * Returns an immutable multimap containing the same mappings as {@code multimap}, in the + * "key-grouped" iteration order described in the class documentation. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code multimap} is null + */ + public static ImmutableMultimap copyOf(Multimap multimap) { + if (multimap instanceof ImmutableMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableMultimap kvMultimap = (ImmutableMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + return ImmutableListMultimap.copyOf(multimap); + } + + /** + * Returns an immutable multimap containing the specified entries. The returned multimap iterates + * over keys in the order they were first encountered in the input, and the values for each key + * are iterated in the order they were encountered. + * + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + public static ImmutableMultimap copyOf( + Iterable> entries) { + return ImmutableListMultimap.copyOf(entries); + } + + final transient ImmutableMap> map; + final transient int size; + + // These constants allow the deserialization code to set final fields. This + // holder class makes sure they are not initialized unless an instance is + // deserialized. + @GwtIncompatible // java serialization is not supported + @J2ktIncompatible + static class FieldSettersHolder { + static final Serialization.FieldSetter MAP_FIELD_SETTER = + Serialization.getFieldSetter(ImmutableMultimap.class, "map"); + static final Serialization.FieldSetter SIZE_FIELD_SETTER = + Serialization.getFieldSetter(ImmutableMultimap.class, "size"); + } + + ImmutableMultimap(ImmutableMap> map, int size) { + this.map = map; + this.size = size; + } + + // mutators (not supported) + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + + // DoNotCall wants this to be final, but we want to override it to return more specific types. + // Inheritance is closed, and all subtypes are @DoNotCall, so this is safe to suppress. + @SuppressWarnings("DoNotCall") + public ImmutableCollection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + + // DoNotCall wants this to be final, but we want to override it to return more specific types. + // Inheritance is closed, and all subtypes are @DoNotCall, so this is safe to suppress. + @SuppressWarnings("DoNotCall") + public ImmutableCollection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an immutable collection of the values for the given key. If no mappings in the multimap + * have the provided key, an empty immutable collection is returned. The values are in the same + * order as the parameters used to build this multimap. + */ + @Override + public abstract ImmutableCollection get(K key); + + /** + * Returns an immutable multimap which is the inverse of this one. For every key-value mapping in + * the original, the result will have a mapping with key and value reversed. + * + * @since 11.0 + */ + public abstract ImmutableMultimap inverse(); + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + /** + * Returns {@code true} if this immutable multimap's implementation contains references to + * user-created objects that aren't accessible via this multimap's methods. This is generally used + * to determine whether {@code copyOf} implementations should make an explicit copy to avoid + * memory leaks. + */ + boolean isPartialView() { + return map.isPartialView(); + } + + // accessors + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return value != null && super.containsValue(value); + } + + @Override + public int size() { + return size; + } + + // views + + /** + * Returns an immutable set of the distinct keys in this multimap, in the same order as they + * appear in this multimap. + */ + @Override + public ImmutableSet keySet() { + return map.keySet(); + } + + @Override + Set createKeySet() { + throw new AssertionError("unreachable"); + } + + /** + * Returns an immutable map that associates each key with its corresponding values in the + * multimap. Keys and values appear in the same order as in this multimap. + */ + @Override + @SuppressWarnings("unchecked") // a widening cast + public ImmutableMap> asMap() { + return (ImmutableMap) map; + } + + @Override + Map> createAsMap() { + throw new AssertionError("should never be called"); + } + + /** Returns an immutable collection of all key-value pairs in the multimap. */ + @Override + public ImmutableCollection> entries() { + return (ImmutableCollection>) super.entries(); + } + + @Override + ImmutableCollection> createEntries() { + return new EntryCollection<>(this); + } + + private static class EntryCollection extends ImmutableCollection> { + final ImmutableMultimap multimap; + + EntryCollection(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + @Override + public UnmodifiableIterator> iterator() { + return multimap.entryIterator(); + } + + @Override + boolean isPartialView() { + return multimap.isPartialView(); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + return multimap.containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + private static final long serialVersionUID = 0; + } + + @Override + UnmodifiableIterator> entryIterator() { + return new UnmodifiableIterator>() { + final Iterator>> asMapItr = + map.entrySet().iterator(); + K currentKey = null; + Iterator valueItr = Iterators.emptyIterator(); + + @Override + public boolean hasNext() { + return valueItr.hasNext() || asMapItr.hasNext(); + } + + @Override + public Entry next() { + if (!valueItr.hasNext()) { + Entry> entry = asMapItr.next(); + currentKey = entry.getKey(); + valueItr = entry.getValue().iterator(); + } + /* + * requireNonNull is safe: The first call to this method always enters the !hasNext() case + * and populates currentKey, after which it's never cleared. + */ + return immutableEntry(requireNonNull(currentKey), valueItr.next()); + } + }; + } + + @Override + Spliterator> entrySpliterator() { + return CollectSpliterators.flatMap( + asMap().entrySet().spliterator(), + keyToValueCollectionEntry -> { + K key = keyToValueCollectionEntry.getKey(); + Collection valueCollection = keyToValueCollectionEntry.getValue(); + return CollectSpliterators.map( + valueCollection.spliterator(), (V value) -> Maps.immutableEntry(key, value)); + }, + Spliterator.SIZED | (this instanceof SetMultimap ? Spliterator.DISTINCT : 0), + size()); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + asMap() + .forEach( + (key, valueCollection) -> valueCollection.forEach(value -> action.accept(key, value))); + } + + /** + * Returns an immutable multiset containing all the keys in this multimap, in the same order and + * with the same frequencies as they appear in this multimap; to get only a single occurrence of + * each key, use {@link #keySet}. + */ + @Override + public ImmutableMultiset keys() { + return (ImmutableMultiset) super.keys(); + } + + @Override + ImmutableMultiset createKeys() { + return new Keys(); + } + + @SuppressWarnings("serial") // Uses writeReplace, not default serialization + class Keys extends ImmutableMultiset { + @Override + public boolean contains(Object object) { + return containsKey(object); + } + + @Override + public int count(Object element) { + Collection values = map.get(element); + return (values == null) ? 0 : values.size(); + } + + @Override + public ImmutableSet elementSet() { + return keySet(); + } + + @Override + public int size() { + return ImmutableMultimap.this.size(); + } + + @Override + Multiset.Entry getEntry(int index) { + Map.Entry> entry = map.entrySet().asList().get(index); + return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); + } + + @Override + boolean isPartialView() { + return true; + } + + @GwtIncompatible + @J2ktIncompatible + @Override + Object writeReplace() { + return new KeysSerializedForm(ImmutableMultimap.this); + } + + @GwtIncompatible + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use KeysSerializedForm"); + } + } + + @GwtIncompatible + @J2ktIncompatible + private static final class KeysSerializedForm implements Serializable { + final ImmutableMultimap multimap; + + KeysSerializedForm(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + Object readResolve() { + return multimap.keys(); + } + } + + /** + * Returns an immutable collection of the values in this multimap. Its iterator traverses the + * values for the first key, the values for the second key, and so on. + */ + @Override + public ImmutableCollection values() { + return (ImmutableCollection) super.values(); + } + + @Override + ImmutableCollection createValues() { + return new Values<>(this); + } + + @Override + UnmodifiableIterator valueIterator() { + return new UnmodifiableIterator() { + Iterator> valueCollectionItr = map.values().iterator(); + Iterator valueItr = Iterators.emptyIterator(); + + @Override + public boolean hasNext() { + return valueItr.hasNext() || valueCollectionItr.hasNext(); + } + + @Override + public V next() { + if (!valueItr.hasNext()) { + valueItr = valueCollectionItr.next().iterator(); + } + return valueItr.next(); + } + }; + } + + private static final class Values extends ImmutableCollection { + private final transient ImmutableMultimap multimap; + + Values(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + @Override + public boolean contains(Object object) { + return multimap.containsValue(object); + } + + @Override + public UnmodifiableIterator iterator() { + return multimap.valueIterator(); + } + + @GwtIncompatible // not present in emulated superclass + @Override + int copyIntoArray(Object[] dst, int offset) { + for (ImmutableCollection valueCollection : multimap.map.values()) { + offset = valueCollection.copyIntoArray(dst, offset); + } + return offset; + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + boolean isPartialView() { + return true; + } + + @J2ktIncompatible // serialization + private static final long serialVersionUID = 0; + } + + @J2ktIncompatible // serialization + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultiset.java new file mode 100644 index 0000000000000..71855f6712c6e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultiset.java @@ -0,0 +1,646 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Multiset} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

Grouped iteration. In all current implementations, duplicate elements always appear + * consecutively when iterating. Elements iterate in order by the first appearance of that + * element when the multiset was created. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +public abstract class ImmutableMultiset extends ImmutableMultisetGwtSerializationDependencies + implements Multiset { + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableMultiset}. Elements iterate in order by the first appearance of that element in + * encounter order. + * + * @since 21.0 + */ + public static Collector> toImmutableMultiset() { + return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1); + } + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose + * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to + * the result of applying {@code countFunction} to the inputs. + * + *

If the mapped elements contain duplicates (according to {@link Object#equals}), the first + * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of + * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element. + * + * @since 22.0 + */ + public static Collector> toImmutableMultiset( + Function elementFunction, ToIntFunction countFunction) { + return CollectCollectors.toImmutableMultiset(elementFunction, countFunction); + } + + /** + * Returns the empty immutable multiset. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") // all supported methods are covariant + public static ImmutableMultiset of() { + return (ImmutableMultiset) RegularImmutableMultiset.EMPTY; + } + + /** + * Returns an immutable multiset containing a single element. + * + * @throws NullPointerException if {@code element} is null + * @since 6.0 (source-compatible since 2.0) + */ + public static ImmutableMultiset of(E element) { + return copyFromElements(element); + } + + /** + * Returns an immutable multiset containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + public static ImmutableMultiset of(E e1, E e2) { + return copyFromElements(e1, e2); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + public static ImmutableMultiset of(E e1, E e2, E e3) { + return copyFromElements(e1, e2, e3); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + public static ImmutableMultiset of(E e1, E e2, E e3, E e4) { + return copyFromElements(e1, e2, e3, e4); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5) { + return copyFromElements(e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any element is null + * @since 6.0 (source-compatible since 2.0) + */ + public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { + return new Builder().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build(); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 6.0 + */ + public static ImmutableMultiset copyOf(E[] elements) { + return copyFromElements(elements); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableMultiset copyOf(Iterable elements) { + if (elements instanceof ImmutableMultiset) { + @SuppressWarnings("unchecked") // all supported methods are covariant + ImmutableMultiset result = (ImmutableMultiset) elements; + if (!result.isPartialView()) { + return result; + } + } + + Multiset multiset = + (elements instanceof Multiset) + ? Multisets.cast(elements) + : LinkedHashMultiset.create(elements); + + return copyFromEntries(multiset.entrySet()); + } + + /** + * Returns an immutable multiset containing the given elements, in the "grouped iteration order" + * described in the class documentation. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableMultiset copyOf(Iterator elements) { + Multiset multiset = LinkedHashMultiset.create(); + Iterators.addAll(multiset, elements); + return copyFromEntries(multiset.entrySet()); + } + + private static ImmutableMultiset copyFromElements(E... elements) { + Multiset multiset = LinkedHashMultiset.create(); + Collections.addAll(multiset, elements); + return copyFromEntries(multiset.entrySet()); + } + + static ImmutableMultiset copyFromEntries( + Collection> entries) { + if (entries.isEmpty()) { + return of(); + } else { + return RegularImmutableMultiset.create(entries); + } + } + + ImmutableMultiset() {} + + @Override + public UnmodifiableIterator iterator() { + final Iterator> entryIterator = entrySet().iterator(); + return new UnmodifiableIterator() { + int remaining; + E element; + + @Override + public boolean hasNext() { + return (remaining > 0) || entryIterator.hasNext(); + } + + @Override + public E next() { + if (remaining <= 0) { + Entry entry = entryIterator.next(); + element = entry.getElement(); + remaining = entry.getCount(); + } + remaining--; + /* + * requireNonNull is safe because `remaining` starts at 0, forcing us to initialize + * `element` above. After that, we never clear it. + */ + return requireNonNull(element); + } + }; + } + + private transient ImmutableList asList; + + @Override + public ImmutableList asList() { + ImmutableList result = asList; + return (result == null) ? asList = super.asList() : result; + } + + @Override + public boolean contains(Object object) { + return count(object) > 0; + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final int add(E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final int setCount(E element, int count) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean setCount(E element, int oldCount, int newCount) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible // not present in emulated superclass + @Override + int copyIntoArray(Object[] dst, int offset) { + for (Multiset.Entry entry : entrySet()) { + Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); + offset += entry.getCount(); + } + return offset; + } + + @Override + public boolean equals(Object object) { + return Multisets.equalsImpl(this, object); + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(entrySet()); + } + + @Override + public String toString() { + return entrySet().toString(); + } + + /** + * @since 21.0 (present with return type {@code Set} since 2.0) + */ + @Override + public abstract ImmutableSet elementSet(); + + private transient ImmutableSet> entrySet; + + @Override + public ImmutableSet> entrySet() { + ImmutableSet> es = entrySet; + return (es == null) ? (entrySet = createEntrySet()) : es; + } + + private ImmutableSet> createEntrySet() { + return isEmpty() ? ImmutableSet.>of() : new EntrySet(); + } + + abstract Entry getEntry(int index); + + private final class EntrySet extends IndexedImmutableSet> { + @Override + boolean isPartialView() { + return ImmutableMultiset.this.isPartialView(); + } + + @Override + Entry get(int index) { + return getEntry(index); + } + + @Override + public int size() { + return elementSet().size(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + if (entry.getCount() <= 0) { + return false; + } + int count = count(entry.getElement()); + return count == entry.getCount(); + } + return false; + } + + @Override + public int hashCode() { + return ImmutableMultiset.this.hashCode(); + } + + @GwtIncompatible + @J2ktIncompatible + @Override + Object writeReplace() { + return new EntrySetSerializedForm(ImmutableMultiset.this); + } + + @GwtIncompatible + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use EntrySetSerializedForm"); + } + + @J2ktIncompatible private static final long serialVersionUID = 0; + } + + @GwtIncompatible + @J2ktIncompatible + static class EntrySetSerializedForm implements Serializable { + final ImmutableMultiset multiset; + + EntrySetSerializedForm(ImmutableMultiset multiset) { + this.multiset = multiset; + } + + Object readResolve() { + return multiset.entrySet(); + } + } + + @GwtIncompatible + @J2ktIncompatible + @Override + Object writeReplace() { + return new SerializedForm(this); + } + + @GwtIncompatible + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for creating immutable multiset instances, especially {@code public static final} + * multisets ("constant multisets"). Example: + * + *

{@code
+   * public static final ImmutableMultiset BEANS =
+   *     new ImmutableMultiset.Builder()
+   *         .addCopies(Bean.COCOA, 4)
+   *         .addCopies(Bean.GARDEN, 6)
+   *         .addCopies(Bean.RED, 8)
+   *         .addCopies(Bean.BLACK_EYED, 10)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple multisets in series. + * + * @since 2.0 + */ + public static class Builder extends ImmutableCollection.Builder { + final Multiset contents; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableMultiset#builder}. + */ + public Builder() { + this(LinkedHashMultiset.create()); + } + + Builder(Multiset contents) { + this.contents = contents; + } + + /** + * Adds {@code element} to the {@code ImmutableMultiset}. + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override + public Builder add(E element) { + contents.add(checkNotNull(element)); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableMultiset}. + * + * @param elements the elements to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder add(E... elements) { + super.add(elements); + return this; + } + + /** + * Adds a number of occurrences of an element to this {@code ImmutableMultiset}. + * + * @param element the element to add + * @param occurrences the number of occurrences of the element to add. May be zero, in which + * case no change will be made. + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation + * would result in more than {@link Integer#MAX_VALUE} occurrences of the element + */ + public Builder addCopies(E element, int occurrences) { + contents.add(checkNotNull(element), occurrences); + return this; + } + + /** + * Adds or removes the necessary occurrences of an element such that the element attains the + * desired count. + * + * @param element the element to add or remove occurrences of + * @param count the desired count of the element in this multiset + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code count} is negative + */ + public Builder setCount(E element, int count) { + contents.setCount(checkNotNull(element), count); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableMultiset}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterable elements) { + if (elements instanceof Multiset) { + Multiset multiset = Multisets.cast(elements); + multiset.forEachEntry((e, n) -> contents.add(checkNotNull(e), n)); + } else { + super.addAll(elements); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableMultiset}. + * + * @param elements the elements to add to the {@code ImmutableMultiset} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + /** + * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code + * Builder}. + */ + @Override + public ImmutableMultiset build() { + return copyOf(contents); + } + + @VisibleForTesting + ImmutableMultiset buildJdkBacked() { + if (contents.isEmpty()) { + return of(); + } + return JdkBackedImmutableMultiset.create(contents.entrySet()); + } + } + + static final class ElementSet extends ImmutableSet.Indexed { + private final List> entries; + // TODO(cpovirk): @Weak? + private final Multiset delegate; + + ElementSet(List> entries, Multiset delegate) { + this.entries = entries; + this.delegate = delegate; + } + + @Override + E get(int index) { + return entries.get(index).getElement(); + } + + @Override + public boolean contains(Object object) { + return delegate.contains(object); + } + + @Override + boolean isPartialView() { + return true; + } + + @Override + public int size() { + return entries.size(); + } + } + + @J2ktIncompatible + static final class SerializedForm implements Serializable { + final Object[] elements; + final int[] counts; + + // "extends Object" works around https://github.com/typetools/checker-framework/issues/3013 + SerializedForm(Multiset multiset) { + int distinct = multiset.entrySet().size(); + elements = new Object[distinct]; + counts = new int[distinct]; + int i = 0; + for (Entry entry : multiset.entrySet()) { + elements[i] = entry.getElement(); + counts[i] = entry.getCount(); + i++; + } + } + + Object readResolve() { + LinkedHashMultiset multiset = LinkedHashMultiset.create(elements.length); + for (int i = 0; i < elements.length; i++) { + multiset.add(elements[i], counts[i]); + } + return ImmutableMultiset.copyOf(multiset); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java new file mode 100644 index 0000000000000..87e5360d1b806 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableMultisetGwtSerializationDependencies.java @@ -0,0 +1,44 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * A dummy superclass to support GWT serialization of the element type of an {@link + * ImmutableMultiset}. The GWT supersource for this class contains a field of type {@code E}. + * + *

For details about this hack, see {@code GwtSerializationDependencies}, which takes the same + * approach but with a subclass rather than a superclass. + * + *

TODO(cpovirk): Consider applying this subclass approach to our other types. + * + *

For {@code ImmutableMultiset} in particular, I ran into a problem with the {@code + * GwtSerializationDependencies} approach: When autogenerating a serializer for the new class, GWT + * tries to refer to our dummy serializer for the superclass, + * ImmutableMultiset_CustomFieldSerializer. But that type has no methods (since it's never actually + * used). We could probably fix the problem by adding dummy methods to that class, but that is + * starting to sound harder than taking the superclass approach, which I've been coming to like, + * anyway, since it doesn't require us to declare dummy methods (though occasionally constructors) + * and make types non-final. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ImmutableMultisetGwtSerializationDependencies extends ImmutableCollection {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableRangeMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableRangeMap.java new file mode 100644 index 0000000000000..474c55b786ea9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableRangeMap.java @@ -0,0 +1,436 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.SortedLists.KeyAbsentBehavior; +import org.apache.iotdb.google.common.collect.SortedLists.KeyPresentBehavior; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link RangeMap} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + * @author Louis Wasserman + * @since 14.0 + */ +@GwtIncompatible // NavigableMap +@ElementTypesAreNonnullByDefault +public class ImmutableRangeMap, V> implements RangeMap, Serializable { + + private static final ImmutableRangeMap, Object> EMPTY = + new ImmutableRangeMap<>(ImmutableList.>>of(), ImmutableList.of()); + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableRangeMap}. As in {@link Builder}, overlapping ranges are not permitted. + * + * @since 23.1 + */ + public static , V> + Collector> toImmutableRangeMap( + Function> keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableRangeMap(keyFunction, valueFunction); + } + + /** + * Returns an empty immutable range map. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableRangeMap of() { + return (ImmutableRangeMap) EMPTY; + } + + /** Returns an immutable range map mapping a single range to a single value. */ + public static , V> ImmutableRangeMap of(Range range, V value) { + return new ImmutableRangeMap<>(ImmutableList.of(range), ImmutableList.of(value)); + } + + @SuppressWarnings("unchecked") + public static , V> ImmutableRangeMap copyOf( + RangeMap rangeMap) { + if (rangeMap instanceof ImmutableRangeMap) { + return (ImmutableRangeMap) rangeMap; + } + Map, ? extends V> map = rangeMap.asMapOfRanges(); + ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder<>(map.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(map.size()); + for (Entry, ? extends V> entry : map.entrySet()) { + rangesBuilder.add(entry.getKey()); + valuesBuilder.add(entry.getValue()); + } + return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build()); + } + + /** Returns a new builder for an immutable range map. */ + public static , V> Builder builder() { + return new Builder<>(); + } + + /** + * A builder for immutable range maps. Overlapping ranges are prohibited. + * + * @since 14.0 + */ + public static final class Builder, V> { + private final List, V>> entries; + + public Builder() { + this.entries = Lists.newArrayList(); + } + + /** + * Associates the specified range with the specified value. + * + * @throws IllegalArgumentException if {@code range} is empty + */ + public Builder put(Range range, V value) { + checkNotNull(range); + checkNotNull(value); + checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); + entries.add(Maps.immutableEntry(range, value)); + return this; + } + + /** Copies all associations from the specified range map into this builder. */ + public Builder putAll(RangeMap rangeMap) { + for (Entry, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { + put(entry.getKey(), entry.getValue()); + } + return this; + } + + Builder combine(Builder builder) { + entries.addAll(builder.entries); + return this; + } + + /** + * Returns an {@code ImmutableRangeMap} containing the associations previously added to this + * builder. + * + * @throws IllegalArgumentException if any two ranges inserted into this builder overlap + */ + public ImmutableRangeMap build() { + Collections.sort(entries, Range.rangeLexOrdering().onKeys()); + ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder<>(entries.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(entries.size()); + for (int i = 0; i < entries.size(); i++) { + Range range = entries.get(i).getKey(); + if (i > 0) { + Range prevRange = entries.get(i - 1).getKey(); + if (range.isConnected(prevRange) && !range.intersection(prevRange).isEmpty()) { + throw new IllegalArgumentException( + "Overlapping ranges: range " + prevRange + " overlaps with entry " + range); + } + } + rangesBuilder.add(range); + valuesBuilder.add(entries.get(i).getValue()); + } + return new ImmutableRangeMap<>(rangesBuilder.build(), valuesBuilder.build()); + } + } + + private final transient ImmutableList> ranges; + private final transient ImmutableList values; + + ImmutableRangeMap(ImmutableList> ranges, ImmutableList values) { + this.ranges = ranges; + this.values = values; + } + + @Override + public V get(K key) { + int index = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + Cut.belowValue(key), + KeyPresentBehavior.ANY_PRESENT, + KeyAbsentBehavior.NEXT_LOWER); + if (index == -1) { + return null; + } else { + Range range = ranges.get(index); + return range.contains(key) ? values.get(index) : null; + } + } + + @Override + public Entry, V> getEntry(K key) { + int index = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + Cut.belowValue(key), + KeyPresentBehavior.ANY_PRESENT, + KeyAbsentBehavior.NEXT_LOWER); + if (index == -1) { + return null; + } else { + Range range = ranges.get(index); + return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null; + } + } + + @Override + public Range span() { + if (ranges.isEmpty()) { + throw new NoSuchElementException(); + } + Range firstRange = ranges.get(0); + Range lastRange = ranges.get(ranges.size() - 1); + return Range.create(firstRange.lowerBound, lastRange.upperBound); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void put(Range range, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void putCoalescing(Range range, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void putAll(RangeMap rangeMap) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void remove(Range range) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeMap} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void merge( + Range range, V value, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public ImmutableMap, V> asMapOfRanges() { + if (ranges.isEmpty()) { + return ImmutableMap.of(); + } + RegularImmutableSortedSet> rangeSet = + new RegularImmutableSortedSet<>(ranges, Range.rangeLexOrdering()); + return new ImmutableSortedMap<>(rangeSet, values); + } + + @Override + public ImmutableMap, V> asDescendingMapOfRanges() { + if (ranges.isEmpty()) { + return ImmutableMap.of(); + } + RegularImmutableSortedSet> rangeSet = + new RegularImmutableSortedSet<>(ranges.reverse(), Range.rangeLexOrdering().reverse()); + return new ImmutableSortedMap<>(rangeSet, values.reverse()); + } + + @Override + public ImmutableRangeMap subRangeMap(final Range range) { + if (checkNotNull(range).isEmpty()) { + return ImmutableRangeMap.of(); + } else if (ranges.isEmpty() || range.encloses(span())) { + return this; + } + int lowerIndex = + SortedLists.binarySearch( + ranges, + Range.upperBoundFn(), + range.lowerBound, + KeyPresentBehavior.FIRST_AFTER, + KeyAbsentBehavior.NEXT_HIGHER); + int upperIndex = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + range.upperBound, + KeyPresentBehavior.ANY_PRESENT, + KeyAbsentBehavior.NEXT_HIGHER); + if (lowerIndex >= upperIndex) { + return ImmutableRangeMap.of(); + } + final int off = lowerIndex; + final int len = upperIndex - lowerIndex; + ImmutableList> subRanges = + new ImmutableList>() { + @Override + public int size() { + return len; + } + + @Override + public Range get(int index) { + checkElementIndex(index, len); + if (index == 0 || index == len - 1) { + return ranges.get(index + off).intersection(range); + } else { + return ranges.get(index + off); + } + } + + @Override + boolean isPartialView() { + return true; + } + }; + final ImmutableRangeMap outer = this; + return new ImmutableRangeMap(subRanges, values.subList(lowerIndex, upperIndex)) { + @Override + public ImmutableRangeMap subRangeMap(Range subRange) { + if (range.isConnected(subRange)) { + return outer.subRangeMap(subRange.intersection(range)); + } else { + return ImmutableRangeMap.of(); + } + } + }; + } + + @Override + public int hashCode() { + return asMapOfRanges().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap) o; + return asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } + return false; + } + + @Override + public String toString() { + return asMapOfRanges().toString(); + } + + /** + * This class is used to serialize ImmutableRangeMap instances. Serializes the {@link + * #asMapOfRanges()} form. + */ + private static class SerializedForm, V> implements Serializable { + + private final ImmutableMap, V> mapOfRanges; + + SerializedForm(ImmutableMap, V> mapOfRanges) { + this.mapOfRanges = mapOfRanges; + } + + Object readResolve() { + if (mapOfRanges.isEmpty()) { + return of(); + } else { + return createRangeMap(); + } + } + + Object createRangeMap() { + Builder builder = new Builder<>(); + for (Entry, V> entry : mapOfRanges.entrySet()) { + builder.put(entry.getKey(), entry.getValue()); + } + return builder.build(); + } + + private static final long serialVersionUID = 0; + } + + Object writeReplace() { + return new SerializedForm<>(asMapOfRanges()); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableRangeSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableRangeSet.java new file mode 100644 index 0000000000000..faf942a76ddc3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableRangeSet.java @@ -0,0 +1,847 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.SortedLists.KeyAbsentBehavior; +import org.apache.iotdb.google.common.collect.SortedLists.KeyPresentBehavior; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.stream.Collector; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER; +import static org.apache.iotdb.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; +import static org.apache.iotdb.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; + +/** + * A {@link RangeSet} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + * @author Louis Wasserman + * @since 14.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class ImmutableRangeSet extends AbstractRangeSet + implements Serializable { + + private static final ImmutableRangeSet> EMPTY = + new ImmutableRangeSet<>(ImmutableList.>>of()); + + private static final ImmutableRangeSet> ALL = + new ImmutableRangeSet<>(ImmutableList.of(Range.>all())); + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableRangeSet}. As in {@link Builder}, overlapping ranges are not permitted and adjacent + * ranges will be merged. + * + * @since 23.1 + */ + public static > + Collector, ?, ImmutableRangeSet> toImmutableRangeSet() { + return CollectCollectors.toImmutableRangeSet(); + } + + /** + * Returns an empty immutable range set. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") + public static ImmutableRangeSet of() { + return (ImmutableRangeSet) EMPTY; + } + + /** + * Returns an immutable range set containing the specified single range. If {@link Range#isEmpty() + * range.isEmpty()}, this is equivalent to {@link ImmutableRangeSet#of()}. + */ + public static ImmutableRangeSet of(Range range) { + checkNotNull(range); + if (range.isEmpty()) { + return of(); + } else if (range.equals(Range.all())) { + return all(); + } else { + return new ImmutableRangeSet(ImmutableList.of(range)); + } + } + + /** Returns an immutable range set containing the single range {@link Range#all()}. */ + @SuppressWarnings("unchecked") + static ImmutableRangeSet all() { + return (ImmutableRangeSet) ALL; + } + + /** Returns an immutable copy of the specified {@code RangeSet}. */ + public static ImmutableRangeSet copyOf(RangeSet rangeSet) { + checkNotNull(rangeSet); + if (rangeSet.isEmpty()) { + return of(); + } else if (rangeSet.encloses(Range.all())) { + return all(); + } + + if (rangeSet instanceof ImmutableRangeSet) { + ImmutableRangeSet immutableRangeSet = (ImmutableRangeSet) rangeSet; + if (!immutableRangeSet.isPartialView()) { + return immutableRangeSet; + } + } + return new ImmutableRangeSet(ImmutableList.copyOf(rangeSet.asRanges())); + } + + /** + * Returns an {@code ImmutableRangeSet} containing each of the specified disjoint ranges. + * Overlapping ranges and empty ranges are forbidden, though adjacent ranges are permitted and + * will be merged. + * + * @throws IllegalArgumentException if any ranges overlap or are empty + * @since 21.0 + */ + public static > ImmutableRangeSet copyOf(Iterable> ranges) { + return new ImmutableRangeSet.Builder().addAll(ranges).build(); + } + + /** + * Returns an {@code ImmutableRangeSet} representing the union of the specified ranges. + * + *

This is the smallest {@code RangeSet} which encloses each of the specified ranges. Duplicate + * or connected ranges are permitted, and will be coalesced in the result. + * + * @since 21.0 + */ + public static > ImmutableRangeSet unionOf(Iterable> ranges) { + return copyOf(TreeRangeSet.create(ranges)); + } + + ImmutableRangeSet(ImmutableList> ranges) { + this.ranges = ranges; + } + + private ImmutableRangeSet(ImmutableList> ranges, ImmutableRangeSet complement) { + this.ranges = ranges; + this.complement = complement; + } + + private final transient ImmutableList> ranges; + + @Override + public boolean intersects(Range otherRange) { + int ceilingIndex = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + otherRange.lowerBound, + Ordering.natural(), + ANY_PRESENT, + NEXT_HIGHER); + if (ceilingIndex < ranges.size() + && ranges.get(ceilingIndex).isConnected(otherRange) + && !ranges.get(ceilingIndex).intersection(otherRange).isEmpty()) { + return true; + } + return ceilingIndex > 0 + && ranges.get(ceilingIndex - 1).isConnected(otherRange) + && !ranges.get(ceilingIndex - 1).intersection(otherRange).isEmpty(); + } + + @Override + public boolean encloses(Range otherRange) { + int index = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + otherRange.lowerBound, + Ordering.natural(), + ANY_PRESENT, + NEXT_LOWER); + return index != -1 && ranges.get(index).encloses(otherRange); + } + + @Override + public Range rangeContaining(C value) { + int index = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + Cut.belowValue(value), + Ordering.natural(), + ANY_PRESENT, + NEXT_LOWER); + if (index != -1) { + Range range = ranges.get(index); + return range.contains(value) ? range : null; + } + return null; + } + + @Override + public Range span() { + if (ranges.isEmpty()) { + throw new NoSuchElementException(); + } + return Range.create(ranges.get(0).lowerBound, ranges.get(ranges.size() - 1).upperBound); + } + + @Override + public boolean isEmpty() { + return ranges.isEmpty(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void add(Range range) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void addAll(RangeSet other) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void addAll(Iterable> other) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void removeAll(RangeSet other) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public void removeAll(Iterable> other) { + throw new UnsupportedOperationException(); + } + + @Override + public ImmutableSet> asRanges() { + if (ranges.isEmpty()) { + return ImmutableSet.of(); + } + return new RegularImmutableSortedSet<>(ranges, Range.rangeLexOrdering()); + } + + @Override + public ImmutableSet> asDescendingSetOfRanges() { + if (ranges.isEmpty()) { + return ImmutableSet.of(); + } + return new RegularImmutableSortedSet<>(ranges.reverse(), Range.rangeLexOrdering().reverse()); + } + + private transient ImmutableRangeSet complement; + + private final class ComplementRanges extends ImmutableList> { + // True if the "positive" range set is empty or bounded below. + private final boolean positiveBoundedBelow; + + // True if the "positive" range set is empty or bounded above. + private final boolean positiveBoundedAbove; + + private final int size; + + ComplementRanges() { + this.positiveBoundedBelow = ranges.get(0).hasLowerBound(); + this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound(); + + int size = ranges.size() - 1; + if (positiveBoundedBelow) { + size++; + } + if (positiveBoundedAbove) { + size++; + } + this.size = size; + } + + @Override + public int size() { + return size; + } + + @Override + public Range get(int index) { + checkElementIndex(index, size); + + Cut lowerBound; + if (positiveBoundedBelow) { + lowerBound = (index == 0) ? Cut.belowAll() : ranges.get(index - 1).upperBound; + } else { + lowerBound = ranges.get(index).upperBound; + } + + Cut upperBound; + if (positiveBoundedAbove && index == size - 1) { + upperBound = Cut.aboveAll(); + } else { + upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound; + } + + return Range.create(lowerBound, upperBound); + } + + @Override + boolean isPartialView() { + return true; + } + } + + @Override + public ImmutableRangeSet complement() { + ImmutableRangeSet result = complement; + if (result != null) { + return result; + } else if (ranges.isEmpty()) { + return complement = all(); + } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) { + return complement = of(); + } else { + ImmutableList> complementRanges = new ComplementRanges(); + result = complement = new ImmutableRangeSet(complementRanges, this); + } + return result; + } + + /** + * Returns a new range set consisting of the union of this range set and {@code other}. + * + *

This is essentially the same as {@code TreeRangeSet.create(this).addAll(other)} except it + * returns an {@code ImmutableRangeSet}. + * + * @since 21.0 + */ + public ImmutableRangeSet union(RangeSet other) { + return unionOf(Iterables.concat(asRanges(), other.asRanges())); + } + + /** + * Returns a new range set consisting of the intersection of this range set and {@code other}. + * + *

This is essentially the same as {@code + * TreeRangeSet.create(this).removeAll(other.complement())} except it returns an {@code + * ImmutableRangeSet}. + * + * @since 21.0 + */ + public ImmutableRangeSet intersection(RangeSet other) { + RangeSet copy = TreeRangeSet.create(this); + copy.removeAll(other.complement()); + return copyOf(copy); + } + + /** + * Returns a new range set consisting of the difference of this range set and {@code other}. + * + *

This is essentially the same as {@code TreeRangeSet.create(this).removeAll(other)} except it + * returns an {@code ImmutableRangeSet}. + * + * @since 21.0 + */ + public ImmutableRangeSet difference(RangeSet other) { + RangeSet copy = TreeRangeSet.create(this); + copy.removeAll(other); + return copyOf(copy); + } + + /** + * Returns a list containing the nonempty intersections of {@code range} with the ranges in this + * range set. + */ + private ImmutableList> intersectRanges(final Range range) { + if (ranges.isEmpty() || range.isEmpty()) { + return ImmutableList.of(); + } else if (range.encloses(span())) { + return ranges; + } + + final int fromIndex; + if (range.hasLowerBound()) { + fromIndex = + SortedLists.binarySearch( + ranges, + Range.upperBoundFn(), + range.lowerBound, + KeyPresentBehavior.FIRST_AFTER, + KeyAbsentBehavior.NEXT_HIGHER); + } else { + fromIndex = 0; + } + + int toIndex; + if (range.hasUpperBound()) { + toIndex = + SortedLists.binarySearch( + ranges, + Range.lowerBoundFn(), + range.upperBound, + KeyPresentBehavior.FIRST_PRESENT, + KeyAbsentBehavior.NEXT_HIGHER); + } else { + toIndex = ranges.size(); + } + final int length = toIndex - fromIndex; + if (length == 0) { + return ImmutableList.of(); + } else { + return new ImmutableList>() { + @Override + public int size() { + return length; + } + + @Override + public Range get(int index) { + checkElementIndex(index, length); + if (index == 0 || index == length - 1) { + return ranges.get(index + fromIndex).intersection(range); + } else { + return ranges.get(index + fromIndex); + } + } + + @Override + boolean isPartialView() { + return true; + } + }; + } + } + + /** Returns a view of the intersection of this range set with the given range. */ + @Override + public ImmutableRangeSet subRangeSet(Range range) { + if (!isEmpty()) { + Range span = span(); + if (range.encloses(span)) { + return this; + } else if (range.isConnected(span)) { + return new ImmutableRangeSet(intersectRanges(range)); + } + } + return of(); + } + + /** + * Returns an {@link ImmutableSortedSet} containing the same values in the given domain + * {@linkplain RangeSet#contains contained} by this range set. + * + *

Note: {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For + * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty + * ranges {@code [3..3)} and {@code [4..4)}. + * + *

Warning: Be extremely careful what you do with the {@code asSet} view of a large + * range set (such as {@code ImmutableRangeSet.of(Range.greaterThan(0))}). Certain operations on + * such a set can be performed efficiently, but others (such as {@link Set#hashCode} or {@link + * Collections#frequency}) can cause major performance problems. + * + *

The returned set's {@link Object#toString} method returns a shorthand form of the set's + * contents, such as {@code "[1..100]}"}. + * + * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if + * neither has an upper bound + */ + public ImmutableSortedSet asSet(DiscreteDomain domain) { + checkNotNull(domain); + if (isEmpty()) { + return ImmutableSortedSet.of(); + } + Range span = span().canonical(domain); + if (!span.hasLowerBound()) { + // according to the spec of canonical, neither this ImmutableRangeSet nor + // the range have a lower bound + throw new IllegalArgumentException( + "Neither the DiscreteDomain nor this range set are bounded below"); + } else if (!span.hasUpperBound()) { + try { + domain.maxValue(); + } catch (NoSuchElementException e) { + throw new IllegalArgumentException( + "Neither the DiscreteDomain nor this range set are bounded above"); + } + } + + return new AsSet(domain); + } + + private final class AsSet extends ImmutableSortedSet { + private final DiscreteDomain domain; + + AsSet(DiscreteDomain domain) { + super(Ordering.natural()); + this.domain = domain; + } + + private transient Integer size; + + @Override + public int size() { + // racy single-check idiom + Integer result = size; + if (result == null) { + long total = 0; + for (Range range : ranges) { + total += ContiguousSet.create(range, domain).size(); + if (total >= Integer.MAX_VALUE) { + break; + } + } + result = size = Ints.saturatedCast(total); + } + return result.intValue(); + } + + @Override + public UnmodifiableIterator iterator() { + return new AbstractIterator() { + final Iterator> rangeItr = ranges.iterator(); + Iterator elemItr = Iterators.emptyIterator(); + + @Override + protected C computeNext() { + while (!elemItr.hasNext()) { + if (rangeItr.hasNext()) { + elemItr = ContiguousSet.create(rangeItr.next(), domain).iterator(); + } else { + return endOfData(); + } + } + return elemItr.next(); + } + }; + } + + @Override + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return new AbstractIterator() { + final Iterator> rangeItr = ranges.reverse().iterator(); + Iterator elemItr = Iterators.emptyIterator(); + + @Override + protected C computeNext() { + while (!elemItr.hasNext()) { + if (rangeItr.hasNext()) { + elemItr = ContiguousSet.create(rangeItr.next(), domain).descendingIterator(); + } else { + return endOfData(); + } + } + return elemItr.next(); + } + }; + } + + ImmutableSortedSet subSet(Range range) { + return subRangeSet(range).asSet(domain); + } + + @Override + ImmutableSortedSet headSetImpl(C toElement, boolean inclusive) { + return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + } + + @Override + ImmutableSortedSet subSetImpl( + C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) { + return ImmutableSortedSet.of(); + } + return subSet( + Range.range( + fromElement, BoundType.forBoolean(fromInclusive), + toElement, BoundType.forBoolean(toInclusive))); + } + + @Override + ImmutableSortedSet tailSetImpl(C fromElement, boolean inclusive) { + return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + } + + @Override + public boolean contains(Object o) { + if (o == null) { + return false; + } + try { + @SuppressWarnings("unchecked") // we catch CCE's + C c = (C) o; + return ImmutableRangeSet.this.contains(c); + } catch (ClassCastException e) { + return false; + } + } + + @Override + int indexOf(Object target) { + if (contains(target)) { + @SuppressWarnings("unchecked") // if it's contained, it's definitely a C + C c = (C) requireNonNull(target); + long total = 0; + for (Range range : ranges) { + if (range.contains(c)) { + return Ints.saturatedCast(total + ContiguousSet.create(range, domain).indexOf(c)); + } else { + total += ContiguousSet.create(range, domain).size(); + } + } + throw new AssertionError("impossible"); + } + return -1; + } + + @Override + ImmutableSortedSet createDescendingSet() { + return new DescendingImmutableSortedSet(this); + } + + @Override + boolean isPartialView() { + return ranges.isPartialView(); + } + + @Override + public String toString() { + return ranges.toString(); + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new AsSetSerializedForm(ranges, domain); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + } + + private static class AsSetSerializedForm implements Serializable { + private final ImmutableList> ranges; + private final DiscreteDomain domain; + + AsSetSerializedForm(ImmutableList> ranges, DiscreteDomain domain) { + this.ranges = ranges; + this.domain = domain; + } + + Object readResolve() { + return new ImmutableRangeSet(ranges).asSet(domain); + } + } + + /** + * Returns {@code true} if this immutable range set's implementation contains references to + * user-created objects that aren't accessible via this range set's methods. This is generally + * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid + * memory leaks. + */ + boolean isPartialView() { + return ranges.isPartialView(); + } + + /** Returns a new builder for an immutable range set. */ + public static > Builder builder() { + return new Builder(); + } + + /** + * A builder for immutable range sets. + * + * @since 14.0 + */ + public static class Builder> { + private final List> ranges; + + public Builder() { + this.ranges = Lists.newArrayList(); + } + + // TODO(lowasser): consider adding union, in addition to add, that does allow overlap + + /** + * Add the specified range to this builder. Adjacent ranges are permitted and will be merged, + * but overlapping ranges will cause an exception when {@link #build()} is called. + * + * @throws IllegalArgumentException if {@code range} is empty + */ + public Builder add(Range range) { + checkArgument(!range.isEmpty(), "range must not be empty, but was %s", range); + ranges.add(range); + return this; + } + + /** + * Add all ranges from the specified range set to this builder. Adjacent ranges are permitted + * and will be merged, but overlapping ranges will cause an exception when {@link #build()} is + * called. + */ + public Builder addAll(RangeSet ranges) { + return addAll(ranges.asRanges()); + } + + /** + * Add all of the specified ranges to this builder. Adjacent ranges are permitted and will be + * merged, but overlapping ranges will cause an exception when {@link #build()} is called. + * + * @throws IllegalArgumentException if any inserted ranges are empty + * @since 21.0 + */ + public Builder addAll(Iterable> ranges) { + for (Range range : ranges) { + add(range); + } + return this; + } + + Builder combine(Builder builder) { + addAll(builder.ranges); + return this; + } + + /** + * Returns an {@code ImmutableRangeSet} containing the ranges added to this builder. + * + * @throws IllegalArgumentException if any input ranges have nonempty overlap + */ + public ImmutableRangeSet build() { + ImmutableList.Builder> mergedRangesBuilder = + new ImmutableList.Builder<>(ranges.size()); + Collections.sort(ranges, Range.rangeLexOrdering()); + PeekingIterator> peekingItr = Iterators.peekingIterator(ranges.iterator()); + while (peekingItr.hasNext()) { + Range range = peekingItr.next(); + while (peekingItr.hasNext()) { + Range nextRange = peekingItr.peek(); + if (range.isConnected(nextRange)) { + checkArgument( + range.intersection(nextRange).isEmpty(), + "Overlapping ranges not permitted but found %s overlapping %s", + range, + nextRange); + range = range.span(peekingItr.next()); + } else { + break; + } + } + mergedRangesBuilder.add(range); + } + ImmutableList> mergedRanges = mergedRangesBuilder.build(); + if (mergedRanges.isEmpty()) { + return of(); + } else if (mergedRanges.size() == 1 + && Iterables.getOnlyElement(mergedRanges).equals(Range.all())) { + return all(); + } else { + return new ImmutableRangeSet(mergedRanges); + } + } + } + + private static final class SerializedForm implements Serializable { + private final ImmutableList> ranges; + + SerializedForm(ImmutableList> ranges) { + this.ranges = ranges; + } + + Object readResolve() { + if (ranges.isEmpty()) { + return of(); + } else if (ranges.equals(ImmutableList.of(Range.all()))) { + return all(); + } else { + return new ImmutableRangeSet(ranges); + } + } + } + + @J2ktIncompatible // java.io.ObjectInputStream + Object writeReplace() { + return new SerializedForm(ranges); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSet.java new file mode 100644 index 0000000000000..53c6bb94e4ff2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSet.java @@ -0,0 +1,986 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.stream.Collector; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * A {@link Set} whose contents will never change, with many other important properties detailed at + * {@link ImmutableCollection}. + * + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +public abstract class ImmutableSet extends ImmutableCollection implements Set { + static final int SPLITERATOR_CHARACTERISTICS = + ImmutableCollection.SPLITERATOR_CHARACTERISTICS | Spliterator.DISTINCT; + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableSet}. Elements appear in the resulting set in the encounter order of the stream; if + * the stream contains duplicates (according to {@link Object#equals(Object)}), only the first + * duplicate in encounter order will appear in the result. + * + * @since 21.0 + */ + public static Collector> toImmutableSet() { + return CollectCollectors.toImmutableSet(); + } + + /** + * Returns the empty immutable set. Preferred over {@link Collections#emptySet} for code + * consistency, and because the return type conveys the immutability guarantee. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings({"unchecked"}) // fully variant implementation (never actually produces any Es) + public static ImmutableSet of() { + return (ImmutableSet) RegularImmutableSet.EMPTY; + } + + /** + * Returns an immutable set containing {@code element}. Preferred over {@link + * Collections#singleton} for code consistency, {@code null} rejection, and because the return + * type conveys the immutability guarantee. + */ + public static ImmutableSet of(E element) { + return new SingletonImmutableSet(element); + } + + /** + * Returns an immutable set containing the given elements, minus duplicates, in the order each was + * first specified. That is, if multiple elements are {@linkplain Object#equals equal}, all except + * the first are ignored. + */ + public static ImmutableSet of(E e1, E e2) { + return construct(2, 2, e1, e2); + } + + /** + * Returns an immutable set containing the given elements, minus duplicates, in the order each was + * first specified. That is, if multiple elements are {@linkplain Object#equals equal}, all except + * the first are ignored. + */ + public static ImmutableSet of(E e1, E e2, E e3) { + return construct(3, 3, e1, e2, e3); + } + + /** + * Returns an immutable set containing the given elements, minus duplicates, in the order each was + * first specified. That is, if multiple elements are {@linkplain Object#equals equal}, all except + * the first are ignored. + */ + public static ImmutableSet of(E e1, E e2, E e3, E e4) { + return construct(4, 4, e1, e2, e3, e4); + } + + /** + * Returns an immutable set containing the given elements, minus duplicates, in the order each was + * first specified. That is, if multiple elements are {@linkplain Object#equals equal}, all except + * the first are ignored. + */ + public static ImmutableSet of(E e1, E e2, E e3, E e4, E e5) { + return construct(5, 5, e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable set containing the given elements, minus duplicates, in the order each was + * first specified. That is, if multiple elements are {@linkplain Object#equals equal}, all except + * the first are ignored. + * + *

The array {@code others} must not be longer than {@code Integer.MAX_VALUE - 6}. + * + * @since 3.0 (source-compatible since 2.0) + */ + @SafeVarargs // For Eclipse. For internal javac we have disabled this pointless type of warning. + public static ImmutableSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { + checkArgument( + others.length <= Integer.MAX_VALUE - 6, "the total number of elements must fit in an int"); + final int paramCount = 6; + Object[] elements = new Object[paramCount + others.length]; + elements[0] = e1; + elements[1] = e2; + elements[2] = e3; + elements[3] = e4; + elements[4] = e5; + elements[5] = e6; + System.arraycopy(others, 0, elements, paramCount, others.length); + return construct(elements.length, elements.length, elements); + } + + /** + * Constructs an {@code ImmutableSet} from the first {@code n} elements of the specified array, + * which we have no particular reason to believe does or does not contain duplicates. If {@code k} + * is the size of the returned {@code ImmutableSet}, then the unique elements of {@code elements} + * will be in the first {@code k} positions, and {@code elements[i] == null} for {@code k <= i < + * n}. + * + *

This may modify {@code elements}. Additionally, if {@code n == elements.length} and {@code + * elements} contains no duplicates, {@code elements} may be used without copying in the returned + * {@code ImmutableSet}, in which case the caller must not modify it. + * + *

{@code elements} may contain only values of type {@code E}. + * + * @throws NullPointerException if any of the first {@code n} elements of {@code elements} is null + */ + private static ImmutableSet constructUnknownDuplication(int n, Object... elements) { + // Guess the size is "halfway between" all duplicates and no duplicates, on a log scale. + return construct( + n, + Math.max( + ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY, + IntMath.sqrt(n, RoundingMode.CEILING)), + elements); + } + + /** + * Constructs an {@code ImmutableSet} from the first {@code n} elements of the specified array. If + * {@code k} is the size of the returned {@code ImmutableSet}, then the unique elements of {@code + * elements} will be in the first {@code k} positions, and {@code elements[i] == null} for {@code + * k <= i < n}. + * + *

This may modify {@code elements}. Additionally, if {@code n == elements.length} and {@code + * elements} contains no duplicates, {@code elements} may be used without copying in the returned + * {@code ImmutableSet}, in which case it may no longer be modified. + * + *

{@code elements} may contain only values of type {@code E}. + * + * @throws NullPointerException if any of the first {@code n} elements of {@code elements} is null + */ + private static ImmutableSet construct(int n, int expectedSize, Object... elements) { + switch (n) { + case 0: + return of(); + case 1: + @SuppressWarnings("unchecked") // safe; elements contains only E's + E elem = (E) elements[0]; + return of(elem); + default: + SetBuilderImpl builder = new RegularSetBuilderImpl(expectedSize); + for (int i = 0; i < n; i++) { + @SuppressWarnings("unchecked") + E e = (E) checkNotNull(elements[i]); + builder = builder.add(e); + } + return builder.review().build(); + } + } + + /** + * Returns an immutable set containing each of {@code elements}, minus duplicates, in the order + * each appears first in the source collection. + * + *

Performance note: This method will sometimes recognize that the actual copy operation + * is unnecessary; for example, {@code copyOf(copyOf(anArrayList))} will copy the data only once. + * This reduces the expense of habitually making defensive copies at API boundaries. However, the + * precise conditions for skipping the copy operation are undefined. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 7.0 (source-compatible since 2.0) + */ + public static ImmutableSet copyOf(Collection elements) { + /* + * TODO(lowasser): consider checking for ImmutableAsList here + * TODO(lowasser): consider checking for Multiset here + */ + // Don't refer to ImmutableSortedSet by name so it won't pull in all that code + if (elements instanceof ImmutableSet && !(elements instanceof SortedSet)) { + @SuppressWarnings("unchecked") // all supported methods are covariant + ImmutableSet set = (ImmutableSet) elements; + if (!set.isPartialView()) { + return set; + } + } else if (elements instanceof EnumSet) { + return copyOfEnumSet((EnumSet) elements); + } + Object[] array = elements.toArray(); + if (elements instanceof Set) { + // assume probably no duplicates (though it might be using different equality semantics) + return construct(array.length, array.length, array); + } else { + return constructUnknownDuplication(array.length, array); + } + } + + /** + * Returns an immutable set containing each of {@code elements}, minus duplicates, in the order + * each appears first in the source iterable. This method iterates over {@code elements} only + * once. + * + *

Performance note: This method will sometimes recognize that the actual copy operation + * is unnecessary; for example, {@code copyOf(copyOf(anArrayList))} should copy the data only + * once. This reduces the expense of habitually making defensive copies at API boundaries. + * However, the precise conditions for skipping the copy operation are undefined. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSet copyOf(Iterable elements) { + return (elements instanceof Collection) + ? copyOf((Collection) elements) + : copyOf(elements.iterator()); + } + + /** + * Returns an immutable set containing each of {@code elements}, minus duplicates, in the order + * each appears first in the source iterator. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSet copyOf(Iterator elements) { + // We special-case for 0 or 1 elements, but anything further is madness. + if (!elements.hasNext()) { + return of(); + } + E first = elements.next(); + if (!elements.hasNext()) { + return of(first); + } else { + return new ImmutableSet.Builder().add(first).addAll(elements).build(); + } + } + + /** + * Returns an immutable set containing each of {@code elements}, minus duplicates, in the order + * each appears first in the source array. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 3.0 + */ + public static ImmutableSet copyOf(E[] elements) { + switch (elements.length) { + case 0: + return of(); + case 1: + return of(elements[0]); + default: + return constructUnknownDuplication(elements.length, elements.clone()); + } + } + + @SuppressWarnings("rawtypes") // necessary to compile against Java 8 + private static ImmutableSet copyOfEnumSet(EnumSet enumSet) { + return ImmutableEnumSet.asImmutable(EnumSet.copyOf(enumSet)); + } + + ImmutableSet() {} + + /** Returns {@code true} if the {@code hashCode()} method runs quickly. */ + boolean isHashCodeFast() { + return false; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ImmutableSet + && isHashCodeFast() + && ((ImmutableSet) object).isHashCodeFast() + && hashCode() != object.hashCode()) { + return false; + } + return Sets.equalsImpl(this, object); + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + // This declaration is needed to make Set.iterator() and + // ImmutableCollection.iterator() consistent. + @Override + public abstract UnmodifiableIterator iterator(); + + @GwtCompatible + abstract static class CachingAsList extends ImmutableSet { + private transient ImmutableList asList; + + @Override + public ImmutableList asList() { + ImmutableList result = asList; + if (result == null) { + return asList = createAsList(); + } else { + return result; + } + } + + ImmutableList createAsList() { + return new RegularImmutableAsList(this, toArray()); + } + } + + abstract static class Indexed extends CachingAsList { + abstract E get(int index); + + @Override + public UnmodifiableIterator iterator() { + return asList().iterator(); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.indexed(size(), SPLITERATOR_CHARACTERISTICS, this::get); + } + + @Override + public void forEach(Consumer consumer) { + checkNotNull(consumer); + int n = size(); + for (int i = 0; i < n; i++) { + consumer.accept(get(i)); + } + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + return asList().copyIntoArray(dst, offset); + } + + @Override + ImmutableList createAsList() { + return new ImmutableAsList() { + @Override + public E get(int index) { + return Indexed.this.get(index); + } + + @Override + Indexed delegateCollection() { + return Indexed.this; + } + }; + } + } + + /* + * This class is used to serialize all ImmutableSet instances, except for + * ImmutableEnumSet/ImmutableSortedSet, regardless of implementation type. It + * captures their "logical contents" and they are reconstructed using public + * static factories. This is necessary to ensure that the existence of a + * particular implementation type is an implementation detail. + */ + @J2ktIncompatible // serialization + private static class SerializedForm implements Serializable { + final Object[] elements; + + SerializedForm(Object[] elements) { + this.elements = elements; + } + + Object readResolve() { + return copyOf(elements); + } + + private static final long serialVersionUID = 0; + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm(toArray()); + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a new builder, expecting the specified number of distinct elements to be added. + * + *

If {@code expectedSize} is exactly the number of distinct elements added to the builder + * before {@link Builder#build} is called, the builder is likely to perform better than an unsized + * {@link #builder()} would have. + * + *

It is not specified if any performance benefits apply if {@code expectedSize} is close to, + * but not exactly, the number of distinct elements added to the builder. + * + * @since 23.1 + */ + public static Builder builderWithExpectedSize(int expectedSize) { + checkNonnegative(expectedSize, "expectedSize"); + return new Builder(expectedSize); + } + + /** + * A builder for creating {@code ImmutableSet} instances. Example: + * + *

{@code
+   * static final ImmutableSet GOOGLE_COLORS =
+   *     ImmutableSet.builder()
+   *         .addAll(WEBSAFE_COLORS)
+   *         .add(new Color(0, 191, 255))
+   *         .build();
+   * }
+ * + *

Elements appear in the resulting set in the same order they were first added to the builder. + * + *

Building does not change the state of the builder, so it is still possible to add more + * elements and to build again. + * + * @since 2.0 + */ + public static class Builder extends ImmutableCollection.Builder { + /* + * `impl` is null only for instances of the subclass, ImmutableSortedSet.Builder. That subclass + * overrides all the methods that access it here. Thus, all the methods here can safely assume + * that this field is non-null. + */ + private SetBuilderImpl impl; + boolean forceCopy; + + public Builder() { + this(0); + } + + Builder(int capacity) { + if (capacity > 0) { + impl = new RegularSetBuilderImpl(capacity); + } else { + impl = EmptySetBuilderImpl.instance(); + } + } + + Builder(@SuppressWarnings("unused") boolean subclass) { + this.impl = null; // unused + } + + @VisibleForTesting + void forceJdk() { + requireNonNull(impl); // see the comment on the field + this.impl = new JdkBackedSetBuilderImpl(impl); + } + + final void copyIfNecessary() { + if (forceCopy) { + copy(); + forceCopy = false; + } + } + + void copy() { + requireNonNull(impl); // see the comment on the field + impl = impl.copy(); + } + + @Override + public Builder add(E element) { + requireNonNull(impl); // see the comment on the field + checkNotNull(element); + copyIfNecessary(); + impl = impl.add(element); + return this; + } + + @Override + public Builder add(E... elements) { + super.add(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSet}, ignoring duplicate + * elements (only the first duplicate element is added). + * + * @param elements the elements to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + @Override + public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + Builder combine(Builder other) { + requireNonNull(impl); + requireNonNull(other.impl); + /* + * For discussion of requireNonNull, see the comment on the field. + * + * (And I don't believe there's any situation in which we call x.combine(y) when x is a plain + * ImmutableSet.Builder but y is an ImmutableSortedSet.Builder (or vice versa). Certainly + * ImmutableSortedSet.Builder.combine() is written as if its argument will never be a plain + * ImmutableSet.Builder: It casts immediately to ImmutableSortedSet.Builder.) + */ + copyIfNecessary(); + this.impl = this.impl.combine(other.impl); + return this; + } + + @Override + public ImmutableSet build() { + requireNonNull(impl); // see the comment on the field + forceCopy = true; + impl = impl.review(); + return impl.build(); + } + } + + /** Swappable internal implementation of an ImmutableSet.Builder. */ + private abstract static class SetBuilderImpl { + // The first `distinct` elements are non-null. + // Since we can never access null elements, we don't mark this nullable. + E[] dedupedElements; + int distinct; + + @SuppressWarnings("unchecked") + SetBuilderImpl(int expectedCapacity) { + this.dedupedElements = (E[]) new Object[expectedCapacity]; + this.distinct = 0; + } + + /** Initializes this SetBuilderImpl with a copy of the deduped elements array from toCopy. */ + SetBuilderImpl(SetBuilderImpl toCopy) { + this.dedupedElements = Arrays.copyOf(toCopy.dedupedElements, toCopy.dedupedElements.length); + this.distinct = toCopy.distinct; + } + + /** + * Resizes internal data structures if necessary to store the specified number of distinct + * elements. + */ + private void ensureCapacity(int minCapacity) { + if (minCapacity > dedupedElements.length) { + int newCapacity = + ImmutableCollection.Builder.expandedCapacity(dedupedElements.length, minCapacity); + dedupedElements = Arrays.copyOf(dedupedElements, newCapacity); + } + } + + /** Adds e to the insertion-order array of deduplicated elements. Calls ensureCapacity. */ + final void addDedupedElement(E e) { + ensureCapacity(distinct + 1); + dedupedElements[distinct++] = e; + } + + /** + * Adds e to this SetBuilderImpl, returning the updated result. Only use the returned + * SetBuilderImpl, since we may switch implementations if e.g. hash flooding is detected. + */ + abstract SetBuilderImpl add(E e); + + /** Adds all the elements from the specified SetBuilderImpl to this SetBuilderImpl. */ + final SetBuilderImpl combine(SetBuilderImpl other) { + SetBuilderImpl result = this; + for (int i = 0; i < other.distinct; i++) { + /* + * requireNonNull is safe because we ensure that the first `distinct` elements have been + * populated. + */ + result = result.add(requireNonNull(other.dedupedElements[i])); + } + return result; + } + + /** + * Creates a new copy of this SetBuilderImpl. Modifications to that SetBuilderImpl will not + * affect this SetBuilderImpl or sets constructed from this SetBuilderImpl via build(). + */ + abstract SetBuilderImpl copy(); + + /** + * Call this before build(). Does a final check on the internal data structures, e.g. shrinking + * unnecessarily large structures or detecting previously unnoticed hash flooding. + */ + SetBuilderImpl review() { + return this; + } + + abstract ImmutableSet build(); + } + + private static final class EmptySetBuilderImpl extends SetBuilderImpl { + private static final EmptySetBuilderImpl INSTANCE = new EmptySetBuilderImpl<>(); + + @SuppressWarnings("unchecked") + static SetBuilderImpl instance() { + return (SetBuilderImpl) INSTANCE; + } + + private EmptySetBuilderImpl() { + super(0); + } + + @Override + SetBuilderImpl add(E e) { + return new RegularSetBuilderImpl(Builder.DEFAULT_INITIAL_CAPACITY).add(e); + } + + @Override + SetBuilderImpl copy() { + return this; + } + + @Override + ImmutableSet build() { + return ImmutableSet.of(); + } + } + + // We use power-of-2 tables, and this is the highest int that's a power of 2 + static final int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; + + // Represents how tightly we can pack things, as a maximum. + private static final double DESIRED_LOAD_FACTOR = 0.7; + + // If the set has this many elements, it will "max out" the table size + private static final int CUTOFF = (int) (MAX_TABLE_SIZE * DESIRED_LOAD_FACTOR); + + /** + * Returns an array size suitable for the backing array of a hash table that uses open addressing + * with linear probing in its implementation. The returned size is the smallest power of two that + * can hold setSize elements with the desired load factor. Always returns at least setSize + 2. + */ + // TODO(cpovirk): Move to Hashing or something, since it's used elsewhere in the Android version. + static int chooseTableSize(int setSize) { + setSize = Math.max(setSize, 2); + // Correct the size for open addressing to match desired load factor. + if (setSize < CUTOFF) { + // Round up to the next highest power of 2. + int tableSize = Integer.highestOneBit(setSize - 1) << 1; + while (tableSize * DESIRED_LOAD_FACTOR < setSize) { + tableSize <<= 1; + } + return tableSize; + } + + // The table can't be completely full or we'll get infinite reprobes + checkArgument(setSize < MAX_TABLE_SIZE, "collection too large"); + return MAX_TABLE_SIZE; + } + + /** + * Default implementation of the guts of ImmutableSet.Builder, creating an open-addressed hash + * table and deduplicating elements as they come, so it only allocates O(max(distinct, + * expectedCapacity)) rather than O(calls to add). + * + *

This implementation attempts to detect hash flooding, and if it's identified, falls back to + * JdkBackedSetBuilderImpl. + */ + private static final class RegularSetBuilderImpl extends SetBuilderImpl { + // null until at least two elements are present + private Object[] hashTable; + private int maxRunBeforeFallback; + private int expandTableThreshold; + private int hashCode; + + RegularSetBuilderImpl(int expectedCapacity) { + super(expectedCapacity); + this.hashTable = null; + this.maxRunBeforeFallback = 0; + this.expandTableThreshold = 0; + } + + RegularSetBuilderImpl(RegularSetBuilderImpl toCopy) { + super(toCopy); + this.hashTable = (toCopy.hashTable == null) ? null : toCopy.hashTable.clone(); + this.maxRunBeforeFallback = toCopy.maxRunBeforeFallback; + this.expandTableThreshold = toCopy.expandTableThreshold; + this.hashCode = toCopy.hashCode; + } + + @Override + SetBuilderImpl add(E e) { + checkNotNull(e); + if (hashTable == null) { + if (distinct == 0) { + addDedupedElement(e); + return this; + } else { + ensureTableCapacity(dedupedElements.length); + E elem = dedupedElements[0]; + distinct--; + return insertInHashTable(elem).add(e); + } + } + return insertInHashTable(e); + } + + private SetBuilderImpl insertInHashTable(E e) { + requireNonNull(hashTable); + int eHash = e.hashCode(); + int i0 = Hashing.smear(eHash); + int mask = hashTable.length - 1; + for (int i = i0; i - i0 < maxRunBeforeFallback; i++) { + int index = i & mask; + Object tableEntry = hashTable[index]; + if (tableEntry == null) { + addDedupedElement(e); + hashTable[index] = e; + hashCode += eHash; + ensureTableCapacity(distinct); // rebuilds table if necessary + return this; + } else if (tableEntry.equals(e)) { // not a new element, ignore + return this; + } + } + // we fell out of the loop due to a long run; fall back to JDK impl + return new JdkBackedSetBuilderImpl(this).add(e); + } + + @Override + SetBuilderImpl copy() { + return new RegularSetBuilderImpl(this); + } + + @Override + SetBuilderImpl review() { + if (hashTable == null) { + return this; + } + int targetTableSize = chooseTableSize(distinct); + if (targetTableSize * 2 < hashTable.length) { + hashTable = rebuildHashTable(targetTableSize, dedupedElements, distinct); + maxRunBeforeFallback = maxRunBeforeFallback(targetTableSize); + expandTableThreshold = (int) (DESIRED_LOAD_FACTOR * targetTableSize); + } + return hashFloodingDetected(hashTable) ? new JdkBackedSetBuilderImpl(this) : this; + } + + @Override + ImmutableSet build() { + switch (distinct) { + case 0: + return of(); + case 1: + /* + * requireNonNull is safe because we ensure that the first `distinct` elements have been + * populated. + */ + return of(requireNonNull(dedupedElements[0])); + default: + /* + * The suppression is safe because we ensure that the first `distinct` elements have been + * populated. + */ + @SuppressWarnings("nullness") + Object[] elements = + (distinct == dedupedElements.length) + ? dedupedElements + : Arrays.copyOf(dedupedElements, distinct); + return new RegularImmutableSet( + elements, hashCode, requireNonNull(hashTable), hashTable.length - 1); + } + } + + /** Builds a new open-addressed hash table from the first n objects in elements. */ + static Object[] rebuildHashTable(int newTableSize, Object[] elements, int n) { + Object[] hashTable = new Object[newTableSize]; + int mask = hashTable.length - 1; + for (int i = 0; i < n; i++) { + // requireNonNull is safe because we ensure that the first n elements have been populated. + Object e = requireNonNull(elements[i]); + int j0 = Hashing.smear(e.hashCode()); + for (int j = j0; ; j++) { + int index = j & mask; + if (hashTable[index] == null) { + hashTable[index] = e; + break; + } + } + } + return hashTable; + } + + void ensureTableCapacity(int minCapacity) { + int newTableSize; + if (hashTable == null) { + newTableSize = chooseTableSize(minCapacity); + hashTable = new Object[newTableSize]; + } else if (minCapacity > expandTableThreshold && hashTable.length < MAX_TABLE_SIZE) { + newTableSize = hashTable.length * 2; + hashTable = rebuildHashTable(newTableSize, dedupedElements, distinct); + } else { + return; + } + maxRunBeforeFallback = maxRunBeforeFallback(newTableSize); + expandTableThreshold = (int) (DESIRED_LOAD_FACTOR * newTableSize); + } + + /** + * We attempt to detect deliberate hash flooding attempts. If one is detected, we fall back to a + * wrapper around j.u.HashSet, which has built-in flooding protection. MAX_RUN_MULTIPLIER was + * determined experimentally to match our desired probability of false positives. + */ + // NB: yes, this is surprisingly high, but that's what the experiments said was necessary + // Raising this number slows the worst-case contains behavior, speeds up hashFloodingDetected, + // and reduces the false-positive probability. + static final int MAX_RUN_MULTIPLIER = 13; + + /** + * Checks the whole hash table for poor hash distribution. Takes O(n) in the worst case, O(n / + * log n) on average. + * + *

The online hash flooding detecting in RegularSetBuilderImpl.add can detect e.g. many + * exactly matching hash codes, which would cause construction to take O(n^2), but can't detect + * e.g. hash codes adversarially designed to go into ascending table locations, which keeps + * construction O(n) (as desired) but then can have O(n) queries later. + * + *

If this returns false, then no query can take more than O(log n). + * + *

Note that for a RegularImmutableSet with elements with truly random hash codes, contains + * operations take expected O(1) time but with high probability take O(log n) for at least some + * element. (https://en.wikipedia.org/wiki/Linear_probing#Analysis) + * + *

This method may return {@code true} even on truly random input, but {@code + * ImmutableSetTest} tests that the probability of that is low. + */ + static boolean hashFloodingDetected(Object[] hashTable) { + int maxRunBeforeFallback = maxRunBeforeFallback(hashTable.length); + int mask = hashTable.length - 1; + + // Invariant: all elements at indices in [knownRunStart, knownRunEnd) are nonnull. + // If knownRunStart == knownRunEnd, this is vacuously true. + // When knownRunEnd exceeds hashTable.length, it "wraps", detecting runs around the end + // of the table. + int knownRunStart = 0; + int knownRunEnd = 0; + + outerLoop: + while (knownRunStart < hashTable.length) { + if (knownRunStart == knownRunEnd && hashTable[knownRunStart] == null) { + if (hashTable[(knownRunStart + maxRunBeforeFallback - 1) & mask] == null) { + // There are only maxRunBeforeFallback - 1 elements between here and there, + // so even if they were all nonnull, we wouldn't detect a hash flood. Therefore, + // we can skip them all. + knownRunStart += maxRunBeforeFallback; + } else { + knownRunStart++; // the only case in which maxRunEnd doesn't increase by mRBF + // happens about f * (1-f) for f = DESIRED_LOAD_FACTOR, so around 21% of the time + } + knownRunEnd = knownRunStart; + } else { + for (int j = knownRunStart + maxRunBeforeFallback - 1; j >= knownRunEnd; j--) { + if (hashTable[j & mask] == null) { + knownRunEnd = knownRunStart + maxRunBeforeFallback; + knownRunStart = j + 1; + continue outerLoop; + } + } + return true; + } + } + return false; + } + + /** + * If more than this many consecutive positions are filled in a table of the specified size, + * report probable hash flooding. ({@link #hashFloodingDetected} may also report hash flooding + * if fewer consecutive positions are filled; see that method for details.) + */ + static int maxRunBeforeFallback(int tableSize) { + return MAX_RUN_MULTIPLIER * IntMath.log2(tableSize, RoundingMode.UNNECESSARY); + } + } + + /** + * SetBuilderImpl version that uses a JDK HashSet, which has built in hash flooding protection. + */ + private static final class JdkBackedSetBuilderImpl extends SetBuilderImpl { + private final Set delegate; + + JdkBackedSetBuilderImpl(SetBuilderImpl toCopy) { + super(toCopy); // initializes dedupedElements and distinct + delegate = Sets.newHashSetWithExpectedSize(distinct); + for (int i = 0; i < distinct; i++) { + /* + * requireNonNull is safe because we ensure that the first `distinct` elements have been + * populated. + */ + delegate.add(requireNonNull(dedupedElements[i])); + } + } + + @Override + SetBuilderImpl add(E e) { + checkNotNull(e); + if (delegate.add(e)) { + addDedupedElement(e); + } + return this; + } + + @Override + SetBuilderImpl copy() { + return new JdkBackedSetBuilderImpl<>(this); + } + + @Override + ImmutableSet build() { + switch (distinct) { + case 0: + return of(); + case 1: + /* + * requireNonNull is safe because we ensure that the first `distinct` elements have been + * populated. + */ + return of(requireNonNull(dedupedElements[0])); + default: + return new JdkBackedImmutableSet( + delegate, ImmutableList.asImmutableList(dedupedElements, distinct)); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSetMultimap.java new file mode 100644 index 0000000000000..3408ceeadb7e2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSetMultimap.java @@ -0,0 +1,630 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.MoreObjects; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link SetMultimap} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

Warning: As in all {@link SetMultimap}s, do not modify either a key or a value + * of a {@code ImmutableSetMultimap} in a way that affects its {@link Object#equals} behavior. + * Undefined behavior and bugs will result. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Mike Ward + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public class ImmutableSetMultimap extends ImmutableMultimap + implements SetMultimap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap} + * whose keys and values are the result of applying the provided mapping functions to the input + * elements. + * + *

For streams with defined encounter order (as defined in the Ordering section of the {@link + * java.util.stream} Javadoc), that order is preserved, but entries are grouped by key. + * + *

Example: + * + *

{@code
+   * static final Multimap FIRST_LETTER_MULTIMAP =
+   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
+   *         .collect(toImmutableSetMultimap(str -> str.charAt(0), str -> str.substring(1)));
+   *
+   * // is equivalent to
+   *
+   * static final Multimap FIRST_LETTER_MULTIMAP =
+   *     new ImmutableSetMultimap.Builder()
+   *         .put('b', "anana")
+   *         .putAll('a', "pple", "sparagus")
+   *         .putAll('c', "arrot", "herry")
+   *         .build();
+   * }
+ * + * @since 21.0 + */ + public static + Collector> toImmutableSetMultimap( + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableSetMultimap(keyFunction, valueFunction); + } + + /** + * Returns a {@code Collector} accumulating entries into an {@code ImmutableSetMultimap}. Each + * input element is mapped to a key and a stream of values, each of which are put into the + * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the + * streams of values. + * + *

Example: + * + *

{@code
+   * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
+   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
+   *         .collect(
+   *             flatteningToImmutableSetMultimap(
+   *                  str -> str.charAt(0),
+   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
+   *
+   * // is equivalent to
+   *
+   * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
+   *     ImmutableSetMultimap.builder()
+   *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
+   *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
+   *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
+   *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
+   *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
+   *         .build();
+   *
+   * // after deduplication, the resulting multimap is equivalent to
+   *
+   * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
+   *     ImmutableSetMultimap.builder()
+   *         .putAll('b', Arrays.asList('a', 'n'))
+   *         .putAll('a', Arrays.asList('p', 'l', 'e', 's', 'a', 'r', 'g', 'u'))
+   *         .putAll('c', Arrays.asList('a', 'r', 'o', 't', 'h', 'e', 'y'))
+   *         .build();
+   * }
+   * }
+ * + * @since 21.0 + */ + public static + Collector> flatteningToImmutableSetMultimap( + Function keyFunction, + Function> valuesFunction) { + return CollectCollectors.flatteningToImmutableSetMultimap(keyFunction, valuesFunction); + } + + /** + * Returns the empty multimap. + * + *

Performance note: the instance returned is a singleton. + */ + // Casting is safe because the multimap will never hold any elements. + @SuppressWarnings("unchecked") + public static ImmutableSetMultimap of() { + return (ImmutableSetMultimap) EmptyImmutableSetMultimap.INSTANCE; + } + + /** Returns an immutable multimap containing a single entry. */ + public static ImmutableSetMultimap of(K k1, V v1) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of + * an entry (according to {@link Object#equals}) after the first are ignored. + */ + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of + * an entry (according to {@link Object#equals}) after the first are ignored. + */ + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of + * an entry (according to {@link Object#equals}) after the first are ignored. + */ + public static ImmutableSetMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + return builder.build(); + } + + /** + * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of + * an entry (according to {@link Object#equals}) after the first are ignored. + */ + public static ImmutableSetMultimap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + builder.put(k5, v5); + return builder.build(); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** Returns a new {@link Builder}. */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * A builder for creating immutable {@code SetMultimap} instances, especially {@code public static + * final} multimaps ("constant multimaps"). Example: + * + *

{@code
+   * static final Multimap STRING_TO_INTEGER_MULTIMAP =
+   *     new ImmutableSetMultimap.Builder()
+   *         .put("one", 1)
+   *         .putAll("several", 1, 2, 3)
+   *         .putAll("many", 1, 2, 3, 4, 5)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple multimaps in series. Each multimap contains the key-value mappings in the previously + * created multimaps. + * + * @since 2.0 + */ + public static final class Builder extends ImmutableMultimap.Builder { + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableSetMultimap#builder}. + */ + public Builder() { + super(); + } + + @Override + Collection newMutableValueCollection() { + return Platform.preservesInsertionOrderOnAddsSet(); + } + + /** Adds a key-value mapping to the built multimap if it is not already present. */ + @Override + public Builder put(K key, V value) { + super.put(key, value); + return this; + } + + /** + * Adds an entry to the built multimap if it is not already present. + * + * @since 11.0 + */ + @Override + public Builder put(Entry entry) { + super.put(entry); + return this; + } + + /** + * {@inheritDoc} + * + * @since 19.0 + */ + @Override + public Builder putAll(Iterable> entries) { + super.putAll(entries); + return this; + } + + @Override + public Builder putAll(K key, Iterable values) { + super.putAll(key, values); + return this; + } + + @Override + public Builder putAll(K key, V... values) { + return putAll(key, Arrays.asList(values)); + } + + @Override + public Builder putAll(Multimap multimap) { + for (Entry> entry : + multimap.asMap().entrySet()) { + putAll(entry.getKey(), entry.getValue()); + } + return this; + } + + @Override + Builder combine(ImmutableMultimap.Builder other) { + super.combine(other); + return this; + } + + /** + * {@inheritDoc} + * + * @since 8.0 + */ + @Override + public Builder orderKeysBy(Comparator keyComparator) { + super.orderKeysBy(keyComparator); + return this; + } + + /** + * Specifies the ordering of the generated multimap's values for each key. + * + *

If this method is called, the sets returned by the {@code get()} method of the generated + * multimap and its {@link Multimap#asMap()} view are {@link ImmutableSortedSet} instances. + * However, serialization does not preserve that property, though it does maintain the key and + * value ordering. + * + * @since 8.0 + */ + // TODO: Make serialization behavior consistent. + @Override + public Builder orderValuesBy(Comparator valueComparator) { + super.orderValuesBy(valueComparator); + return this; + } + + /** Returns a newly-created immutable set multimap. */ + @Override + public ImmutableSetMultimap build() { + Collection>> mapEntries = builderMap.entrySet(); + if (keyComparator != null) { + mapEntries = Ordering.from(keyComparator).onKeys().immutableSortedCopy(mapEntries); + } + return fromMapEntries(mapEntries, valueComparator); + } + } + + /** + * Returns an immutable set multimap containing the same mappings as {@code multimap}. The + * generated multimap's key and value orderings correspond to the iteration ordering of the {@code + * multimap.asMap()} view. Repeated occurrences of an entry in the multimap after the first are + * ignored. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code multimap} is null + */ + public static ImmutableSetMultimap copyOf( + Multimap multimap) { + return copyOf(multimap, null); + } + + private static ImmutableSetMultimap copyOf( + Multimap multimap, Comparator valueComparator) { + checkNotNull(multimap); // eager for GWT + if (multimap.isEmpty() && valueComparator == null) { + return of(); + } + + if (multimap instanceof ImmutableSetMultimap) { + @SuppressWarnings("unchecked") // safe since multimap is not writable + ImmutableSetMultimap kvMultimap = (ImmutableSetMultimap) multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + return fromMapEntries(multimap.asMap().entrySet(), valueComparator); + } + + /** + * Returns an immutable multimap containing the specified entries. The returned multimap iterates + * over keys in the order they were first encountered in the input, and the values for each key + * are iterated in the order they were encountered. If two values for the same key are {@linkplain + * Object#equals equal}, the first value encountered is used. + * + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + public static ImmutableSetMultimap copyOf( + Iterable> entries) { + return new Builder().putAll(entries).build(); + } + + /** Creates an ImmutableSetMultimap from an asMap.entrySet. */ + static ImmutableSetMultimap fromMapEntries( + Collection>> mapEntries, + Comparator valueComparator) { + if (mapEntries.isEmpty()) { + return of(); + } + ImmutableMap.Builder> builder = + new ImmutableMap.Builder<>(mapEntries.size()); + int size = 0; + + for (Entry> entry : mapEntries) { + K key = entry.getKey(); + Collection values = entry.getValue(); + ImmutableSet set = valueSet(valueComparator, values); + if (!set.isEmpty()) { + builder.put(key, set); + size += set.size(); + } + } + + return new ImmutableSetMultimap<>(builder.buildOrThrow(), size, valueComparator); + } + + /** + * Returned by get() when a missing key is provided. Also holds the comparator, if any, used for + * values. + */ + private final transient ImmutableSet emptySet; + + ImmutableSetMultimap( + ImmutableMap> map, int size, Comparator valueComparator) { + super(map, size); + this.emptySet = emptySet(valueComparator); + } + + // views + + /** + * Returns an immutable set of the values for the given key. If no mappings in the multimap have + * the provided key, an empty immutable set is returned. The values are in the same order as the + * parameters used to build this multimap. + */ + @Override + public ImmutableSet get(K key) { + // This cast is safe as its type is known in constructor. + ImmutableSet set = (ImmutableSet) map.get(key); + return MoreObjects.firstNonNull(set, emptySet); + } + + private transient ImmutableSetMultimap inverse; + + /** + * {@inheritDoc} + * + *

Because an inverse of a set multimap cannot contain multiple pairs with the same key and + * value, this method returns an {@code ImmutableSetMultimap} rather than the {@code + * ImmutableMultimap} specified in the {@code ImmutableMultimap} class. + */ + @Override + public ImmutableSetMultimap inverse() { + ImmutableSetMultimap result = inverse; + return (result == null) ? (inverse = invert()) : result; + } + + private ImmutableSetMultimap invert() { + Builder builder = builder(); + for (Entry entry : entries()) { + builder.put(entry.getValue(), entry.getKey()); + } + ImmutableSetMultimap invertedMultimap = builder.build(); + invertedMultimap.inverse = this; + return invertedMultimap; + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final ImmutableSet removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the multimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final ImmutableSet replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + private transient ImmutableSet> entries; + + /** + * Returns an immutable collection of all key-value pairs in the multimap. Its iterator traverses + * the values for the first key, the values for the second key, and so on. + */ + @Override + public ImmutableSet> entries() { + ImmutableSet> result = entries; + return result == null ? (entries = new EntrySet<>(this)) : result; + } + + private static final class EntrySet extends ImmutableSet> { + private final transient ImmutableSetMultimap multimap; + + EntrySet(ImmutableSetMultimap multimap) { + this.multimap = multimap; + } + + @Override + public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + return multimap.containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public UnmodifiableIterator> iterator() { + return multimap.entryIterator(); + } + + @Override + boolean isPartialView() { + return false; + } + } + + private static ImmutableSet valueSet( + Comparator valueComparator, Collection values) { + return (valueComparator == null) + ? ImmutableSet.copyOf(values) + : ImmutableSortedSet.copyOf(valueComparator, values); + } + + private static ImmutableSet emptySet(Comparator valueComparator) { + return (valueComparator == null) + ? ImmutableSet.of() + : ImmutableSortedSet.emptySet(valueComparator); + } + + private static ImmutableSet.Builder valuesBuilder(Comparator valueComparator) { + return (valueComparator == null) + ? new ImmutableSet.Builder() + : new ImmutableSortedSet.Builder(valueComparator); + } + + /** + * @serialData number of distinct keys, and then for each distinct key: the key, the number of + * values for that key, and the key's values + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(valueComparator()); + Serialization.writeMultimap(this, stream); + } + + Comparator valueComparator() { + return emptySet instanceof ImmutableSortedSet + ? ((ImmutableSortedSet) emptySet).comparator() + : null; + } + + @GwtIncompatible // java serialization + @J2ktIncompatible + private static final class SetFieldSettersHolder { + static final Serialization.FieldSetter EMPTY_SET_FIELD_SETTER = + Serialization.getFieldSetter(ImmutableSetMultimap.class, "emptySet"); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + // Serialization type safety is at the caller's mercy. + @SuppressWarnings("unchecked") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + Comparator valueComparator = (Comparator) stream.readObject(); + int keyCount = stream.readInt(); + if (keyCount < 0) { + throw new InvalidObjectException("Invalid key count " + keyCount); + } + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int tmpSize = 0; + + for (int i = 0; i < keyCount; i++) { + Object key = stream.readObject(); + int valueCount = stream.readInt(); + if (valueCount <= 0) { + throw new InvalidObjectException("Invalid value count " + valueCount); + } + + ImmutableSet.Builder valuesBuilder = valuesBuilder(valueComparator); + for (int j = 0; j < valueCount; j++) { + valuesBuilder.add(stream.readObject()); + } + ImmutableSet valueSet = valuesBuilder.build(); + if (valueSet.size() != valueCount) { + throw new InvalidObjectException("Duplicate key-value pairs exist for key " + key); + } + builder.put(key, valueSet); + tmpSize += valueCount; + } + + ImmutableMap> tmpMap; + try { + tmpMap = builder.buildOrThrow(); + } catch (IllegalArgumentException e) { + throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); + } + + FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); + FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); + SetFieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator)); + } + + @GwtIncompatible // not needed in emulated source. + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedAsList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedAsList.java new file mode 100644 index 0000000000000..7863788830f56 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedAsList.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Comparator; +import java.util.Spliterator; + +/** + * List returned by {@code ImmutableSortedSet.asList()} when the set isn't empty. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") +@ElementTypesAreNonnullByDefault +final class ImmutableSortedAsList extends RegularImmutableAsList + implements SortedIterable { + ImmutableSortedAsList(ImmutableSortedSet backingSet, ImmutableList backingList) { + super(backingSet, backingList); + } + + @Override + ImmutableSortedSet delegateCollection() { + return (ImmutableSortedSet) super.delegateCollection(); + } + + @Override + public Comparator comparator() { + return delegateCollection().comparator(); + } + + // Override indexOf() and lastIndexOf() to be O(log N) instead of O(N). + + @GwtIncompatible // ImmutableSortedSet.indexOf + // TODO(cpovirk): consider manual binary search under GWT to preserve O(log N) lookup + @Override + public int indexOf(Object target) { + int index = delegateCollection().indexOf(target); + + // TODO(kevinb): reconsider if it's really worth making feeble attempts at + // sanity for inconsistent comparators. + + // The equals() check is needed when the comparator isn't compatible with + // equals(). + return (index >= 0 && get(index).equals(target)) ? index : -1; + } + + @GwtIncompatible // ImmutableSortedSet.indexOf + @Override + public int lastIndexOf(Object target) { + return indexOf(target); + } + + @Override + public boolean contains(Object target) { + // Necessary for ISS's with comparators inconsistent with equals. + return indexOf(target) >= 0; + } + + @GwtIncompatible // super.subListUnchecked does not exist; inherited subList is valid if slow + /* + * TODO(cpovirk): if we start to override indexOf/lastIndexOf under GWT, we'll want some way to + * override subList to return an ImmutableSortedAsList for better performance. Right now, I'm not + * sure there's any performance hit from our failure to override subListUnchecked under GWT + */ + @Override + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + ImmutableList parentSubList = super.subListUnchecked(fromIndex, toIndex); + return new RegularImmutableSortedSet(parentSubList, comparator()).asList(); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.indexed( + size(), + ImmutableList.SPLITERATOR_CHARACTERISTICS | Spliterator.SORTED | Spliterator.DISTINCT, + delegateList()::get, + comparator()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMap.java new file mode 100644 index 0000000000000..a9e21afa86580 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMap.java @@ -0,0 +1,1146 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.Spliterator; +import java.util.TreeMap; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static org.apache.iotdb.google.common.collect.Maps.keyOrNull; + +/** + * A {@link NavigableMap} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

Warning: as with any sorted collection, you are strongly advised not to use a {@link + * Comparator} or {@link Comparable} type whose comparison behavior is inconsistent with + * equals. That is, {@code a.compareTo(b)} or {@code comparator.compare(a, b)} should equal zero + * if and only if {@code a.equals(b)}. If this advice is not followed, the resulting map will + * not correctly obey its specification. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (implements {@code NavigableMap} since 12.0) + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public final class ImmutableSortedMap extends ImmutableSortedMapFauxverideShim + implements NavigableMap { + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSortedMap} whose + * keys and values are the result of applying the provided mapping functions to the input + * elements. The generated map is sorted by the specified comparator. + * + *

If the mapped keys contain duplicates (according to the specified comparator), an {@code + * IllegalArgumentException} is thrown when the collection operation is performed. (This differs + * from the {@code Collector} returned by {@link Collectors#toMap(Function, Function)}, which + * throws an {@code IllegalStateException}.) + * + * @since 21.0 + */ + public static + Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction) { + return CollectCollectors.toImmutableSortedMap(comparator, keyFunction, valueFunction); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSortedMap} whose + * keys and values are the result of applying the provided mapping functions to the input + * elements. + * + *

If the mapped keys contain duplicates (according to the comparator), the values are merged + * using the specified merging function. Entries will appear in the encounter order of the first + * occurrence of the key. + * + * @since 21.0 + */ + public static + Collector> toImmutableSortedMap( + Comparator comparator, + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + return CollectCollectors.toImmutableSortedMap( + comparator, keyFunction, valueFunction, mergeFunction); + } + + /* + * TODO(kevinb): Confirm that ImmutableSortedMap is faster to construct and + * uses less memory than TreeMap; then say so in the class Javadoc. + */ + private static final Comparator NATURAL_ORDER = Ordering.natural(); + + private static final ImmutableSortedMap NATURAL_EMPTY_MAP = + new ImmutableSortedMap<>( + ImmutableSortedSet.emptySet(Ordering.natural()), ImmutableList.of()); + + static ImmutableSortedMap emptyMap(Comparator comparator) { + if (Ordering.natural().equals(comparator)) { + return of(); + } else { + return new ImmutableSortedMap<>( + ImmutableSortedSet.emptySet(comparator), ImmutableList.of()); + } + } + + /** + * Returns the empty sorted map. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") + // unsafe, comparator() returns a comparator on the specified type + // TODO(kevinb): evaluate whether or not of().comparator() should return null + public static ImmutableSortedMap of() { + return (ImmutableSortedMap) NATURAL_EMPTY_MAP; + } + + /** Returns an immutable map containing a single entry. */ + public static , V> ImmutableSortedMap of(K k1, V v1) { + return of(Ordering.natural(), k1, v1); + } + + /** Returns an immutable map containing a single entry. */ + private static ImmutableSortedMap of(Comparator comparator, K k1, V v1) { + return new ImmutableSortedMap<>( + new RegularImmutableSortedSet(ImmutableList.of(k1), checkNotNull(comparator)), + ImmutableList.of(v1)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if the two keys are equal according to their natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, V v1, K k2, V v2) { + return fromEntries(entryOf(k1, v1), entryOf(k2, v2)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3) { + return fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return fromEntries(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return fromEntries( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + * @since 31.0 + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { + return fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + * @since 31.0 + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + return fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + * @since 31.0 + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8) { + return fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + * @since 31.0 + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9) { + return fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8), + entryOf(k9, v9)); + } + + /** + * Returns an immutable sorted map containing the given entries, sorted by the natural ordering of + * their keys. + * + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + * @since 31.0 + */ + @SuppressWarnings("unchecked") + public static , V> ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9, + K k10, + V v10) { + return fromEntries( + entryOf(k1, v1), + entryOf(k2, v2), + entryOf(k3, v3), + entryOf(k4, v4), + entryOf(k5, v5), + entryOf(k6, v6), + entryOf(k7, v7), + entryOf(k8, v8), + entryOf(k9, v9), + entryOf(k10, v10)); + } + + /** + * Returns an immutable map containing the same entries as {@code map}, sorted by the natural + * ordering of the keys. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

This method is not type-safe, as it may be called on a map with keys that are not mutually + * comparable. + * + * @throws ClassCastException if the keys in {@code map} are not mutually comparable + * @throws NullPointerException if any key or value in {@code map} is null + * @throws IllegalArgumentException if any two keys are equal according to their natural ordering + */ + public static ImmutableSortedMap copyOf(Map map) { + // Hack around K not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) NATURAL_ORDER; + return copyOfInternal(map, naturalOrder); + } + + /** + * Returns an immutable map containing the same entries as {@code map}, with keys sorted by the + * provided comparator. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code map} is null + * @throws IllegalArgumentException if any two keys are equal according to the comparator + */ + public static ImmutableSortedMap copyOf( + Map map, Comparator comparator) { + return copyOfInternal(map, checkNotNull(comparator)); + } + + /** + * Returns an immutable map containing the given entries, with keys sorted by their natural + * ordering. + * + *

This method is not type-safe, as it may be called on a map with keys that are not mutually + * comparable. + * + * @throws NullPointerException if any key or value in {@code map} is null + * @throws IllegalArgumentException if any two keys are equal according to the comparator + * @since 19.0 + */ + public static ImmutableSortedMap copyOf( + Iterable> entries) { + // Hack around K not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) NATURAL_ORDER; + return copyOf(entries, naturalOrder); + } + + /** + * Returns an immutable map containing the given entries, with keys sorted by the provided + * comparator. + * + * @throws NullPointerException if any key or value in {@code map} is null + * @throws IllegalArgumentException if any two keys are equal according to the comparator + * @since 19.0 + */ + public static ImmutableSortedMap copyOf( + Iterable> entries, + Comparator comparator) { + return fromEntries(checkNotNull(comparator), false, entries); + } + + /** + * Returns an immutable map containing the same entries as the provided sorted map, with the same + * ordering. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + @SuppressWarnings("unchecked") + public static ImmutableSortedMap copyOfSorted(SortedMap map) { + Comparator comparator = map.comparator(); + if (comparator == null) { + // If map has a null comparator, the keys should have a natural ordering, + // even though K doesn't explicitly implement Comparable. + comparator = (Comparator) NATURAL_ORDER; + } + if (map instanceof ImmutableSortedMap) { + // TODO(kevinb): Prove that this cast is safe, even though + // Collections.unmodifiableSortedMap requires the same key type. + @SuppressWarnings("unchecked") + ImmutableSortedMap kvMap = (ImmutableSortedMap) map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } + return fromEntries(comparator, true, map.entrySet()); + } + + private static ImmutableSortedMap copyOfInternal( + Map map, Comparator comparator) { + boolean sameComparator = false; + if (map instanceof SortedMap) { + SortedMap sortedMap = (SortedMap) map; + Comparator comparator2 = sortedMap.comparator(); + sameComparator = + (comparator2 == null) ? comparator == NATURAL_ORDER : comparator.equals(comparator2); + } + + if (sameComparator && (map instanceof ImmutableSortedMap)) { + // TODO(kevinb): Prove that this cast is safe, even though + // Collections.unmodifiableSortedMap requires the same key type. + @SuppressWarnings("unchecked") + ImmutableSortedMap kvMap = (ImmutableSortedMap) map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } + return fromEntries(comparator, sameComparator, map.entrySet()); + } + + private static , V> ImmutableSortedMap fromEntries( + Entry... entries) { + return fromEntries(Ordering.natural(), false, entries, entries.length); + } + + /** + * Accepts a collection of possibly-null entries. If {@code sameComparator}, then it is assumed + * that they do not need to be sorted or checked for dupes. + */ + private static ImmutableSortedMap fromEntries( + Comparator comparator, + boolean sameComparator, + Iterable> entries) { + // "adding" type params to an array of a raw type should be safe as + // long as no one can ever cast that same array instance back to a + // raw type. + @SuppressWarnings("unchecked") + Entry[] entryArray = (Entry[]) Iterables.toArray(entries, EMPTY_ENTRY_ARRAY); + return fromEntries(comparator, sameComparator, entryArray, entryArray.length); + } + + private static ImmutableSortedMap fromEntries( + final Comparator comparator, + boolean sameComparator, + Entry[] entryArray, + int size) { + switch (size) { + case 0: + return emptyMap(comparator); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entryArray[0]); + return of(comparator, onlyEntry.getKey(), onlyEntry.getValue()); + default: + Object[] keys = new Object[size]; + Object[] values = new Object[size]; + if (sameComparator) { + // Need to check for nulls, but don't need to sort or validate. + for (int i = 0; i < size; i++) { + // requireNonNull is safe because the first `size` elements have been filled in. + Entry entry = requireNonNull(entryArray[i]); + Object key = entry.getKey(); + Object value = entry.getValue(); + checkEntryNotNull(key, value); + keys[i] = key; + values[i] = value; + } + } else { + // Need to sort and check for nulls and dupes. + // Inline the Comparator implementation rather than transforming with a Function + // to save code size. + Arrays.sort( + entryArray, + 0, + size, + (e1, e2) -> { + // requireNonNull is safe because the first `size` elements have been filled in. + requireNonNull(e1); + requireNonNull(e2); + return comparator.compare(e1.getKey(), e2.getKey()); + }); + // requireNonNull is safe because the first `size` elements have been filled in. + Entry firstEntry = requireNonNull(entryArray[0]); + K prevKey = firstEntry.getKey(); + keys[0] = prevKey; + values[0] = firstEntry.getValue(); + checkEntryNotNull(keys[0], values[0]); + for (int i = 1; i < size; i++) { + // requireNonNull is safe because the first `size` elements have been filled in. + Entry prevEntry = requireNonNull(entryArray[i - 1]); + Entry entry = requireNonNull(entryArray[i]); + K key = entry.getKey(); + V value = entry.getValue(); + checkEntryNotNull(key, value); + keys[i] = key; + values[i] = value; + checkNoConflict(comparator.compare(prevKey, key) != 0, "key", prevEntry, entry); + prevKey = key; + } + } + return new ImmutableSortedMap<>( + new RegularImmutableSortedSet(new RegularImmutableList(keys), comparator), + new RegularImmutableList(values)); + } + } + + /** + * Returns a builder that creates immutable sorted maps whose keys are ordered by their natural + * ordering. The sorted maps use {@link Ordering#natural()} as the comparator. + */ + public static , V> Builder naturalOrder() { + return new Builder<>(Ordering.natural()); + } + + /** + * Returns a builder that creates immutable sorted maps with an explicit comparator. If the + * comparator has a more general type than the map's keys, such as creating a {@code + * SortedMap} with a {@code Comparator}, use the {@link Builder} + * constructor instead. + * + * @throws NullPointerException if {@code comparator} is null + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder<>(comparator); + } + + /** + * Returns a builder that creates immutable sorted maps whose keys are ordered by the reverse of + * their natural ordering. + */ + public static , V> Builder reverseOrder() { + return new Builder<>(Ordering.natural().reverse()); + } + + /** + * A builder for creating immutable sorted map instances, especially {@code public static final} + * maps ("constant maps"). Example: + * + *

{@code
+   * static final ImmutableSortedMap INT_TO_WORD =
+   *     new ImmutableSortedMap.Builder(Ordering.natural())
+   *         .put(1, "one")
+   *         .put(2, "two")
+   *         .put(3, "three")
+   *         .buildOrThrow();
+   * }
+ * + *

For small immutable sorted maps, the {@code ImmutableSortedMap.of()} methods are even + * more convenient. + * + *

Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to + * build multiple maps in series. Each map is a superset of the maps created before it. + * + * @since 2.0 + */ + public static class Builder extends ImmutableMap.Builder { + private final Comparator comparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableSortedMap#orderedBy}. + */ + @SuppressWarnings("unchecked") + public Builder(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate keys, according to the + * comparator (which might be the keys' natural order), are not allowed, and will cause {@link + * #build} to fail. + */ + @Override + public Builder put(K key, V value) { + super.put(key, value); + return this; + } + + /** + * Adds the given {@code entry} to the map, making it immutable if necessary. Duplicate keys, + * according to the comparator (which might be the keys' natural order), are not allowed, and + * will cause {@link #build} to fail. + * + * @since 11.0 + */ + @Override + public Builder put(Entry entry) { + super.put(entry); + return this; + } + + /** + * Associates all of the given map's keys and values in the built map. Duplicate keys, according + * to the comparator (which might be the keys' natural order), are not allowed, and will cause + * {@link #build} to fail. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + @Override + public Builder putAll(Map map) { + super.putAll(map); + return this; + } + + /** + * Adds all the given entries to the built map. Duplicate keys, according to the comparator + * (which might be the keys' natural order), are not allowed, and will cause {@link #build} to + * fail. + * + * @throws NullPointerException if any key, value, or entry is null + * @since 19.0 + */ + @Override + public Builder putAll(Iterable> entries) { + super.putAll(entries); + return this; + } + + /** + * Throws an {@code UnsupportedOperationException}. + * + * @since 19.0 + * @deprecated Unsupported by ImmutableSortedMap.Builder. + */ + @Override + @Deprecated + public final Builder orderEntriesByValue(Comparator valueComparator) { + throw new UnsupportedOperationException("Not available on ImmutableSortedMap.Builder"); + } + + @Override + Builder combine(ImmutableMap.Builder other) { + super.combine(other); + return this; + } + + /** + * Returns a newly-created immutable sorted map. + * + *

Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method + * will throw an exception if there are duplicate keys. The {@code build()} method will soon be + * deprecated. + * + * @throws IllegalArgumentException if any two keys are equal according to the comparator (which + * might be the keys' natural order) + */ + @Override + public ImmutableSortedMap build() { + return buildOrThrow(); + } + + /** + * Returns a newly-created immutable sorted map, or throws an exception if any two keys are + * equal. + * + * @throws IllegalArgumentException if any two keys are equal according to the comparator (which + * might be the keys' natural order) + * @since 31.0 + */ + @Override + public ImmutableSortedMap buildOrThrow() { + switch (size) { + case 0: + return emptyMap(comparator); + case 1: + // requireNonNull is safe because the first `size` elements have been filled in. + Entry onlyEntry = requireNonNull(entries[0]); + return of(comparator, onlyEntry.getKey(), onlyEntry.getValue()); + default: + return fromEntries(comparator, false, entries, size); + } + } + + /** + * Throws UnsupportedOperationException. A future version may support this operation. Then the + * value for any given key will be the one that was last supplied in a {@code put} operation for + * that key. + * + * @throws UnsupportedOperationException always + * @since 31.1 + * @deprecated This method is not currently implemented, and may never be. + */ + @Deprecated + @Override + public final ImmutableSortedMap buildKeepingLast() { + // TODO(emcmanus): implement + throw new UnsupportedOperationException( + "ImmutableSortedMap.Builder does not yet implement buildKeepingLast()"); + } + } + + private final transient RegularImmutableSortedSet keySet; + private final transient ImmutableList valueList; + private transient ImmutableSortedMap descendingMap; + + ImmutableSortedMap(RegularImmutableSortedSet keySet, ImmutableList valueList) { + this(keySet, valueList, null); + } + + ImmutableSortedMap( + RegularImmutableSortedSet keySet, + ImmutableList valueList, + ImmutableSortedMap descendingMap) { + this.keySet = keySet; + this.valueList = valueList; + this.descendingMap = descendingMap; + } + + @Override + public int size() { + return valueList.size(); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + ImmutableList keyList = keySet.asList(); + for (int i = 0; i < size(); i++) { + action.accept(keyList.get(i), valueList.get(i)); + } + } + + @Override + public V get(Object key) { + int index = keySet.indexOf(key); + return (index == -1) ? null : valueList.get(index); + } + + @Override + boolean isPartialView() { + return keySet.isPartialView() || valueList.isPartialView(); + } + + /** Returns an immutable set of the mappings in this map, sorted by the key ordering. */ + @Override + public ImmutableSet> entrySet() { + return super.entrySet(); + } + + @Override + ImmutableSet> createEntrySet() { + class EntrySet extends ImmutableMapEntrySet { + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + public Spliterator> spliterator() { + return asList().spliterator(); + } + + @Override + public void forEach(Consumer> action) { + asList().forEach(action); + } + + @Override + ImmutableList> createAsList() { + return new ImmutableAsList>() { + @Override + public Entry get(int index) { + return new AbstractMap.SimpleImmutableEntry<>( + keySet.asList().get(index), valueList.get(index)); + } + + @Override + public Spliterator> spliterator() { + return CollectSpliterators.indexed( + size(), ImmutableSet.SPLITERATOR_CHARACTERISTICS, this::get); + } + + @Override + ImmutableCollection> delegateCollection() { + return EntrySet.this; + } + }; + } + + @Override + ImmutableMap map() { + return ImmutableSortedMap.this; + } + } + return isEmpty() ? ImmutableSet.>of() : new EntrySet(); + } + + /** Returns an immutable sorted set of the keys in this map. */ + @Override + public ImmutableSortedSet keySet() { + return keySet; + } + + @Override + ImmutableSet createKeySet() { + throw new AssertionError("should never be called"); + } + + /** + * Returns an immutable collection of the values in this map, sorted by the ordering of the + * corresponding keys. + */ + @Override + public ImmutableCollection values() { + return valueList; + } + + @Override + ImmutableCollection createValues() { + throw new AssertionError("should never be called"); + } + + /** + * Returns the comparator that orders the keys, which is {@link Ordering#natural()} when the + * natural ordering of the keys is used. Note that its behavior is not consistent with {@link + * TreeMap#comparator()}, which returns {@code null} to indicate natural ordering. + */ + @Override + public Comparator comparator() { + return keySet().comparator(); + } + + @Override + public K firstKey() { + return keySet().first(); + } + + @Override + public K lastKey() { + return keySet().last(); + } + + private ImmutableSortedMap getSubMap(int fromIndex, int toIndex) { + if (fromIndex == 0 && toIndex == size()) { + return this; + } else if (fromIndex == toIndex) { + return emptyMap(comparator()); + } else { + return new ImmutableSortedMap<>( + keySet.getSubSet(fromIndex, toIndex), valueList.subList(fromIndex, toIndex)); + } + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries whose keys are less + * than {@code toKey}. + * + *

The {@link SortedMap#headMap} documentation states that a submap of a submap throws an + * {@link IllegalArgumentException} if passed a {@code toKey} greater than an earlier {@code + * toKey}. However, this method doesn't throw an exception in that situation, but instead keeps + * the original {@code toKey}. + */ + @Override + public ImmutableSortedMap headMap(K toKey) { + return headMap(toKey, false); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries whose keys are less + * than (or equal to, if {@code inclusive}) {@code toKey}. + * + *

The {@link SortedMap#headMap} documentation states that a submap of a submap throws an + * {@link IllegalArgumentException} if passed a {@code toKey} greater than an earlier {@code + * toKey}. However, this method doesn't throw an exception in that situation, but instead keeps + * the original {@code toKey}. + * + * @since 12.0 + */ + @Override + public ImmutableSortedMap headMap(K toKey, boolean inclusive) { + return getSubMap(0, keySet.headIndex(checkNotNull(toKey), inclusive)); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries whose keys ranges + * from {@code fromKey}, inclusive, to {@code toKey}, exclusive. + * + *

The {@link SortedMap#subMap} documentation states that a submap of a submap throws an {@link + * IllegalArgumentException} if passed a {@code fromKey} less than an earlier {@code fromKey}. + * However, this method doesn't throw an exception in that situation, but instead keeps the + * original {@code fromKey}. Similarly, this method keeps the original {@code toKey}, instead of + * throwing an exception, if passed a {@code toKey} greater than an earlier {@code toKey}. + */ + @Override + public ImmutableSortedMap subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries whose keys ranges + * from {@code fromKey} to {@code toKey}, inclusive or exclusive as indicated by the boolean + * flags. + * + *

The {@link SortedMap#subMap} documentation states that a submap of a submap throws an {@link + * IllegalArgumentException} if passed a {@code fromKey} less than an earlier {@code fromKey}. + * However, this method doesn't throw an exception in that situation, but instead keeps the + * original {@code fromKey}. Similarly, this method keeps the original {@code toKey}, instead of + * throwing an exception, if passed a {@code toKey} greater than an earlier {@code toKey}. + * + * @since 12.0 + */ + @Override + public ImmutableSortedMap subMap( + K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + checkNotNull(fromKey); + checkNotNull(toKey); + checkArgument( + comparator().compare(fromKey, toKey) <= 0, + "expected fromKey <= toKey but %s > %s", + fromKey, + toKey); + return headMap(toKey, toInclusive).tailMap(fromKey, fromInclusive); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries whose keys are + * greater than or equals to {@code fromKey}. + * + *

The {@link SortedMap#tailMap} documentation states that a submap of a submap throws an + * {@link IllegalArgumentException} if passed a {@code fromKey} less than an earlier {@code + * fromKey}. However, this method doesn't throw an exception in that situation, but instead keeps + * the original {@code fromKey}. + */ + @Override + public ImmutableSortedMap tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + /** + * This method returns a {@code ImmutableSortedMap}, consisting of the entries whose keys are + * greater than (or equal to, if {@code inclusive}) {@code fromKey}. + * + *

The {@link SortedMap#tailMap} documentation states that a submap of a submap throws an + * {@link IllegalArgumentException} if passed a {@code fromKey} less than an earlier {@code + * fromKey}. However, this method doesn't throw an exception in that situation, but instead keeps + * the original {@code fromKey}. + * + * @since 12.0 + */ + @Override + public ImmutableSortedMap tailMap(K fromKey, boolean inclusive) { + return getSubMap(keySet.tailIndex(checkNotNull(fromKey), inclusive), size()); + } + + @Override + public Entry lowerEntry(K key) { + return headMap(key, false).lastEntry(); + } + + @Override + public K lowerKey(K key) { + return keyOrNull(lowerEntry(key)); + } + + @Override + public Entry floorEntry(K key) { + return headMap(key, true).lastEntry(); + } + + @Override + public K floorKey(K key) { + return keyOrNull(floorEntry(key)); + } + + @Override + public Entry ceilingEntry(K key) { + return tailMap(key, true).firstEntry(); + } + + @Override + public K ceilingKey(K key) { + return keyOrNull(ceilingEntry(key)); + } + + @Override + public Entry higherEntry(K key) { + return tailMap(key, false).firstEntry(); + } + + @Override + public K higherKey(K key) { + return keyOrNull(higherEntry(key)); + } + + @Override + public Entry firstEntry() { + return isEmpty() ? null : entrySet().asList().get(0); + } + + @Override + public Entry lastEntry() { + return isEmpty() ? null : entrySet().asList().get(size() - 1); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public ImmutableSortedMap descendingMap() { + // TODO(kevinb): The descendingMap is never actually cached at all. Either: + // + // - Cache it, and annotate the field with @LazyInit. + // - Simplify the code below, and consider eliminating the field (b/287198172), which is also + // set by one of the constructors. + ImmutableSortedMap result = descendingMap; + if (result == null) { + if (isEmpty()) { + return emptyMap(Ordering.from(comparator()).reverse()); + } else { + return new ImmutableSortedMap<>( + (RegularImmutableSortedSet) keySet.descendingSet(), valueList.reverse(), this); + } + } + return result; + } + + @Override + public ImmutableSortedSet navigableKeySet() { + return keySet; + } + + @Override + public ImmutableSortedSet descendingKeySet() { + return keySet.descendingSet(); + } + + /** + * Serialized type for all ImmutableSortedMap instances. It captures the logical contents and they + * are reconstructed using public factory methods. This ensures that the implementation types + * remain as implementation details. + */ + @J2ktIncompatible // serialization + private static class SerializedForm extends ImmutableMap.SerializedForm { + private final Comparator comparator; + + SerializedForm(ImmutableSortedMap sortedMap) { + super(sortedMap); + comparator = sortedMap.comparator(); + } + + @Override + Builder makeBuilder(int size) { + return new Builder<>(comparator); + } + + private static final long serialVersionUID = 0; + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm<>(this); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + // This class is never actually serialized directly, but we have to make the + // warning go away (and suppressing would suppress for all nested classes too) + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMapFauxverideShim.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMapFauxverideShim.java new file mode 100644 index 0000000000000..1eea5b3891b0f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMapFauxverideShim.java @@ -0,0 +1,309 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; + +/** + * "Overrides" the {@link ImmutableMap} static methods that lack {@link ImmutableSortedMap} + * equivalents with deprecated, exception-throwing versions. See {@link + * ImmutableSortedSetFauxverideShim} for details. + * + * @author Chris Povirk + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class ImmutableSortedMapFauxverideShim extends ImmutableMap { + /** + * Not supported. Use {@link ImmutableSortedMap#toImmutableSortedMap}, which offers better + * type-safety, instead. This method exists only to hide {@link ImmutableMap#toImmutableMap} from + * consumers of {@code ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#toImmutableSortedMap}. + */ + @Deprecated + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link ImmutableSortedMap#toImmutableSortedMap}, which offers better + * type-safety, instead. This method exists only to hide {@link ImmutableMap#toImmutableMap} from + * consumers of {@code ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#toImmutableSortedMap}. + */ + @Deprecated + public static Collector> toImmutableMap( + Function keyFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link ImmutableSortedMap#naturalOrder}, which offers better type-safety, + * instead. This method exists only to hide {@link ImmutableMap#builder} from consumers of {@code + * ImmutableSortedMap}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMap#naturalOrder}, which offers better type-safety. + */ + @Deprecated + public static ImmutableSortedMap.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported for ImmutableSortedMap. + * + * @throws UnsupportedOperationException always + * @deprecated Not supported for ImmutableSortedMap. + */ + @Deprecated + public static ImmutableSortedMap.Builder builderWithExpectedSize(int expectedSize) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain a non-{@code Comparable} + * key. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this dummy + * version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a key of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls to will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a map that may contain non-{@code Comparable} + * keys. Proper calls will resolve to the version in {@code ImmutableSortedMap}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass keys of type {@code Comparable} to use {@link + * ImmutableSortedMap#of(Comparable, Object, Comparable, Object, Comparable, Object, + * Comparable, Object, Comparable, Object)}. + */ + @Deprecated + public static ImmutableSortedMap of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9, + K k10, + V v10) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}. + * + * @deprecated Use {@code ImmutableSortedMap.copyOf(ImmutableMap.ofEntries(...))}. + */ + @Deprecated + public static ImmutableSortedMap ofEntries( + Entry... entries) { + throw new UnsupportedOperationException(); + } + + // No copyOf() fauxveride; see ImmutableSortedSetFauxverideShim. +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMultiset.java new file mode 100644 index 0000000000000..2a464e26daf55 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMultiset.java @@ -0,0 +1,598 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link SortedMultiset} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

Warning: as with any sorted collection, you are strongly advised not to use a {@link + * Comparator} or {@link Comparable} type whose comparison behavior is inconsistent with + * equals. That is, {@code a.compareTo(b)} or {@code comparator.compare(a, b)} should equal zero + * if and only if {@code a.equals(b)}. If this advice is not followed, the resulting + * collection will not correctly obey its specification. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Louis Wasserman + * @since 12.0 + */ +@GwtIncompatible // hasn't been tested yet +@ElementTypesAreNonnullByDefault +public abstract class ImmutableSortedMultiset extends ImmutableSortedMultisetFauxverideShim + implements SortedMultiset { + // TODO(lowasser): GWT compatibility + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableMultiset}. Elements are sorted by the specified comparator. + * + *

Warning: {@code comparator} should be consistent with {@code equals} as + * explained in the {@link Comparator} documentation. + * + * @since 21.0 + */ + public static Collector> toImmutableSortedMultiset( + Comparator comparator) { + return toImmutableSortedMultiset(comparator, Function.identity(), e -> 1); + } + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableSortedMultiset} + * whose elements are the result of applying {@code elementFunction} to the inputs, with counts + * equal to the result of applying {@code countFunction} to the inputs. + * + *

If the mapped elements contain duplicates (according to {@code comparator}), the first + * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of + * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element. + * + * @since 22.0 + */ + public static + Collector> toImmutableSortedMultiset( + Comparator comparator, + Function elementFunction, + ToIntFunction countFunction) { + checkNotNull(comparator); + checkNotNull(elementFunction); + checkNotNull(countFunction); + return Collector.of( + () -> TreeMultiset.create(comparator), + (multiset, t) -> + multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)), + (multiset1, multiset2) -> { + multiset1.addAll(multiset2); + return multiset1; + }, + (Multiset multiset) -> copyOfSortedEntries(comparator, multiset.entrySet())); + } + + /** + * Returns the empty immutable sorted multiset. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") + public static ImmutableSortedMultiset of() { + return (ImmutableSortedMultiset) RegularImmutableSortedMultiset.NATURAL_EMPTY_MULTISET; + } + + /** Returns an immutable sorted multiset containing a single element. */ + public static > ImmutableSortedMultiset of(E element) { + RegularImmutableSortedSet elementSet = + (RegularImmutableSortedSet) ImmutableSortedSet.of(element); + long[] cumulativeCounts = {0, 1}; + return new RegularImmutableSortedMultiset(elementSet, cumulativeCounts, 0, 1); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + * @throws NullPointerException if any element is null + */ + public static > ImmutableSortedMultiset of(E e1, E e2) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + * @throws NullPointerException if any element is null + */ + public static > ImmutableSortedMultiset of(E e1, E e2, E e3) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + * @throws NullPointerException if any element is null + */ + public static > ImmutableSortedMultiset of( + E e1, E e2, E e3, E e4) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + * @throws NullPointerException if any element is null + */ + public static > ImmutableSortedMultiset of( + E e1, E e2, E e3, E e4, E e5) { + return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4, e5)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + * @throws NullPointerException if any element is null + */ + public static > ImmutableSortedMultiset of( + E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + int size = remaining.length + 6; + List all = Lists.newArrayListWithCapacity(size); + Collections.addAll(all, e1, e2, e3, e4, e5, e6); + Collections.addAll(all, remaining); + return copyOf(Ordering.natural(), all); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static > ImmutableSortedMultiset copyOf(E[] elements) { + return copyOf(Ordering.natural(), Arrays.asList(elements)); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. To create a copy of a {@code SortedMultiset} that preserves the comparator, call + * {@link #copyOfSorted} instead. This method iterates over {@code elements} at most once. + * + *

Note that if {@code s} is a {@code Multiset}, then {@code + * ImmutableSortedMultiset.copyOf(s)} returns an {@code ImmutableSortedMultiset} + * containing each of the strings in {@code s}, while {@code ImmutableSortedMultiset.of(s)} + * returns an {@code ImmutableSortedMultiset>} containing one element (the given + * multiset itself). + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

This method is not type-safe, as it may be called on elements that are not mutually + * comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedMultiset copyOf(Iterable elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedMultisetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by their natural + * ordering. + * + *

This method is not type-safe, as it may be called on elements that are not mutually + * comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedMultiset copyOf(Iterator elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedMultisetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by the given {@code + * Comparator}. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} is null + */ + public static ImmutableSortedMultiset copyOf( + Comparator comparator, Iterator elements) { + checkNotNull(comparator); + return new Builder(comparator).addAll(elements).build(); + } + + /** + * Returns an immutable sorted multiset containing the given elements sorted by the given {@code + * Comparator}. This method iterates over {@code elements} at most once. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} is null + */ + public static ImmutableSortedMultiset copyOf( + Comparator comparator, Iterable elements) { + if (elements instanceof ImmutableSortedMultiset) { + @SuppressWarnings("unchecked") // immutable collections are always safe for covariant casts + ImmutableSortedMultiset multiset = (ImmutableSortedMultiset) elements; + if (comparator.equals(multiset.comparator())) { + if (multiset.isPartialView()) { + return copyOfSortedEntries(comparator, multiset.entrySet().asList()); + } else { + return multiset; + } + } + } + elements = Lists.newArrayList(elements); // defensive copy + TreeMultiset sortedCopy = TreeMultiset.create(checkNotNull(comparator)); + Iterables.addAll(sortedCopy, elements); + return copyOfSortedEntries(comparator, sortedCopy.entrySet()); + } + + /** + * Returns an immutable sorted multiset containing the elements of a sorted multiset, sorted by + * the same {@code Comparator}. That behavior differs from {@link #copyOf(Iterable)}, which always + * uses the natural ordering of the elements. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

This method is safe to use even when {@code sortedMultiset} is a synchronized or concurrent + * collection that is currently being modified by another thread. + * + * @throws NullPointerException if {@code sortedMultiset} or any of its elements is null + */ + public static ImmutableSortedMultiset copyOfSorted(SortedMultiset sortedMultiset) { + return copyOfSortedEntries( + sortedMultiset.comparator(), Lists.newArrayList(sortedMultiset.entrySet())); + } + + private static ImmutableSortedMultiset copyOfSortedEntries( + Comparator comparator, Collection> entries) { + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } + ImmutableList.Builder elementsBuilder = new ImmutableList.Builder(entries.size()); + long[] cumulativeCounts = new long[entries.size() + 1]; + int i = 0; + for (Entry entry : entries) { + elementsBuilder.add(entry.getElement()); + cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount(); + i++; + } + return new RegularImmutableSortedMultiset( + new RegularImmutableSortedSet(elementsBuilder.build(), comparator), + cumulativeCounts, + 0, + entries.size()); + } + + @SuppressWarnings("unchecked") + static ImmutableSortedMultiset emptyMultiset(Comparator comparator) { + if (Ordering.natural().equals(comparator)) { + return (ImmutableSortedMultiset) RegularImmutableSortedMultiset.NATURAL_EMPTY_MULTISET; + } else { + return new RegularImmutableSortedMultiset(comparator); + } + } + + ImmutableSortedMultiset() {} + + @Override + public final Comparator comparator() { + return elementSet().comparator(); + } + + @Override + public abstract ImmutableSortedSet elementSet(); + + transient ImmutableSortedMultiset descendingMultiset; + + @Override + public ImmutableSortedMultiset descendingMultiset() { + ImmutableSortedMultiset result = descendingMultiset; + if (result == null) { + return descendingMultiset = + this.isEmpty() + ? emptyMultiset(Ordering.from(comparator()).reverse()) + : new DescendingImmutableSortedMultiset(this); + } + return result; + } + + /** + * {@inheritDoc} + * + *

This implementation is guaranteed to throw an {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + *

This implementation is guaranteed to throw an {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public abstract ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType); + + @Override + public ImmutableSortedMultiset subMultiset( + E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { + checkArgument( + comparator().compare(lowerBound, upperBound) <= 0, + "Expected lowerBound <= upperBound but %s > %s", + lowerBound, + upperBound); + return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); + } + + @Override + public abstract ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType); + + /** + * Returns a builder that creates immutable sorted multisets with an explicit comparator. If the + * comparator has a more general type than the set being generated, such as creating a {@code + * SortedMultiset} with a {@code Comparator}, use the {@link Builder} constructor + * instead. + * + * @throws NullPointerException if {@code comparator} is null + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder(comparator); + } + + /** + * Returns a builder that creates immutable sorted multisets whose elements are ordered by the + * reverse of their natural ordering. + * + *

Note: the type parameter {@code E} extends {@code Comparable} rather than {@code + * Comparable} as a workaround for javac bug 6468354. + */ + public static > Builder reverseOrder() { + return new Builder(Ordering.natural().reverse()); + } + + /** + * Returns a builder that creates immutable sorted multisets whose elements are ordered by their + * natural ordering. The sorted multisets use {@link Ordering#natural()} as the comparator. This + * method provides more type-safety than {@link #builder}, as it can be called only for classes + * that implement {@link Comparable}. + * + *

Note: the type parameter {@code E} extends {@code Comparable} rather than {@code + * Comparable} as a workaround for javac bug 6468354. + */ + public static > Builder naturalOrder() { + return new Builder(Ordering.natural()); + } + + /** + * A builder for creating immutable multiset instances, especially {@code public static final} + * multisets ("constant multisets"). Example: + * + *

{@code
+   * public static final ImmutableSortedMultiset BEANS =
+   *     new ImmutableSortedMultiset.Builder(colorComparator())
+   *         .addCopies(Bean.COCOA, 4)
+   *         .addCopies(Bean.GARDEN, 6)
+   *         .addCopies(Bean.RED, 8)
+   *         .addCopies(Bean.BLACK_EYED, 10)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple multisets in series. + * + * @since 12.0 + */ + public static class Builder extends ImmutableMultiset.Builder { + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableSortedMultiset#orderedBy(Comparator)}. + */ + public Builder(Comparator comparator) { + super(TreeMultiset.create(checkNotNull(comparator))); + } + + /** + * Adds {@code element} to the {@code ImmutableSortedMultiset}. + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override + public Builder add(E element) { + super.add(element); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. + * + * @param elements the elements to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder add(E... elements) { + super.add(elements); + return this; + } + + /** + * Adds a number of occurrences of an element to this {@code ImmutableSortedMultiset}. + * + * @param element the element to add + * @param occurrences the number of occurrences of the element to add. May be zero, in which + * case no change will be made. + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation + * would result in more than {@link Integer#MAX_VALUE} occurrences of the element + */ + @Override + public Builder addCopies(E element, int occurrences) { + super.addCopies(element, occurrences); + return this; + } + + /** + * Adds or removes the necessary occurrences of an element such that the element attains the + * desired count. + * + * @param element the element to add or remove occurrences of + * @param count the desired count of the element in this multiset + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + * @throws IllegalArgumentException if {@code count} is negative + */ + @Override + public Builder setCount(E element, int count) { + super.setCount(element, count); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableSortedMultiset} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. + * + * @param elements the elements to add to the {@code ImmutableSortedMultiset} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a null element + */ + @Override + public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + /** + * Returns a newly-created {@code ImmutableSortedMultiset} based on the contents of the {@code + * Builder}. + */ + @Override + public ImmutableSortedMultiset build() { + return copyOfSorted((SortedMultiset) contents); + } + } + + @J2ktIncompatible // serialization + private static final class SerializedForm implements Serializable { + final Comparator comparator; + final E[] elements; + final int[] counts; + + @SuppressWarnings("unchecked") + SerializedForm(SortedMultiset multiset) { + this.comparator = multiset.comparator(); + int n = multiset.entrySet().size(); + elements = (E[]) new Object[n]; + counts = new int[n]; + int i = 0; + for (Entry entry : multiset.entrySet()) { + elements[i] = entry.getElement(); + counts[i] = entry.getCount(); + i++; + } + } + + Object readResolve() { + int n = elements.length; + Builder builder = new Builder<>(comparator); + for (int i = 0; i < n; i++) { + builder.addCopies(elements[i], counts[i]); + } + return builder.build(); + } + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm(this); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMultisetFauxverideShim.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMultisetFauxverideShim.java new file mode 100644 index 0000000000000..55a6cffc4b783 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedMultisetFauxverideShim.java @@ -0,0 +1,206 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; + +/** + * "Overrides" the {@link ImmutableMultiset} static methods that lack {@link + * ImmutableSortedMultiset} equivalents with deprecated, exception-throwing versions. This prevents + * accidents like the following: + * + *

{@code
+ * List objects = ...;
+ * // Sort them:
+ * Set sorted = ImmutableSortedMultiset.copyOf(objects);
+ * // BAD CODE! The returned multiset is actually an unsorted ImmutableMultiset!
+ * }
+ *
+ * 

While we could put the overrides in {@link ImmutableSortedMultiset} itself, it seems clearer + * to separate these "do not call" methods from those intended for normal use. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class ImmutableSortedMultisetFauxverideShim extends ImmutableMultiset { + /** + * Not supported. Use {@link ImmutableSortedMultiset#toImmutableSortedMultiset} instead. This + * method exists only to hide {@link ImmutableMultiset#toImmutableMultiset} from consumers of + * {@code ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#toImmutableSortedMultiset}. + * @since 21.0 + */ + @Deprecated + public static Collector> toImmutableMultiset() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link ImmutableSortedMultiset#toImmutableSortedMultiset} instead. This + * method exists only to hide {@link ImmutableMultiset#toImmutableMultiset} from consumers of + * {@code ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#toImmutableSortedMultiset}. + * @since 22.0 + */ + @Deprecated + public static Collector> toImmutableMultiset( + Function elementFunction, ToIntFunction countFunction) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link ImmutableSortedMultiset#naturalOrder}, which offers better + * type-safety, instead. This method exists only to hide {@link ImmutableMultiset#builder} from + * consumers of {@code ImmutableSortedMultiset}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedMultiset#naturalOrder}, which offers better type-safety. + */ + @Deprecated + public static ImmutableSortedMultiset.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a parameter of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E element) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable)} . + * + */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain a non-{@code + * Comparable} element. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#of(Comparable, Comparable, Comparable, Comparable, Comparable, + * Comparable, Comparable...)} . + */ + @Deprecated + public static ImmutableSortedMultiset of( + E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a multiset that may contain non-{@code + * Comparable} elements. Proper calls will resolve to the version in {@code + * ImmutableSortedMultiset}, not this dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass parameters of type {@code Comparable} to use {@link + * ImmutableSortedMultiset#copyOf(Comparable[])}. + */ + @Deprecated + public static ImmutableSortedMultiset copyOf(E[] elements) { + throw new UnsupportedOperationException(); + } + + /* + * We would like to include an unsupported " copyOf(Iterable)" here, providing only the + * properly typed "> copyOf(Iterable)" in ImmutableSortedMultiset (and + * likewise for the Iterator equivalent). However, due to a change in Sun's interpretation of the + * JLS (as described at http://bugs.sun.com/view_bug.do?bug_id=6182950), the OpenJDK 7 compiler + * available as of this writing rejects our attempts. To maintain compatibility with that version + * and with any other compilers that interpret the JLS similarly, there is no definition of + * copyOf() here, and the definition in ImmutableSortedMultiset matches that in + * ImmutableMultiset. + * + * The result is that ImmutableSortedMultiset.copyOf() may be called on non-Comparable elements. + * We have not discovered a better solution. In retrospect, the static factory methods should + * have gone in a separate class so that ImmutableSortedMultiset wouldn't "inherit" + * too-permissive factory methods from ImmutableMultiset. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedSet.java new file mode 100644 index 0000000000000..0ee4702ec7df4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedSet.java @@ -0,0 +1,857 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.ObjectArrays.checkElementsNotNull; + +/** + * A {@link NavigableSet} whose contents will never change, with many other important properties + * detailed at {@link ImmutableCollection}. + * + *

Warning: as with any sorted collection, you are strongly advised not to use a {@link + * Comparator} or {@link Comparable} type whose comparison behavior is inconsistent with + * equals. That is, {@code a.compareTo(b)} or {@code comparator.compare(a, b)} should equal zero + * if and only if {@code a.equals(b)}. If this advice is not followed, the resulting + * collection will not correctly obey its specification. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (implements {@code NavigableSet} since 12.0) + */ +// TODO(benyu): benchmark and optimize all creation paths, which are a mess now +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // we're overriding default serialization +@ElementTypesAreNonnullByDefault +public abstract class ImmutableSortedSet extends ImmutableSortedSetFauxverideShim + implements NavigableSet, SortedIterable { + static final int SPLITERATOR_CHARACTERISTICS = + ImmutableSet.SPLITERATOR_CHARACTERISTICS | Spliterator.SORTED; + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code + * ImmutableSortedSet}, ordered by the specified comparator. + * + *

If the elements contain duplicates (according to the comparator), only the first duplicate + * in encounter order will appear in the result. + * + * @since 21.0 + */ + public static Collector> toImmutableSortedSet( + Comparator comparator) { + return CollectCollectors.toImmutableSortedSet(comparator); + } + + static RegularImmutableSortedSet emptySet(Comparator comparator) { + if (Ordering.natural().equals(comparator)) { + return (RegularImmutableSortedSet) RegularImmutableSortedSet.NATURAL_EMPTY_SET; + } else { + return new RegularImmutableSortedSet(ImmutableList.of(), comparator); + } + } + + /** + * Returns the empty immutable sorted set. + * + *

Performance note: the instance returned is a singleton. + */ + public static ImmutableSortedSet of() { + return (ImmutableSortedSet) RegularImmutableSortedSet.NATURAL_EMPTY_SET; + } + + /** Returns an immutable sorted set containing a single element. */ + public static > ImmutableSortedSet of(E element) { + return new RegularImmutableSortedSet(ImmutableList.of(element), Ordering.natural()); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@link Comparable#compareTo}, only the first + * one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2) { + return construct(Ordering.natural(), 2, e1, e2); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@link Comparable#compareTo}, only the first + * one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2, E e3) { + return construct(Ordering.natural(), 3, e1, e2, e3); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@link Comparable#compareTo}, only the first + * one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + return construct(Ordering.natural(), 4, e1, e2, e3, e4); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@link Comparable#compareTo}, only the first + * one specified is included. + * + * @throws NullPointerException if any element is null + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of( + E e1, E e2, E e3, E e4, E e5) { + return construct(Ordering.natural(), 5, e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@link Comparable#compareTo}, only the first + * one specified is included. + * + * @throws NullPointerException if any element is null + * @since 3.0 (source-compatible since 2.0) + */ + @SuppressWarnings("unchecked") + public static > ImmutableSortedSet of( + E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + Comparable[] contents = new Comparable[6 + remaining.length]; + contents[0] = e1; + contents[1] = e2; + contents[2] = e3; + contents[3] = e4; + contents[4] = e5; + contents[5] = e6; + System.arraycopy(remaining, 0, contents, 6, remaining.length); + return construct(Ordering.natural(), contents.length, (E[]) contents); + } + + // TODO(kevinb): Consider factory methods that reject duplicates + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@link Comparable#compareTo}, only the first + * one specified is included. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 3.0 + */ + public static > ImmutableSortedSet copyOf(E[] elements) { + return construct(Ordering.natural(), elements.length, elements.clone()); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@code compareTo()}, only the first one + * specified is included. To create a copy of a {@code SortedSet} that preserves the comparator, + * call {@link #copyOfSorted} instead. This method iterates over {@code elements} at most once. + * + *

Note that if {@code s} is a {@code Set}, then {@code ImmutableSortedSet.copyOf(s)} + * returns an {@code ImmutableSortedSet} containing each of the strings in {@code s}, + * while {@code ImmutableSortedSet.of(s)} returns an {@code ImmutableSortedSet>} + * containing one element (the given set itself). + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

This method is not type-safe, as it may be called on elements that are not mutually + * comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedSet copyOf(Iterable elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@code compareTo()}, only the first one + * specified is included. To create a copy of a {@code SortedSet} that preserves the comparator, + * call {@link #copyOfSorted} instead. This method iterates over {@code elements} at most once. + * + *

Note that if {@code s} is a {@code Set}, then {@code ImmutableSortedSet.copyOf(s)} + * returns an {@code ImmutableSortedSet} containing each of the strings in {@code s}, + * while {@code ImmutableSortedSet.of(s)} returns an {@code ImmutableSortedSet>} + * containing one element (the given set itself). + * + *

Note: Despite what the method name suggests, if {@code elements} is an {@code + * ImmutableSortedSet}, it may be returned instead of a copy. + * + *

This method is not type-safe, as it may be called on elements that are not mutually + * comparable. + * + *

This method is safe to use even when {@code elements} is a synchronized or concurrent + * collection that is currently being modified by another thread. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + * @since 7.0 (source-compatible since 2.0) + */ + public static ImmutableSortedSet copyOf(Collection elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by their natural ordering. + * When multiple elements are equivalent according to {@code compareTo()}, only the first one + * specified is included. + * + *

This method is not type-safe, as it may be called on elements that are not mutually + * comparable. + * + * @throws ClassCastException if the elements are not mutually comparable + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSortedSet copyOf(Iterator elements) { + // Hack around E not being a subtype of Comparable. + // Unsafe, see ImmutableSortedSetFauxverideShim. + @SuppressWarnings("unchecked") + Ordering naturalOrder = (Ordering) Ordering.natural(); + return copyOf(naturalOrder, elements); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by the given {@code + * Comparator}. When multiple elements are equivalent according to {@code compareTo()}, only the + * first one specified is included. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} is null + */ + public static ImmutableSortedSet copyOf( + Comparator comparator, Iterator elements) { + return new Builder(comparator).addAll(elements).build(); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by the given {@code + * Comparator}. When multiple elements are equivalent according to {@code compare()}, only the + * first one specified is included. This method iterates over {@code elements} at most once. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} is null + */ + public static ImmutableSortedSet copyOf( + Comparator comparator, Iterable elements) { + checkNotNull(comparator); + boolean hasSameComparator = SortedIterables.hasSameComparator(comparator, elements); + + if (hasSameComparator && (elements instanceof ImmutableSortedSet)) { + @SuppressWarnings("unchecked") + ImmutableSortedSet original = (ImmutableSortedSet) elements; + if (!original.isPartialView()) { + return original; + } + } + @SuppressWarnings("unchecked") // elements only contains E's; it's safe. + E[] array = (E[]) Iterables.toArray(elements); + return construct(comparator, array.length, array); + } + + /** + * Returns an immutable sorted set containing the given elements sorted by the given {@code + * Comparator}. When multiple elements are equivalent according to {@code compareTo()}, only the + * first one specified is included. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

This method is safe to use even when {@code elements} is a synchronized or concurrent + * collection that is currently being modified by another thread. + * + * @throws NullPointerException if {@code comparator} or any of {@code elements} is null + * @since 7.0 (source-compatible since 2.0) + */ + public static ImmutableSortedSet copyOf( + Comparator comparator, Collection elements) { + return copyOf(comparator, (Iterable) elements); + } + + /** + * Returns an immutable sorted set containing the elements of a sorted set, sorted by the same + * {@code Comparator}. That behavior differs from {@link #copyOf(Iterable)}, which always uses the + * natural ordering of the elements. + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + * + *

This method is safe to use even when {@code sortedSet} is a synchronized or concurrent + * collection that is currently being modified by another thread. + * + * @throws NullPointerException if {@code sortedSet} or any of its elements is null + */ + public static ImmutableSortedSet copyOfSorted(SortedSet sortedSet) { + Comparator comparator = SortedIterables.comparator(sortedSet); + ImmutableList list = ImmutableList.copyOf(sortedSet); + if (list.isEmpty()) { + return emptySet(comparator); + } else { + return new RegularImmutableSortedSet(list, comparator); + } + } + + /** + * Constructs an {@code ImmutableSortedSet} from the first {@code n} elements of {@code contents}. + * If {@code k} is the size of the returned {@code ImmutableSortedSet}, then the sorted unique + * elements are in the first {@code k} positions of {@code contents}, and {@code contents[i] == + * null} for {@code k <= i < n}. + * + *

This method takes ownership of {@code contents}; do not modify {@code contents} after this + * returns. + * + * @throws NullPointerException if any of the first {@code n} elements of {@code contents} is null + */ + static ImmutableSortedSet construct( + Comparator comparator, int n, E... contents) { + if (n == 0) { + return emptySet(comparator); + } + checkElementsNotNull(contents, n); + Arrays.sort(contents, 0, n, comparator); + int uniques = 1; + for (int i = 1; i < n; i++) { + E cur = contents[i]; + E prev = contents[uniques - 1]; + if (comparator.compare(cur, prev) != 0) { + contents[uniques++] = cur; + } + } + Arrays.fill(contents, uniques, n, null); + return new RegularImmutableSortedSet( + ImmutableList.asImmutableList(contents, uniques), comparator); + } + + /** + * Returns a builder that creates immutable sorted sets with an explicit comparator. If the + * comparator has a more general type than the set being generated, such as creating a {@code + * SortedSet} with a {@code Comparator}, use the {@link Builder} constructor + * instead. + * + * @throws NullPointerException if {@code comparator} is null + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder(comparator); + } + + /** + * Returns a builder that creates immutable sorted sets whose elements are ordered by the reverse + * of their natural ordering. + */ + public static > Builder reverseOrder() { + return new Builder(Collections.reverseOrder()); + } + + /** + * Returns a builder that creates immutable sorted sets whose elements are ordered by their + * natural ordering. The sorted sets use {@link Ordering#natural()} as the comparator. This method + * provides more type-safety than {@link #builder}, as it can be called only for classes that + * implement {@link Comparable}. + */ + public static > Builder naturalOrder() { + return new Builder(Ordering.natural()); + } + + /** + * A builder for creating immutable sorted set instances, especially {@code public static final} + * sets ("constant sets"), with a given comparator. Example: + * + *

{@code
+   * public static final ImmutableSortedSet LUCKY_NUMBERS =
+   *     new ImmutableSortedSet.Builder(ODDS_FIRST_COMPARATOR)
+   *         .addAll(SINGLE_DIGIT_PRIMES)
+   *         .add(42)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple sets in series. Each set is a superset of the set created before it. + * + * @since 2.0 + */ + public static final class Builder extends ImmutableSet.Builder { + private final Comparator comparator; + private E[] elements; + private int n; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableSortedSet#orderedBy}. + */ + public Builder(Comparator comparator) { + super(true); // don't construct guts of hash-based set builder + this.comparator = checkNotNull(comparator); + this.elements = (E[]) new Object[ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY]; + this.n = 0; + } + + @Override + void copy() { + elements = Arrays.copyOf(elements, elements.length); + } + + private void sortAndDedup() { + if (n == 0) { + return; + } + Arrays.sort(elements, 0, n, comparator); + int unique = 1; + for (int i = 1; i < n; i++) { + int cmp = comparator.compare(elements[unique - 1], elements[i]); + if (cmp < 0) { + elements[unique++] = elements[i]; + } else if (cmp > 0) { + throw new AssertionError( + "Comparator " + comparator + " compare method violates its contract"); + } + } + Arrays.fill(elements, unique, n, null); + n = unique; + } + + /** + * Adds {@code element} to the {@code ImmutableSortedSet}. If the {@code ImmutableSortedSet} + * already contains {@code element}, then {@code add} has no effect. (only the previously added + * element is retained). + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override + public Builder add(E element) { + checkNotNull(element); + copyIfNecessary(); + if (n == elements.length) { + sortAndDedup(); + /* + * Sorting operations can only be allowed to occur once every O(n) operations to keep + * amortized O(n log n) performance. Therefore, ensure there are at least O(n) *unused* + * spaces in the builder array. + */ + int newLength = ImmutableCollection.Builder.expandedCapacity(n, n + 1); + if (newLength > elements.length) { + elements = Arrays.copyOf(elements, newLength); + } + } + elements[n++] = element; + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedSet}, ignoring duplicate + * elements (only the first duplicate element is added). + * + * @param elements the elements to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} contains a null element + */ + @Override + public Builder add(E... elements) { + checkElementsNotNull(elements); + for (E e : elements) { + add(e); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedSet}, ignoring duplicate + * elements (only the first duplicate element is added). + * + * @param elements the elements to add to the {@code ImmutableSortedSet} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} contains a null element + */ + @Override + public Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSortedSet}, ignoring duplicate + * elements (only the first duplicate element is added). + * + * @param elements the elements to add to the {@code ImmutableSortedSet} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} contains a null element + */ + @Override + public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + @Override + Builder combine(ImmutableSet.Builder builder) { + copyIfNecessary(); + Builder other = (Builder) builder; + for (int i = 0; i < other.n; i++) { + add(other.elements[i]); + } + return this; + } + + /** + * Returns a newly-created {@code ImmutableSortedSet} based on the contents of the {@code + * Builder} and its comparator. + */ + @Override + public ImmutableSortedSet build() { + sortAndDedup(); + if (n == 0) { + return emptySet(comparator); + } else { + forceCopy = true; + return new RegularImmutableSortedSet( + ImmutableList.asImmutableList(elements, n), comparator); + } + } + } + + int unsafeCompare(Object a, Object b) { + return unsafeCompare(comparator, a, b); + } + + static int unsafeCompare(Comparator comparator, Object a, Object b) { + // Pretend the comparator can compare anything. If it turns out it can't + // compare a and b, we should get a CCE or NPE on the subsequent line. Only methods + // that are spec'd to throw CCE and NPE should call this. + @SuppressWarnings({"unchecked", "nullness"}) + Comparator unsafeComparator = (Comparator) comparator; + return unsafeComparator.compare(a, b); + } + + final transient Comparator comparator; + + ImmutableSortedSet(Comparator comparator) { + this.comparator = comparator; + } + + /** + * Returns the comparator that orders the elements, which is {@link Ordering#natural()} when the + * natural ordering of the elements is used. Note that its behavior is not consistent with {@link + * SortedSet#comparator()}, which returns {@code null} to indicate natural ordering. + */ + @Override + public Comparator comparator() { + return comparator; + } + + @Override // needed to unify the iterator() methods in Collection and SortedIterable + public abstract UnmodifiableIterator iterator(); + + /** + * {@inheritDoc} + * + *

This method returns a serializable {@code ImmutableSortedSet}. + * + *

The {@link SortedSet#headSet} documentation states that a subset of a subset throws an + * {@link IllegalArgumentException} if passed a {@code toElement} greater than an earlier {@code + * toElement}. However, this method doesn't throw an exception in that situation, but instead + * keeps the original {@code toElement}. + */ + @Override + public ImmutableSortedSet headSet(E toElement) { + return headSet(toElement, false); + } + + /** + * @since 12.0 + */ + @Override + public ImmutableSortedSet headSet(E toElement, boolean inclusive) { + return headSetImpl(checkNotNull(toElement), inclusive); + } + + /** + * {@inheritDoc} + * + *

This method returns a serializable {@code ImmutableSortedSet}. + * + *

The {@link SortedSet#subSet} documentation states that a subset of a subset throws an {@link + * IllegalArgumentException} if passed a {@code fromElement} smaller than an earlier {@code + * fromElement}. However, this method doesn't throw an exception in that situation, but instead + * keeps the original {@code fromElement}. Similarly, this method keeps the original {@code + * toElement}, instead of throwing an exception, if passed a {@code toElement} greater than an + * earlier {@code toElement}. + */ + @Override + public ImmutableSortedSet subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public ImmutableSortedSet subSet( + E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + checkNotNull(fromElement); + checkNotNull(toElement); + checkArgument(comparator.compare(fromElement, toElement) <= 0); + return subSetImpl(fromElement, fromInclusive, toElement, toInclusive); + } + + /** + * {@inheritDoc} + * + *

This method returns a serializable {@code ImmutableSortedSet}. + * + *

The {@link SortedSet#tailSet} documentation states that a subset of a subset throws an + * {@link IllegalArgumentException} if passed a {@code fromElement} smaller than an earlier {@code + * fromElement}. However, this method doesn't throw an exception in that situation, but instead + * keeps the original {@code fromElement}. + */ + @Override + public ImmutableSortedSet tailSet(E fromElement) { + return tailSet(fromElement, true); + } + + /** + * @since 12.0 + */ + @Override + public ImmutableSortedSet tailSet(E fromElement, boolean inclusive) { + return tailSetImpl(checkNotNull(fromElement), inclusive); + } + + /* + * These methods perform most headSet, subSet, and tailSet logic, besides + * parameter validation. + */ + abstract ImmutableSortedSet headSetImpl(E toElement, boolean inclusive); + + abstract ImmutableSortedSet subSetImpl( + E fromElement, boolean fromInclusive, E toElement, boolean toInclusive); + + abstract ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive); + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public E lower(E e) { + return Iterators.getNext(headSet(e, false).descendingIterator(), null); + } + + /** + * @since 12.0 + */ + @Override + public E floor(E e) { + return Iterators.getNext(headSet(e, true).descendingIterator(), null); + } + + /** + * @since 12.0 + */ + @Override + public E ceiling(E e) { + return Iterables.getFirst(tailSet(e, true), null); + } + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public E higher(E e) { + return Iterables.getFirst(tailSet(e, false), null); + } + + @Override + public E first() { + return iterator().next(); + } + + @Override + public E last() { + return descendingIterator().next(); + } + + /** + * Guaranteed to throw an exception and leave the set unmodified. + * + * @since 12.0 + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @GwtIncompatible // NavigableSet + @Override + public final E pollFirst() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the set unmodified. + * + * @since 12.0 + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @GwtIncompatible // NavigableSet + @Override + public final E pollLast() { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible // NavigableSet + transient ImmutableSortedSet descendingSet; + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public ImmutableSortedSet descendingSet() { + // racy single-check idiom + ImmutableSortedSet result = descendingSet; + if (result == null) { + result = descendingSet = createDescendingSet(); + result.descendingSet = this; + } + return result; + } + + // Most classes should implement this as new DescendingImmutableSortedSet(this), + // but we push down that implementation because ProGuard can't eliminate it even when it's always + // overridden. + @GwtIncompatible // NavigableSet + abstract ImmutableSortedSet createDescendingSet(); + + @Override + public Spliterator spliterator() { + return new Spliterators.AbstractSpliterator( + size(), SPLITERATOR_CHARACTERISTICS | Spliterator.SIZED) { + final UnmodifiableIterator iterator = iterator(); + + @Override + public boolean tryAdvance(Consumer action) { + if (iterator.hasNext()) { + action.accept(iterator.next()); + return true; + } else { + return false; + } + } + + @Override + public Comparator getComparator() { + return comparator; + } + }; + } + + /** + * @since 12.0 + */ + @GwtIncompatible // NavigableSet + @Override + public abstract UnmodifiableIterator descendingIterator(); + + /** Returns the position of an element within the set, or -1 if not present. */ + abstract int indexOf(Object target); + + /* + * This class is used to serialize all ImmutableSortedSet instances, + * regardless of implementation type. It captures their "logical contents" + * only. This is necessary to ensure that the existence of a particular + * implementation type is an implementation detail. + */ + @J2ktIncompatible // serialization + private static class SerializedForm implements Serializable { + final Comparator comparator; + final Object[] elements; + + public SerializedForm(Comparator comparator, Object[] elements) { + this.comparator = comparator; + this.elements = elements; + } + + @SuppressWarnings("unchecked") + Object readResolve() { + return new Builder(comparator).add((E[]) elements).build(); + } + + private static final long serialVersionUID = 0; + } + + @J2ktIncompatible // serialization + private void readObject(ObjectInputStream unused) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new SerializedForm(comparator, toArray()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedSetFauxverideShim.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedSetFauxverideShim.java new file mode 100644 index 0000000000000..015ca7db5f050 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableSortedSetFauxverideShim.java @@ -0,0 +1,201 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.stream.Collector; + +/** + * "Overrides" the {@link ImmutableSet} static methods that lack {@link ImmutableSortedSet} + * equivalents with deprecated, exception-throwing versions. This prevents accidents like the + * following: + * + *

{@code
+ * List objects = ...;
+ * // Sort them:
+ * Set sorted = ImmutableSortedSet.copyOf(objects);
+ * // BAD CODE! The returned set is actually an unsorted ImmutableSet!
+ * }
+ *
+ * 

While we could put the overrides in {@link ImmutableSortedSet} itself, it seems clearer to + * separate these "do not call" methods from those intended for normal use. + * + * @author Chris Povirk + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class ImmutableSortedSetFauxverideShim extends ImmutableSet.CachingAsList { + /** + * Not supported. Use {@link ImmutableSortedSet#toImmutableSortedSet} instead. This method exists + * only to hide {@link ImmutableSet#toImmutableSet} from consumers of {@code ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedSet#toImmutableSortedSet}. + * @since 21.0 + */ + @Deprecated + public static Collector> toImmutableSet() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. Use {@link ImmutableSortedSet#naturalOrder}, which offers better type-safety, + * instead. This method exists only to hide {@link ImmutableSet#builder} from consumers of {@code + * ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Use {@link ImmutableSortedSet#naturalOrder}, which offers better type-safety. + */ + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. This method exists only to hide {@link ImmutableSet#builderWithExpectedSize} + * from consumers of {@code ImmutableSortedSet}. + * + * @throws UnsupportedOperationException always + * @deprecated Not supported by ImmutableSortedSet. + */ + @Deprecated + public static ImmutableSortedSet.Builder builderWithExpectedSize(int expectedSize) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass a parameter of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E element) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of( Comparable, Comparable, Comparable, Comparable, Comparable)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain a non-{@code Comparable} + * element. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass the parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#of(Comparable, Comparable, Comparable, Comparable, Comparable, + * Comparable, Comparable...)}. + */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** + * Not supported. You are attempting to create a set that may contain non-{@code Comparable} + * elements. Proper calls will resolve to the version in {@code ImmutableSortedSet}, not this + * dummy version. + * + * @throws UnsupportedOperationException always + * @deprecated Pass parameters of type {@code Comparable} to use {@link + * ImmutableSortedSet#copyOf(Comparable[])}. + */ + @Deprecated + public static ImmutableSortedSet copyOf(E[] elements) { + throw new UnsupportedOperationException(); + } + + /* + * We would like to include an unsupported " copyOf(Iterable)" here, + * providing only the properly typed + * "> copyOf(Iterable)" in ImmutableSortedSet (and + * likewise for the Iterator equivalent). However, due to a change in Sun's + * interpretation of the JLS (as described at + * http://bugs.sun.com/view_bug.do?bug_id=6182950), the OpenJDK 7 compiler + * available as of this writing rejects our attempts. To maintain + * compatibility with that version and with any other compilers that interpret + * the JLS similarly, there is no definition of copyOf() here, and the + * definition in ImmutableSortedSet matches that in ImmutableSet. + * + * The result is that ImmutableSortedSet.copyOf() may be called on + * non-Comparable elements. We have not discovered a better solution. In + * retrospect, the static factory methods should have gone in a separate class + * so that ImmutableSortedSet wouldn't "inherit" too-permissive factory + * methods from ImmutableSet. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableTable.java new file mode 100644 index 0000000000000..883e03584d900 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ImmutableTable.java @@ -0,0 +1,501 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.MoreObjects; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Spliterator; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Table} whose contents will never change, with many other important properties detailed + * at {@link ImmutableCollection}. + * + *

See the Guava User Guide article on immutable collections. + * + * @author Gregory Kick + * @since 11.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ImmutableTable extends AbstractTable + implements Serializable { + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each + * input element is mapped to one cell in the returned table, with the rows, columns, and values + * generated by applying the specified functions. + * + *

The returned {@code Collector} will throw a {@code NullPointerException} at collection time + * if the row, column, or value functions return null on any input. + * + * @since 21.0 + */ + public static + Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction) { + return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction); + } + + /** + * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each + * input element is mapped to one cell in the returned table, with the rows, columns, and values + * generated by applying the specified functions. If multiple inputs are mapped to the same row + * and column pair, they will be combined with the specified merging function in encounter order. + * + *

The returned {@code Collector} will throw a {@code NullPointerException} at collection time + * if the row, column, value, or merging functions return null on any input. + * + * @since 21.0 + */ + public static + Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + return TableCollectors.toImmutableTable( + rowFunction, columnFunction, valueFunction, mergeFunction); + } + + /** + * Returns an empty immutable table. + * + *

Performance note: the instance returned is a singleton. + */ + @SuppressWarnings("unchecked") + public static ImmutableTable of() { + return (ImmutableTable) SparseImmutableTable.EMPTY; + } + + /** Returns an immutable table containing a single cell. */ + public static ImmutableTable of(R rowKey, C columnKey, V value) { + return new SingletonImmutableTable<>(rowKey, columnKey, value); + } + + /** + * Returns an immutable copy of the provided table. + * + *

The {@link Table#cellSet()} iteration order of the provided table determines the iteration + * ordering of all views in the returned table. Note that some views of the original table and the + * copied table may have different iteration orders. For more control over the ordering, create a + * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and + * {@link Builder#putAll} + * + *

Despite the method name, this method attempts to avoid actually copying the data when it is + * safe to do so. The exact circumstances under which a copy will or will not be performed are + * undocumented and subject to change. + */ + public static ImmutableTable copyOf( + Table table) { + if (table instanceof ImmutableTable) { + @SuppressWarnings("unchecked") + ImmutableTable parameterizedTable = (ImmutableTable) table; + return parameterizedTable; + } else { + return copyOf(table.cellSet()); + } + } + + static ImmutableTable copyOf( + Iterable> cells) { + ImmutableTable.Builder builder = ImmutableTable.builder(); + for (Cell cell : cells) { + builder.put(cell); + } + return builder.build(); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder created by the {@link + * Builder#Builder() ImmutableTable.Builder()} constructor. + */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a + * new entry with those values. + */ + static Cell cellOf(R rowKey, C columnKey, V value) { + return Tables.immutableCell( + checkNotNull(rowKey, "rowKey"), + checkNotNull(columnKey, "columnKey"), + checkNotNull(value, "value")); + } + + /** + * A builder for creating immutable table instances, especially {@code public static final} tables + * ("constant tables"). Example: + * + *

{@code
+   * static final ImmutableTable SPREADSHEET =
+   *     new ImmutableTable.Builder()
+   *         .put(1, 'A', "foo")
+   *         .put(1, 'B', "bar")
+   *         .put(2, 'A', "baz")
+   *         .buildOrThrow();
+   * }
+ * + *

By default, the order in which cells are added to the builder determines the iteration + * ordering of all views in the returned table, with {@link #putAll} following the {@link + * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy} + * is called, the views are sorted by the supplied comparators. + * + *

For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object, + * Object)} are even more convenient. + * + *

Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to + * build multiple tables in series. Each table is a superset of the tables created before it. + * + * @since 11.0 + */ + public static final class Builder { + private final List> cells = Lists.newArrayList(); + private Comparator rowComparator; + private Comparator columnComparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder generated by {@link + * ImmutableTable#builder}. + */ + public Builder() {} + + /** Specifies the ordering of the generated table's rows. */ + public Builder orderRowsBy(Comparator rowComparator) { + this.rowComparator = checkNotNull(rowComparator, "rowComparator"); + return this; + } + + /** Specifies the ordering of the generated table's columns. */ + public Builder orderColumnsBy(Comparator columnComparator) { + this.columnComparator = checkNotNull(columnComparator, "columnComparator"); + return this; + } + + /** + * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built + * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail. + */ + public Builder put(R rowKey, C columnKey, V value) { + cells.add(cellOf(rowKey, columnKey, value)); + return this; + } + + /** + * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key + * pairs are not allowed and will cause {@link #build} to fail. + */ + public Builder put(Cell cell) { + if (cell instanceof Tables.ImmutableCell) { + checkNotNull(cell.getRowKey(), "row"); + checkNotNull(cell.getColumnKey(), "column"); + checkNotNull(cell.getValue(), "value"); + @SuppressWarnings("unchecked") // all supported methods are covariant + Cell immutableCell = (Cell) cell; + cells.add(immutableCell); + } else { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + return this; + } + + /** + * Associates all of the given table's keys and values in the built table. Duplicate row key + * column key pairs are not allowed, and will cause {@link #build} to fail. + * + * @throws NullPointerException if any key or value in {@code table} is null + */ + public Builder putAll(Table table) { + for (Cell cell : table.cellSet()) { + put(cell); + } + return this; + } + + Builder combine(Builder other) { + this.cells.addAll(other.cells); + return this; + } + + /** + * Returns a newly-created immutable table. + * + *

Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method + * will throw an exception if there are duplicate key pairs. The {@code build()} method will + * soon be deprecated. + * + * @throws IllegalArgumentException if duplicate key pairs were added + */ + public ImmutableTable build() { + return buildOrThrow(); + } + + /** + * Returns a newly-created immutable table, or throws an exception if duplicate key pairs were + * added. + * + * @throws IllegalArgumentException if duplicate key pairs were added + * @since 31.0 + */ + public ImmutableTable buildOrThrow() { + int size = cells.size(); + switch (size) { + case 0: + return of(); + case 1: + return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells)); + default: + return RegularImmutableTable.forCells(cells, rowComparator, columnComparator); + } + } + } + + ImmutableTable() {} + + @Override + public ImmutableSet> cellSet() { + return (ImmutableSet>) super.cellSet(); + } + + @Override + abstract ImmutableSet> createCellSet(); + + @Override + final UnmodifiableIterator> cellIterator() { + throw new AssertionError("should never be called"); + } + + @Override + final Spliterator> cellSpliterator() { + throw new AssertionError("should never be called"); + } + + @Override + public ImmutableCollection values() { + return (ImmutableCollection) super.values(); + } + + @Override + abstract ImmutableCollection createValues(); + + @Override + final Iterator valuesIterator() { + throw new AssertionError("should never be called"); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if {@code columnKey} is {@code null} + */ + @Override + public ImmutableMap column(C columnKey) { + checkNotNull(columnKey, "columnKey"); + return MoreObjects.firstNonNull( + (ImmutableMap) columnMap().get(columnKey), ImmutableMap.of()); + } + + @Override + public ImmutableSet columnKeySet() { + return columnMap().keySet(); + } + + /** + * {@inheritDoc} + * + *

The value {@code Map} instances in the returned map are {@link ImmutableMap} instances + * as well. + */ + @Override + public abstract ImmutableMap> columnMap(); + + /** + * {@inheritDoc} + * + * @throws NullPointerException if {@code rowKey} is {@code null} + */ + @Override + public ImmutableMap row(R rowKey) { + checkNotNull(rowKey, "rowKey"); + return MoreObjects.firstNonNull( + (ImmutableMap) rowMap().get(rowKey), ImmutableMap.of()); + } + + @Override + public ImmutableSet rowKeySet() { + return rowMap().keySet(); + } + + /** + * {@inheritDoc} + * + *

The value {@code Map} instances in the returned map are {@link ImmutableMap} instances + * as well. + */ + @Override + public abstract ImmutableMap> rowMap(); + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return get(rowKey, columnKey) != null; + } + + @Override + public boolean containsValue(Object value) { + return values().contains(value); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V put(R rowKey, C columnKey, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } + + /** Creates the common serialized form for this table. */ + abstract SerializedForm createSerializedForm(); + + /** + * Serialized type for all ImmutableTable instances. It captures the logical contents and + * preserves iteration order of all views. + */ + static final class SerializedForm implements Serializable { + private final Object[] rowKeys; + private final Object[] columnKeys; + + private final Object[] cellValues; + private final int[] cellRowIndices; + private final int[] cellColumnIndices; + + private SerializedForm( + Object[] rowKeys, + Object[] columnKeys, + Object[] cellValues, + int[] cellRowIndices, + int[] cellColumnIndices) { + this.rowKeys = rowKeys; + this.columnKeys = columnKeys; + this.cellValues = cellValues; + this.cellRowIndices = cellRowIndices; + this.cellColumnIndices = cellColumnIndices; + } + + static SerializedForm create( + ImmutableTable table, int[] cellRowIndices, int[] cellColumnIndices) { + return new SerializedForm( + table.rowKeySet().toArray(), + table.columnKeySet().toArray(), + table.values().toArray(), + cellRowIndices, + cellColumnIndices); + } + + Object readResolve() { + if (cellValues.length == 0) { + return of(); + } + if (cellValues.length == 1) { + return of(rowKeys[0], columnKeys[0], cellValues[0]); + } + ImmutableList.Builder> cellListBuilder = + new ImmutableList.Builder<>(cellValues.length); + for (int i = 0; i < cellValues.length; i++) { + cellListBuilder.add( + cellOf(rowKeys[cellRowIndices[i]], columnKeys[cellColumnIndices[i]], cellValues[i])); + } + return RegularImmutableTable.forOrderedComponents( + cellListBuilder.build(), ImmutableSet.copyOf(rowKeys), ImmutableSet.copyOf(columnKeys)); + } + + private static final long serialVersionUID = 0; + } + + final Object writeReplace() { + return createSerializedForm(); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + private static final long serialVersionUID = 0xcafebabe; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/IndexedImmutableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/IndexedImmutableSet.java new file mode 100644 index 0000000000000..7340ddf2982cd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/IndexedImmutableSet.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Spliterator; +import java.util.function.Consumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class IndexedImmutableSet extends ImmutableSet.CachingAsList { + abstract E get(int index); + + @Override + public UnmodifiableIterator iterator() { + return asList().iterator(); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.indexed(size(), SPLITERATOR_CHARACTERISTICS, this::get); + } + + @Override + public void forEach(Consumer consumer) { + checkNotNull(consumer); + int n = size(); + for (int i = 0; i < n; i++) { + consumer.accept(get(i)); + } + } + + @Override + @GwtIncompatible + int copyIntoArray(Object[] dst, int offset) { + return asList().copyIntoArray(dst, offset); + } + + @Override + ImmutableList createAsList() { + return new ImmutableAsList() { + @Override + public E get(int index) { + return IndexedImmutableSet.this.get(index); + } + + @Override + boolean isPartialView() { + return IndexedImmutableSet.this.isPartialView(); + } + + @Override + public int size() { + return IndexedImmutableSet.this.size(); + } + + @Override + ImmutableCollection delegateCollection() { + return IndexedImmutableSet.this; + } + }; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Interner.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Interner.java new file mode 100644 index 0000000000000..9b75596d33540 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Interner.java @@ -0,0 +1,53 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Provides similar behavior to {@link String#intern} for any immutable type. Common implementations + * are available from the {@link Interners} class. + * + *

Note that {@code String.intern()} has some well-known performance limitations, and should + * generally be avoided. Prefer {@link Interners#newWeakInterner} or another {@code Interner} + * implementation even for {@code String} interning. + * + * @author Kevin Bourrillion + * @since 3.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface Interner { + /** + * Chooses and returns the representative instance for any of a collection of instances that are + * equal to each other. If two {@linkplain Object#equals equal} inputs are given to this method, + * both calls will return the same instance. That is, {@code intern(a).equals(a)} always holds, + * and {@code intern(a) == intern(b)} if and only if {@code a.equals(b)}. Note that {@code + * intern(a)} is permitted to return one instance now and a different instance later if the + * original interned instance was garbage-collected. + * + *

Warning: do not use with mutable objects. + * + * @throws NullPointerException if {@code sample} is null + */ + E intern(E sample); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Interners.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Interners.java new file mode 100644 index 0000000000000..fbd4068a5e4d6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Interners.java @@ -0,0 +1,198 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.MapMaker.Dummy; +import org.apache.iotdb.google.common.collect.MapMakerInternalMap.InternalEntry; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Contains static methods pertaining to instances of {@link Interner}. + * + * @author Kevin Bourrillion + * @since 3.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Interners { + private Interners() {} + + /** + * Builder for {@link Interner} instances. + * + * @since 21.0 + */ + public static class InternerBuilder { + private final MapMaker mapMaker = new MapMaker(); + private boolean strong = true; + + private InternerBuilder() {} + + /** + * Instructs the {@link InternerBuilder} to build a strong interner. + * + * @see Interners#newStrongInterner() + */ + public InternerBuilder strong() { + this.strong = true; + return this; + } + + /** + * Instructs the {@link InternerBuilder} to build a weak interner. + * + * @see Interners#newWeakInterner() + */ + @GwtIncompatible("java.lang.ref.WeakReference") + public InternerBuilder weak() { + this.strong = false; + return this; + } + + /** + * Sets the concurrency level that will be used by the to-be-built {@link Interner}. + * + * @see MapMaker#concurrencyLevel(int) + */ + public InternerBuilder concurrencyLevel(int concurrencyLevel) { + this.mapMaker.concurrencyLevel(concurrencyLevel); + return this; + } + + public Interner build() { + if (!strong) { + mapMaker.weakKeys(); + } + return new InternerImpl<>(mapMaker); + } + } + + /** Returns a fresh {@link InternerBuilder} instance. */ + public static InternerBuilder newBuilder() { + return new InternerBuilder(); + } + + /** + * Returns a new thread-safe interner which retains a strong reference to each instance it has + * interned, thus preventing these instances from being garbage-collected. If this retention is + * acceptable, this implementation may perform better than {@link #newWeakInterner}. + */ + public static Interner newStrongInterner() { + return newBuilder().strong().build(); + } + + /** + * Returns a new thread-safe interner which retains a weak reference to each instance it has + * interned, and so does not prevent these instances from being garbage-collected. This most + * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative when + * the memory usage of that implementation is unacceptable. + */ + @GwtIncompatible("java.lang.ref.WeakReference") + public static Interner newWeakInterner() { + return newBuilder().weak().build(); + } + + @VisibleForTesting + static final class InternerImpl implements Interner { + // MapMaker is our friend, we know about this type + @VisibleForTesting final MapMakerInternalMap map; + + private InternerImpl(MapMaker mapMaker) { + this.map = + MapMakerInternalMap.createWithDummyValues(mapMaker.keyEquivalence(Equivalence.equals())); + } + + @Override + public E intern(E sample) { + while (true) { + // trying to read the canonical... + @SuppressWarnings("rawtypes") // using raw types to avoid a bug in our nullness checker :( + InternalEntry entry = map.getEntry(sample); + if (entry != null) { + Object canonical = entry.getKey(); + if (canonical != null) { // only matters if weak/soft keys are used + // The compiler would know this is safe if not for our use of raw types (see above). + @SuppressWarnings("unchecked") + E result = (E) canonical; + return result; + } + } + + // didn't see it, trying to put it instead... + Dummy sneaky = map.putIfAbsent(sample, Dummy.VALUE); + if (sneaky == null) { + return sample; + } else { + /* Someone beat us to it! Trying again... + * + * Technically this loop not guaranteed to terminate, so theoretically (extremely + * unlikely) this thread might starve, but even then, there is always going to be another + * thread doing progress here. + */ + } + } + } + } + + /** + * Returns a function that delegates to the {@link Interner#intern} method of the given interner. + * + * @since 8.0 + */ + public static Function asFunction(Interner interner) { + return new InternerFunction<>(checkNotNull(interner)); + } + + private static class InternerFunction implements Function { + + private final Interner interner; + + public InternerFunction(Interner interner) { + this.interner = interner; + } + + @Override + public E apply(E input) { + return interner.intern(input); + } + + @Override + public int hashCode() { + return interner.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof InternerFunction) { + InternerFunction that = (InternerFunction) other; + return interner.equals(that.interner); + } + + return false; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Iterables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Iterables.java new file mode 100644 index 0000000000000..538fdc548108c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Iterables.java @@ -0,0 +1,1063 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.RandomAccess; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * An assortment of mainly legacy static utility methods that operate on or return objects of type + * {@code Iterable}. Except as noted, each method has a corresponding {@link Iterator}-based method + * in the {@link Iterators} class. + * + *

Java 8 users: several common uses for this class are now more comprehensively addressed + * by the new {@link java.util.stream.Stream} library. Read the method documentation below for + * comparisons. This class is not being deprecated, but we gently encourage you to migrate to + * streams. + * + *

Performance notes: Unless otherwise noted, all of the iterables produced in this class + * are lazy, which means that their iterators only advance the backing iteration when + * absolutely necessary. + * + *

See the Guava User Guide article on {@code + * Iterables}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Iterables { + private Iterables() {} + + /** Returns an unmodifiable view of {@code iterable}. */ + public static Iterable unmodifiableIterable( + final Iterable iterable) { + checkNotNull(iterable); + if (iterable instanceof UnmodifiableIterable || iterable instanceof ImmutableCollection) { + @SuppressWarnings("unchecked") // Since it's unmodifiable, the covariant cast is safe + Iterable result = (Iterable) iterable; + return result; + } + return new UnmodifiableIterable<>(iterable); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static Iterable unmodifiableIterable(ImmutableCollection iterable) { + return checkNotNull(iterable); + } + + private static final class UnmodifiableIterable extends FluentIterable { + private final Iterable iterable; + + private UnmodifiableIterable(Iterable iterable) { + this.iterable = iterable; + } + + @Override + public Iterator iterator() { + return Iterators.unmodifiableIterator(iterable.iterator()); + } + + @Override + public void forEach(Consumer action) { + iterable.forEach(action); + } + + @SuppressWarnings("unchecked") // safe upcast, assuming no one has a crazy Spliterator subclass + @Override + public Spliterator spliterator() { + return (Spliterator) iterable.spliterator(); + } + + @Override + public String toString() { + return iterable.toString(); + } + // no equals and hashCode; it would break the contract! + } + + /** Returns the number of elements in {@code iterable}. */ + public static int size(Iterable iterable) { + return (iterable instanceof Collection) + ? ((Collection) iterable).size() + : Iterators.size(iterable.iterator()); + } + + /** + * Returns {@code true} if {@code iterable} contains any element {@code o} for which {@code + * Objects.equals(o, element)} would return {@code true}. Otherwise returns {@code false}, even in + * cases where {@link Collection#contains} might throw {@link NullPointerException} or {@link + * ClassCastException}. + */ + // instead of because of Kotlin b/189937072, discussed in Joiner. + public static boolean contains(Iterable iterable, Object element) { + if (iterable instanceof Collection) { + Collection collection = (Collection) iterable; + return Collections2.safeContains(collection, element); + } + return Iterators.contains(iterable.iterator(), element); + } + + /** + * Removes, from an iterable, every element that belongs to the provided collection. + * + *

This method calls {@link Collection#removeAll} if {@code iterable} is a collection, and + * {@link Iterators#removeAll} otherwise. + * + * @param removeFrom the iterable to (potentially) remove elements from + * @param elementsToRemove the elements to remove + * @return {@code true} if any element was removed from {@code iterable} + */ + public static boolean removeAll(Iterable removeFrom, Collection elementsToRemove) { + return (removeFrom instanceof Collection) + ? ((Collection) removeFrom).removeAll(checkNotNull(elementsToRemove)) + : Iterators.removeAll(removeFrom.iterator(), elementsToRemove); + } + + /** + * Removes, from an iterable, every element that does not belong to the provided collection. + * + *

This method calls {@link Collection#retainAll} if {@code iterable} is a collection, and + * {@link Iterators#retainAll} otherwise. + * + * @param removeFrom the iterable to (potentially) remove elements from + * @param elementsToRetain the elements to retain + * @return {@code true} if any element was removed from {@code iterable} + */ + public static boolean retainAll(Iterable removeFrom, Collection elementsToRetain) { + return (removeFrom instanceof Collection) + ? ((Collection) removeFrom).retainAll(checkNotNull(elementsToRetain)) + : Iterators.retainAll(removeFrom.iterator(), elementsToRetain); + } + + /** + * Removes, from an iterable, every element that satisfies the provided predicate. + * + *

Removals may or may not happen immediately as each element is tested against the predicate. + * The behavior of this method is not specified if {@code predicate} is dependent on {@code + * removeFrom}. + * + *

Java 8 users: if {@code removeFrom} is a {@link Collection}, use {@code + * removeFrom.removeIf(predicate)} instead. + * + * @param removeFrom the iterable to (potentially) remove elements from + * @param predicate a predicate that determines whether an element should be removed + * @return {@code true} if any elements were removed from the iterable + * @throws UnsupportedOperationException if the iterable does not support {@code remove()}. + * @since 2.0 + */ + public static boolean removeIf( + Iterable removeFrom, Predicate predicate) { + if (removeFrom instanceof Collection) { + return ((Collection) removeFrom).removeIf(predicate); + } + return Iterators.removeIf(removeFrom.iterator(), predicate); + } + + /** Removes and returns the first matching element, or returns {@code null} if there is none. */ + static T removeFirstMatching( + Iterable removeFrom, Predicate predicate) { + checkNotNull(predicate); + Iterator iterator = removeFrom.iterator(); + while (iterator.hasNext()) { + T next = iterator.next(); + if (predicate.apply(next)) { + iterator.remove(); + return next; + } + } + return null; + } + + /** + * Determines whether two iterables contain equal elements in the same order. More specifically, + * this method returns {@code true} if {@code iterable1} and {@code iterable2} contain the same + * number of elements and every element of {@code iterable1} is equal to the corresponding element + * of {@code iterable2}. + */ + public static boolean elementsEqual(Iterable iterable1, Iterable iterable2) { + if (iterable1 instanceof Collection && iterable2 instanceof Collection) { + Collection collection1 = (Collection) iterable1; + Collection collection2 = (Collection) iterable2; + if (collection1.size() != collection2.size()) { + return false; + } + } + return Iterators.elementsEqual(iterable1.iterator(), iterable2.iterator()); + } + + /** + * Returns a string representation of {@code iterable}, with the format {@code [e1, e2, ..., en]} + * (that is, identical to {@link java.util.Arrays Arrays}{@code + * .toString(Iterables.toArray(iterable))}). Note that for most implementations of {@link + * Collection}, {@code collection.toString()} also gives the same result, but that behavior is not + * generally guaranteed. + */ + public static String toString(Iterable iterable) { + return Iterators.toString(iterable.iterator()); + } + + /** + * Returns the single element contained in {@code iterable}. + * + *

Java 8 users: the {@code Stream} equivalent to this method is {@code + * stream.collect(MoreCollectors.onlyElement())}. + * + * @throws NoSuchElementException if the iterable is empty + * @throws IllegalArgumentException if the iterable contains multiple elements + */ + @ParametricNullness + public static T getOnlyElement(Iterable iterable) { + return Iterators.getOnlyElement(iterable.iterator()); + } + + /** + * Returns the single element contained in {@code iterable}, or {@code defaultValue} if the + * iterable is empty. + * + *

Java 8 users: the {@code Stream} equivalent to this method is {@code + * stream.collect(MoreCollectors.toOptional()).orElse(defaultValue)}. + * + * @throws IllegalArgumentException if the iterator contains multiple elements + */ + @ParametricNullness + public static T getOnlyElement( + Iterable iterable, @ParametricNullness T defaultValue) { + return Iterators.getOnlyElement(iterable.iterator(), defaultValue); + } + + /** + * Copies an iterable's elements into an array. + * + * @param iterable the iterable to copy + * @param type the type of the elements + * @return a newly-allocated array into which all the elements of the iterable have been copied + */ + @GwtIncompatible // Array.newInstance(Class, int) + public static T[] toArray(Iterable iterable, Class type) { + return toArray(iterable, ObjectArrays.newArray(type, 0)); + } + + static T[] toArray(Iterable iterable, T[] array) { + Collection collection = castOrCopyToCollection(iterable); + return collection.toArray(array); + } + + /** + * Copies an iterable's elements into an array. + * + * @param iterable the iterable to copy + * @return a newly-allocated array into which all the elements of the iterable have been copied + */ + static Object[] toArray(Iterable iterable) { + return castOrCopyToCollection(iterable).toArray(); + } + + /** + * Converts an iterable into a collection. If the iterable is already a collection, it is + * returned. Otherwise, an {@link java.util.ArrayList} is created with the contents of the + * iterable in the same iteration order. + */ + private static Collection castOrCopyToCollection(Iterable iterable) { + return (iterable instanceof Collection) + ? (Collection) iterable + : Lists.newArrayList(iterable.iterator()); + } + + /** + * Adds all elements in {@code iterable} to {@code collection}. + * + * @return {@code true} if {@code collection} was modified as a result of this operation. + */ + public static boolean addAll( + Collection addTo, Iterable elementsToAdd) { + if (elementsToAdd instanceof Collection) { + Collection c = (Collection) elementsToAdd; + return addTo.addAll(c); + } + return Iterators.addAll(addTo, checkNotNull(elementsToAdd).iterator()); + } + + /** + * Returns the number of elements in the specified iterable that equal the specified object. This + * implementation avoids a full iteration when the iterable is a {@link Multiset} or {@link Set}. + * + *

Java 8 users: In most cases, the {@code Stream} equivalent of this method is {@code + * stream.filter(element::equals).count()}. If {@code element} might be null, use {@code + * stream.filter(Predicate.isEqual(element)).count()} instead. + * + * @see java.util.Collections#frequency(Collection, Object) Collections.frequency(Collection, + * Object) + */ + public static int frequency(Iterable iterable, Object element) { + if ((iterable instanceof Multiset)) { + return ((Multiset) iterable).count(element); + } else if ((iterable instanceof Set)) { + return ((Set) iterable).contains(element) ? 1 : 0; + } + return Iterators.frequency(iterable.iterator(), element); + } + + /** + * Returns an iterable whose iterators cycle indefinitely over the elements of {@code iterable}. + * + *

That iterator supports {@code remove()} if {@code iterable.iterator()} does. After {@code + * remove()} is called, subsequent cycles omit the removed element, which is no longer in {@code + * iterable}. The iterator's {@code hasNext()} method returns {@code true} until {@code iterable} + * is empty. + * + *

Warning: Typical uses of the resulting iterator may produce an infinite loop. You + * should use an explicit {@code break} or be certain that you will eventually remove all the + * elements. + * + *

To cycle over the iterable {@code n} times, use the following: {@code + * Iterables.concat(Collections.nCopies(n, iterable))} + * + *

Java 8 users: The {@code Stream} equivalent of this method is {@code + * Stream.generate(() -> iterable).flatMap(Streams::stream)}. + */ + public static Iterable cycle(final Iterable iterable) { + checkNotNull(iterable); + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.cycle(iterable); + } + + @Override + public Spliterator spliterator() { + return Stream.generate(() -> iterable).flatMap(Streams::stream).spliterator(); + } + + @Override + public String toString() { + return iterable.toString() + " (cycled)"; + } + }; + } + + /** + * Returns an iterable whose iterators cycle indefinitely over the provided elements. + * + *

After {@code remove} is invoked on a generated iterator, the removed element will no longer + * appear in either that iterator or any other iterator created from the same source iterable. + * That is, this method behaves exactly as {@code Iterables.cycle(Lists.newArrayList(elements))}. + * The iterator's {@code hasNext} method returns {@code true} until all of the original elements + * have been removed. + * + *

Warning: Typical uses of the resulting iterator may produce an infinite loop. You + * should use an explicit {@code break} or be certain that you will eventually remove all the + * elements. + * + *

To cycle over the elements {@code n} times, use the following: {@code + * Iterables.concat(Collections.nCopies(n, Arrays.asList(elements)))} + * + *

Java 8 users: If passing a single element {@code e}, the {@code Stream} equivalent of + * this method is {@code Stream.generate(() -> e)}. Otherwise, put the elements in a collection + * and use {@code Stream.generate(() -> collection).flatMap(Collection::stream)}. + */ + @SafeVarargs + public static Iterable cycle(T... elements) { + return cycle(Lists.newArrayList(elements)); + } + + /** + * Combines two iterables into a single iterable. The returned iterable has an iterator that + * traverses the elements in {@code a}, followed by the elements in {@code b}. The source + * iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

Java 8 users: The {@code Stream} equivalent of this method is {@code Stream.concat(a, + * b)}. + */ + public static Iterable concat( + Iterable a, Iterable b) { + return FluentIterable.concat(a, b); + } + + /** + * Combines three iterables into a single iterable. The returned iterable has an iterator that + * traverses the elements in {@code a}, followed by the elements in {@code b}, followed by the + * elements in {@code c}. The source iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

Java 8 users: The {@code Stream} equivalent of this method is {@code + * Streams.concat(a, b, c)}. + */ + public static Iterable concat( + Iterable a, Iterable b, Iterable c) { + return FluentIterable.concat(a, b, c); + } + + /** + * Combines four iterables into a single iterable. The returned iterable has an iterator that + * traverses the elements in {@code a}, followed by the elements in {@code b}, followed by the + * elements in {@code c}, followed by the elements in {@code d}. The source iterators are not + * polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

Java 8 users: The {@code Stream} equivalent of this method is {@code + * Streams.concat(a, b, c, d)}. + */ + public static Iterable concat( + Iterable a, + Iterable b, + Iterable c, + Iterable d) { + return FluentIterable.concat(a, b, c, d); + } + + /** + * Combines multiple iterables into a single iterable. The returned iterable has an iterator that + * traverses the elements of each iterable in {@code inputs}. The input iterators are not polled + * until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. + * + *

Java 8 users: The {@code Stream} equivalent of this method is {@code + * Streams.concat(...)}. + * + * @throws NullPointerException if any of the provided iterables is null + */ + @SafeVarargs + public static Iterable concat(Iterable... inputs) { + return FluentIterable.concat(inputs); + } + + /** + * Combines multiple iterables into a single iterable. The returned iterable has an iterator that + * traverses the elements of each iterable in {@code inputs}. The input iterators are not polled + * until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the corresponding input + * iterator supports it. The methods of the returned iterable may throw {@code + * NullPointerException} if any of the input iterators is null. + * + *

Java 8 users: The {@code Stream} equivalent of this method is {@code + * streamOfStreams.flatMap(s -> s)}. + */ + public static Iterable concat( + Iterable> inputs) { + return FluentIterable.concat(inputs); + } + + /** + * Divides an iterable into unmodifiable sublists of the given size (the final iterable may be + * smaller). For example, partitioning an iterable containing {@code [a, b, c, d, e]} with a + * partition size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer iterable containing two + * inner lists of three and two elements, all in the original order. + * + *

Iterators returned by the returned iterable do not support the {@link Iterator#remove()} + * method. The returned lists implement {@link RandomAccess}, whether or not the input list does. + * + *

Note: The current implementation eagerly allocates storage for {@code size} elements. + * As a consequence, passing values like {@code Integer.MAX_VALUE} can lead to {@link + * OutOfMemoryError}. + * + *

Note: if {@code iterable} is a {@link List}, use {@link Lists#partition(List, int)} + * instead. + * + * @param iterable the iterable to return a partitioned view of + * @param size the desired size of each partition (the last may be smaller) + * @return an iterable of unmodifiable lists containing the elements of {@code iterable} divided + * into partitions + * @throws IllegalArgumentException if {@code size} is nonpositive + */ + public static Iterable> partition( + final Iterable iterable, final int size) { + checkNotNull(iterable); + checkArgument(size > 0); + return new FluentIterable>() { + @Override + public Iterator> iterator() { + return Iterators.partition(iterable.iterator(), size); + } + }; + } + + /** + * Divides an iterable into unmodifiable sublists of the given size, padding the final iterable + * with null values if necessary. For example, partitioning an iterable containing {@code [a, b, + * c, d, e]} with a partition size of 3 yields {@code [[a, b, c], [d, e, null]]} -- an outer + * iterable containing two inner lists of three elements each, all in the original order. + * + *

Iterators returned by the returned iterable do not support the {@link Iterator#remove()} + * method. + * + * @param iterable the iterable to return a partitioned view of + * @param size the desired size of each partition + * @return an iterable of unmodifiable lists containing the elements of {@code iterable} divided + * into partitions (the final iterable may have trailing null elements) + * @throws IllegalArgumentException if {@code size} is nonpositive + */ + public static Iterable> paddedPartition( + final Iterable iterable, final int size) { + checkNotNull(iterable); + checkArgument(size > 0); + return new FluentIterable>() { + @Override + public Iterator> iterator() { + return Iterators.paddedPartition(iterable.iterator(), size); + } + }; + } + + /** + * Returns a view of {@code unfiltered} containing all elements that satisfy the input predicate + * {@code retainIfTrue}. The returned iterable's iterator does not support {@code remove()}. + * + *

{@code Stream} equivalent: {@link Stream#filter}. + */ + public static Iterable filter( + final Iterable unfiltered, final Predicate retainIfTrue) { + checkNotNull(unfiltered); + checkNotNull(retainIfTrue); + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.filter(unfiltered.iterator(), retainIfTrue); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + unfiltered.forEach( + (@ParametricNullness T a) -> { + if (retainIfTrue.test(a)) { + action.accept(a); + } + }); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.filter(unfiltered.spliterator(), retainIfTrue); + } + }; + } + + /** + * Returns a view of {@code unfiltered} containing all elements that are of the type {@code + * desiredType}. The returned iterable's iterator does not support {@code remove()}. + * + *

{@code Stream} equivalent: {@code stream.filter(type::isInstance).map(type::cast)}. + * This does perform a little more work than necessary, so another option is to insert an + * unchecked cast at some later point: + * + *

+   * {@code @SuppressWarnings("unchecked") // safe because of ::isInstance check
+   * ImmutableList result =
+   *     (ImmutableList) stream.filter(NewType.class::isInstance).collect(toImmutableList());}
+   * 
+ */ + @SuppressWarnings("unchecked") + @GwtIncompatible // Class.isInstance + public static Iterable filter(final Iterable unfiltered, final Class desiredType) { + checkNotNull(unfiltered); + checkNotNull(desiredType); + return (Iterable) filter(unfiltered, Predicates.instanceOf(desiredType)); + } + + /** + * Returns {@code true} if any element in {@code iterable} satisfies the predicate. + * + *

{@code Stream} equivalent: {@link Stream#anyMatch}. + */ + public static boolean any( + Iterable iterable, Predicate predicate) { + return Iterators.any(iterable.iterator(), predicate); + } + + /** + * Returns {@code true} if every element in {@code iterable} satisfies the predicate. If {@code + * iterable} is empty, {@code true} is returned. + * + *

{@code Stream} equivalent: {@link Stream#allMatch}. + */ + public static boolean all( + Iterable iterable, Predicate predicate) { + return Iterators.all(iterable.iterator(), predicate); + } + + /** + * Returns the first element in {@code iterable} that satisfies the given predicate; use this + * method only when such an element is known to exist. If it is possible that no element + * will match, use {@link #tryFind} or {@link #find(Iterable, Predicate, Object)} instead. + * + *

{@code Stream} equivalent: {@code stream.filter(predicate).findFirst().get()} + * + * @throws NoSuchElementException if no element in {@code iterable} matches the given predicate + */ + @ParametricNullness + public static T find(Iterable iterable, Predicate predicate) { + return Iterators.find(iterable.iterator(), predicate); + } + + /** + * Returns the first element in {@code iterable} that satisfies the given predicate, or {@code + * defaultValue} if none found. Note that this can usually be handled more naturally using {@code + * tryFind(iterable, predicate).or(defaultValue)}. + * + *

{@code Stream} equivalent: {@code + * stream.filter(predicate).findFirst().orElse(defaultValue)} + * + * @since 7.0 + */ + // The signature we really want here is... + // + // @JointlyNullable T find( + // Iterable iterable, + // Predicate predicate, + // @JointlyNullable T defaultValue); + // + // ...where "@JointlyNullable" is similar to @PolyNull but slightly different: + // + // - @PolyNull means "@Nullable or @Nonnull" + // (That would be unsound for an input Iterable<@Nullable Foo>. So, if we wanted to use + // @PolyNull, we would have to restrict this method to non-null . But it has users who pass + // iterables with null elements.) + // + // - @JointlyNullable means "@Nullable or no annotation" + public static T find( + Iterable iterable, Predicate predicate, T defaultValue) { + return Iterators.find(iterable.iterator(), predicate, defaultValue); + } + + /** + * Returns an {@link Optional} containing the first element in {@code iterable} that satisfies the + * given predicate, if such an element exists. + * + *

Warning: avoid using a {@code predicate} that matches {@code null}. If {@code null} + * is matched in {@code iterable}, a NullPointerException will be thrown. + * + *

{@code Stream} equivalent: {@code stream.filter(predicate).findFirst()} + * + * @since 11.0 + */ + public static Optional tryFind(Iterable iterable, Predicate predicate) { + return Iterators.tryFind(iterable.iterator(), predicate); + } + + /** + * Returns the index in {@code iterable} of the first element that satisfies the provided {@code + * predicate}, or {@code -1} if the Iterable has no such elements. + * + *

More formally, returns the lowest index {@code i} such that {@code + * predicate.apply(Iterables.get(iterable, i))} returns {@code true}, or {@code -1} if there is no + * such index. + * + * @since 2.0 + */ + public static int indexOf( + Iterable iterable, Predicate predicate) { + return Iterators.indexOf(iterable.iterator(), predicate); + } + + /** + * Returns a view containing the result of applying {@code function} to each element of {@code + * fromIterable}. + * + *

The returned iterable's iterator supports {@code remove()} if {@code fromIterable}'s + * iterator does. After a successful {@code remove()} call, {@code fromIterable} no longer + * contains the corresponding element. + * + *

If the input {@code Iterable} is known to be a {@code List} or other {@code Collection}, + * consider {@link Lists#transform} and {@link Collections2#transform}. + * + *

{@code Stream} equivalent: {@link Stream#map} + */ + public static Iterable transform( + final Iterable fromIterable, final Function function) { + checkNotNull(fromIterable); + checkNotNull(function); + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.transform(fromIterable.iterator(), function); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + fromIterable.forEach((F f) -> action.accept(function.apply(f))); + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.map(fromIterable.spliterator(), function); + } + }; + } + + /** + * Returns the element at the specified position in an iterable. + * + *

{@code Stream} equivalent: {@code stream.skip(position).findFirst().get()} (throws + * {@code NoSuchElementException} if out of bounds) + * + * @param position position of the element to return + * @return the element at the specified position in {@code iterable} + * @throws IndexOutOfBoundsException if {@code position} is negative or greater than or equal to + * the size of {@code iterable} + */ + @ParametricNullness + public static T get(Iterable iterable, int position) { + checkNotNull(iterable); + return (iterable instanceof List) + ? ((List) iterable).get(position) + : Iterators.get(iterable.iterator(), position); + } + + /** + * Returns the element at the specified position in an iterable or a default value otherwise. + * + *

{@code Stream} equivalent: {@code + * stream.skip(position).findFirst().orElse(defaultValue)} (returns the default value if the index + * is out of bounds) + * + * @param position position of the element to return + * @param defaultValue the default value to return if {@code position} is greater than or equal to + * the size of the iterable + * @return the element at the specified position in {@code iterable} or {@code defaultValue} if + * {@code iterable} contains fewer than {@code position + 1} elements. + * @throws IndexOutOfBoundsException if {@code position} is negative + * @since 4.0 + */ + @ParametricNullness + public static T get( + Iterable iterable, int position, @ParametricNullness T defaultValue) { + checkNotNull(iterable); + Iterators.checkNonnegative(position); + if (iterable instanceof List) { + List list = Lists.cast(iterable); + return (position < list.size()) ? list.get(position) : defaultValue; + } else { + Iterator iterator = iterable.iterator(); + Iterators.advance(iterator, position); + return Iterators.getNext(iterator, defaultValue); + } + } + + /** + * Returns the first element in {@code iterable} or {@code defaultValue} if the iterable is empty. + * The {@link Iterators} analog to this method is {@link Iterators#getNext}. + * + *

If no default value is desired (and the caller instead wants a {@link + * NoSuchElementException} to be thrown), it is recommended that {@code + * iterable.iterator().next()} is used instead. + * + *

To get the only element in a single-element {@code Iterable}, consider using {@link + * #getOnlyElement(Iterable)} or {@link #getOnlyElement(Iterable, Object)} instead. + * + *

{@code Stream} equivalent: {@code stream.findFirst().orElse(defaultValue)} + * + * @param defaultValue the default value to return if the iterable is empty + * @return the first element of {@code iterable} or the default value + * @since 7.0 + */ + @ParametricNullness + public static T getFirst( + Iterable iterable, @ParametricNullness T defaultValue) { + return Iterators.getNext(iterable.iterator(), defaultValue); + } + + /** + * Returns the last element of {@code iterable}. If {@code iterable} is a {@link List} with {@link + * RandomAccess} support, then this operation is guaranteed to be {@code O(1)}. + * + *

{@code Stream} equivalent: {@link Streams#findLast Streams.findLast(stream).get()} + * + * @return the last element of {@code iterable} + * @throws NoSuchElementException if the iterable is empty + */ + @ParametricNullness + public static T getLast(Iterable iterable) { + // TODO(kevinb): Support a concurrently modified collection? + if (iterable instanceof List) { + List list = (List) iterable; + if (list.isEmpty()) { + throw new NoSuchElementException(); + } + return getLastInNonemptyList(list); + } + + return Iterators.getLast(iterable.iterator()); + } + + /** + * Returns the last element of {@code iterable} or {@code defaultValue} if the iterable is empty. + * If {@code iterable} is a {@link List} with {@link RandomAccess} support, then this operation is + * guaranteed to be {@code O(1)}. + * + *

{@code Stream} equivalent: {@code Streams.findLast(stream).orElse(defaultValue)} + * + * @param defaultValue the value to return if {@code iterable} is empty + * @return the last element of {@code iterable} or the default value + * @since 3.0 + */ + @ParametricNullness + public static T getLast( + Iterable iterable, @ParametricNullness T defaultValue) { + if (iterable instanceof Collection) { + Collection c = (Collection) iterable; + if (c.isEmpty()) { + return defaultValue; + } else if (iterable instanceof List) { + return getLastInNonemptyList(Lists.cast(iterable)); + } + } + + return Iterators.getLast(iterable.iterator(), defaultValue); + } + + @ParametricNullness + private static T getLastInNonemptyList(List list) { + return list.get(list.size() - 1); + } + + /** + * Returns a view of {@code iterable} that skips its first {@code numberToSkip} elements. If + * {@code iterable} contains fewer than {@code numberToSkip} elements, the returned iterable skips + * all of its elements. + * + *

Modifications to the underlying {@link Iterable} before a call to {@code iterator()} are + * reflected in the returned iterator. That is, the iterator skips the first {@code numberToSkip} + * elements that exist when the {@code Iterator} is created, not when {@code skip()} is called. + * + *

The returned iterable's iterator supports {@code remove()} if the iterator of the underlying + * iterable supports it. Note that it is not possible to delete the last skipped element by + * immediately calling {@code remove()} on that iterator, as the {@code Iterator} contract states + * that a call to {@code remove()} before a call to {@code next()} will throw an {@link + * IllegalStateException}. + * + *

{@code Stream} equivalent: {@link Stream#skip} + * + * @since 3.0 + */ + public static Iterable skip( + final Iterable iterable, final int numberToSkip) { + checkNotNull(iterable); + checkArgument(numberToSkip >= 0, "number to skip cannot be negative"); + + return new FluentIterable() { + @Override + public Iterator iterator() { + if (iterable instanceof List) { + final List list = (List) iterable; + int toSkip = Math.min(list.size(), numberToSkip); + return list.subList(toSkip, list.size()).iterator(); + } + final Iterator iterator = iterable.iterator(); + + Iterators.advance(iterator, numberToSkip); + + /* + * We can't just return the iterator because an immediate call to its + * remove() method would remove one of the skipped elements instead of + * throwing an IllegalStateException. + */ + return new Iterator() { + boolean atStart = true; + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + @ParametricNullness + public T next() { + T result = iterator.next(); + atStart = false; // not called if next() fails + return result; + } + + @Override + public void remove() { + checkRemove(!atStart); + iterator.remove(); + } + }; + } + + @Override + public Spliterator spliterator() { + if (iterable instanceof List) { + final List list = (List) iterable; + int toSkip = Math.min(list.size(), numberToSkip); + return list.subList(toSkip, list.size()).spliterator(); + } else { + return Streams.stream(iterable).skip(numberToSkip).spliterator(); + } + } + }; + } + + /** + * Returns a view of {@code iterable} containing its first {@code limitSize} elements. If {@code + * iterable} contains fewer than {@code limitSize} elements, the returned view contains all of its + * elements. The returned iterable's iterator supports {@code remove()} if {@code iterable}'s + * iterator does. + * + *

{@code Stream} equivalent: {@link Stream#limit} + * + * @param iterable the iterable to limit + * @param limitSize the maximum number of elements in the returned iterable + * @throws IllegalArgumentException if {@code limitSize} is negative + * @since 3.0 + */ + public static Iterable limit( + final Iterable iterable, final int limitSize) { + checkNotNull(iterable); + checkArgument(limitSize >= 0, "limit is negative"); + return new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.limit(iterable.iterator(), limitSize); + } + + @Override + public Spliterator spliterator() { + return Streams.stream(iterable).limit(limitSize).spliterator(); + } + }; + } + + /** + * Returns a view of the supplied iterable that wraps each generated {@link Iterator} through + * {@link Iterators#consumingIterator(Iterator)}. + * + *

Note: If {@code iterable} is a {@link Queue}, the returned iterable will instead use {@link + * Queue#isEmpty} and {@link Queue#remove()}, since {@link Queue}'s iteration order is undefined. + * Calling {@link Iterator#hasNext()} on a generated iterator from the returned iterable may cause + * an item to be immediately dequeued for return on a subsequent call to {@link Iterator#next()}. + * + *

Whether the input {@code iterable} is a {@link Queue} or not, the returned {@code Iterable} + * is not thread-safe. + * + * @param iterable the iterable to wrap + * @return a view of the supplied iterable that wraps each generated iterator through {@link + * Iterators#consumingIterator(Iterator)}; for queues, an iterable that generates iterators + * that return and consume the queue's elements in queue order + * @see Iterators#consumingIterator(Iterator) + * @since 2.0 + */ + public static Iterable consumingIterable(final Iterable iterable) { + checkNotNull(iterable); + + return new FluentIterable() { + @Override + public Iterator iterator() { + return (iterable instanceof Queue) + ? new ConsumingQueueIterator<>((Queue) iterable) + : Iterators.consumingIterator(iterable.iterator()); + } + + @Override + public String toString() { + return "Iterables.consumingIterable(...)"; + } + }; + } + + // Methods only in Iterables, not in Iterators + + /** + * Determines if the given iterable contains no elements. + * + *

There is no precise {@link Iterator} equivalent to this method, since one can only ask an + * iterator whether it has any elements remaining (which one does using {@link + * Iterator#hasNext}). + * + *

{@code Stream} equivalent: {@code !stream.findAny().isPresent()} + * + * @return {@code true} if the iterable contains no elements + */ + public static boolean isEmpty(Iterable iterable) { + if (iterable instanceof Collection) { + return ((Collection) iterable).isEmpty(); + } + return !iterable.iterator().hasNext(); + } + + /** + * Returns an iterable over the merged contents of all given {@code iterables}. Equivalent entries + * will not be de-duplicated. + * + *

Callers must ensure that the source {@code iterables} are in non-descending order as this + * method does not sort its input. + * + *

For any equivalent elements across all {@code iterables}, it is undefined which element is + * returned first. + * + * @since 11.0 + */ + public static Iterable mergeSorted( + final Iterable> iterables, + final Comparator comparator) { + checkNotNull(iterables, "iterables"); + checkNotNull(comparator, "comparator"); + Iterable iterable = + new FluentIterable() { + @Override + public Iterator iterator() { + return Iterators.mergeSorted( + Iterables.transform(iterables, Iterable::iterator), comparator); + } + }; + return new UnmodifiableIterable<>(iterable); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Iterators.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Iterators.java new file mode 100644 index 0000000000000..0f9c8b8315ded --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Iterators.java @@ -0,0 +1,1434 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.Queue; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Predicates.instanceOf; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * This class contains static utility methods that operate on or return objects of type {@link + * Iterator}. Except as noted, each method has a corresponding {@link Iterable}-based method in the + * {@link Iterables} class. + * + *

Performance notes: Unless otherwise noted, all of the iterators produced in this class + * are lazy, which means that they only advance the backing iteration when absolutely + * necessary. + * + *

See the Guava User Guide section on {@code + * Iterators}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Iterators { + private Iterators() {} + + /** + * Returns the empty iterator. + * + *

The {@link Iterable} equivalent of this method is {@link ImmutableSet#of()}. + */ + static UnmodifiableIterator emptyIterator() { + return emptyListIterator(); + } + + /** + * Returns the empty iterator. + * + *

The {@link Iterable} equivalent of this method is {@link ImmutableSet#of()}. + */ + // Casting to any type is safe since there are no actual elements. + @SuppressWarnings("unchecked") + static UnmodifiableListIterator emptyListIterator() { + return (UnmodifiableListIterator) ArrayItr.EMPTY; + } + + /** + * This is an enum singleton rather than an anonymous class so ProGuard can figure out it's only + * referenced by emptyModifiableIterator(). + */ + private enum EmptyModifiableIterator implements Iterator { + INSTANCE; + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Object next() { + throw new NoSuchElementException(); + } + + @Override + public void remove() { + checkRemove(false); + } + } + + /** + * Returns the empty {@code Iterator} that throws {@link IllegalStateException} instead of {@link + * UnsupportedOperationException} on a call to {@link Iterator#remove()}. + */ + // Casting to any type is safe since there are no actual elements. + @SuppressWarnings("unchecked") + static Iterator emptyModifiableIterator() { + return (Iterator) EmptyModifiableIterator.INSTANCE; + } + + /** Returns an unmodifiable view of {@code iterator}. */ + public static UnmodifiableIterator unmodifiableIterator( + Iterator iterator) { + checkNotNull(iterator); + if (iterator instanceof UnmodifiableIterator) { + @SuppressWarnings("unchecked") // Since it's unmodifiable, the covariant cast is safe + UnmodifiableIterator result = (UnmodifiableIterator) iterator; + return result; + } + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + @ParametricNullness + public T next() { + return iterator.next(); + } + }; + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static UnmodifiableIterator unmodifiableIterator( + UnmodifiableIterator iterator) { + return checkNotNull(iterator); + } + + /** + * Returns the number of elements remaining in {@code iterator}. The iterator will be left + * exhausted: its {@code hasNext()} method will return {@code false}. + */ + public static int size(Iterator iterator) { + long count = 0L; + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return Ints.saturatedCast(count); + } + + /** Returns {@code true} if {@code iterator} contains {@code element}. */ + public static boolean contains(Iterator iterator, Object element) { + if (element == null) { + while (iterator.hasNext()) { + if (iterator.next() == null) { + return true; + } + } + } else { + while (iterator.hasNext()) { + if (element.equals(iterator.next())) { + return true; + } + } + } + return false; + } + + /** + * Traverses an iterator and removes every element that belongs to the provided collection. The + * iterator will be left exhausted: its {@code hasNext()} method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param elementsToRemove the elements to remove + * @return {@code true} if any element was removed from {@code iterator} + */ + public static boolean removeAll(Iterator removeFrom, Collection elementsToRemove) { + checkNotNull(elementsToRemove); + boolean result = false; + while (removeFrom.hasNext()) { + if (elementsToRemove.contains(removeFrom.next())) { + removeFrom.remove(); + result = true; + } + } + return result; + } + + /** + * Removes every element that satisfies the provided predicate from the iterator. The iterator + * will be left exhausted: its {@code hasNext()} method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param predicate a predicate that determines whether an element should be removed + * @return {@code true} if any elements were removed from the iterator + * @since 2.0 + */ + public static boolean removeIf( + Iterator removeFrom, Predicate predicate) { + checkNotNull(predicate); + boolean modified = false; + while (removeFrom.hasNext()) { + if (predicate.apply(removeFrom.next())) { + removeFrom.remove(); + modified = true; + } + } + return modified; + } + + /** + * Traverses an iterator and removes every element that does not belong to the provided + * collection. The iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param elementsToRetain the elements to retain + * @return {@code true} if any element was removed from {@code iterator} + */ + public static boolean retainAll(Iterator removeFrom, Collection elementsToRetain) { + checkNotNull(elementsToRetain); + boolean result = false; + while (removeFrom.hasNext()) { + if (!elementsToRetain.contains(removeFrom.next())) { + removeFrom.remove(); + result = true; + } + } + return result; + } + + /** + * Determines whether two iterators contain equal elements in the same order. More specifically, + * this method returns {@code true} if {@code iterator1} and {@code iterator2} contain the same + * number of elements and every element of {@code iterator1} is equal to the corresponding element + * of {@code iterator2}. + * + *

Note that this will modify the supplied iterators, since they will have been advanced some + * number of elements forward. + */ + public static boolean elementsEqual(Iterator iterator1, Iterator iterator2) { + while (iterator1.hasNext()) { + if (!iterator2.hasNext()) { + return false; + } + Object o1 = iterator1.next(); + Object o2 = iterator2.next(); + if (!Objects.equal(o1, o2)) { + return false; + } + } + return !iterator2.hasNext(); + } + + /** + * Returns a string representation of {@code iterator}, with the format {@code [e1, e2, ..., en]}. + * The iterator will be left exhausted: its {@code hasNext()} method will return {@code false}. + */ + public static String toString(Iterator iterator) { + StringBuilder sb = new StringBuilder().append('['); + boolean first = true; + while (iterator.hasNext()) { + if (!first) { + sb.append(", "); + } + first = false; + sb.append(iterator.next()); + } + return sb.append(']').toString(); + } + + /** + * Returns the single element contained in {@code iterator}. + * + * @throws NoSuchElementException if the iterator is empty + * @throws IllegalArgumentException if the iterator contains multiple elements. The state of the + * iterator is unspecified. + */ + @ParametricNullness + public static T getOnlyElement(Iterator iterator) { + T first = iterator.next(); + if (!iterator.hasNext()) { + return first; + } + + StringBuilder sb = new StringBuilder().append("expected one element but was: <").append(first); + for (int i = 0; i < 4 && iterator.hasNext(); i++) { + sb.append(", ").append(iterator.next()); + } + if (iterator.hasNext()) { + sb.append(", ..."); + } + sb.append('>'); + + throw new IllegalArgumentException(sb.toString()); + } + + /** + * Returns the single element contained in {@code iterator}, or {@code defaultValue} if the + * iterator is empty. + * + * @throws IllegalArgumentException if the iterator contains multiple elements. The state of the + * iterator is unspecified. + */ + @ParametricNullness + public static T getOnlyElement( + Iterator iterator, @ParametricNullness T defaultValue) { + return iterator.hasNext() ? getOnlyElement(iterator) : defaultValue; + } + + /** + * Copies an iterator's elements into an array. The iterator will be left exhausted: its {@code + * hasNext()} method will return {@code false}. + * + * @param iterator the iterator to copy + * @param type the type of the elements + * @return a newly-allocated array into which all the elements of the iterator have been copied + */ + @GwtIncompatible // Array.newInstance(Class, int) + public static T[] toArray(Iterator iterator, Class type) { + List list = Lists.newArrayList(iterator); + return Iterables.toArray(list, type); + } + + /** + * Adds all elements in {@code iterator} to {@code collection}. The iterator will be left + * exhausted: its {@code hasNext()} method will return {@code false}. + * + * @return {@code true} if {@code collection} was modified as a result of this operation + */ + public static boolean addAll( + Collection addTo, Iterator iterator) { + checkNotNull(addTo); + checkNotNull(iterator); + boolean wasModified = false; + while (iterator.hasNext()) { + wasModified |= addTo.add(iterator.next()); + } + return wasModified; + } + + /** + * Returns the number of elements in the specified iterator that equal the specified object. The + * iterator will be left exhausted: its {@code hasNext()} method will return {@code false}. + * + * @see Collections#frequency + */ + public static int frequency(Iterator iterator, Object element) { + int count = 0; + while (contains(iterator, element)) { + // Since it lives in the same class, we know contains gets to the element and then stops, + // though that isn't currently publicly documented. + count++; + } + return count; + } + + /** + * Returns an iterator that cycles indefinitely over the elements of {@code iterable}. + * + *

The returned iterator supports {@code remove()} if the provided iterator does. After {@code + * remove()} is called, subsequent cycles omit the removed element, which is no longer in {@code + * iterable}. The iterator's {@code hasNext()} method returns {@code true} until {@code iterable} + * is empty. + * + *

Warning: Typical uses of the resulting iterator may produce an infinite loop. You + * should use an explicit {@code break} or be certain that you will eventually remove all the + * elements. + */ + public static Iterator cycle(Iterable iterable) { + checkNotNull(iterable); + return new Iterator() { + Iterator iterator = emptyModifiableIterator(); + + @Override + public boolean hasNext() { + /* + * Don't store a new Iterator until we know the user can't remove() the last returned + * element anymore. Otherwise, when we remove from the old iterator, we may be invalidating + * the new one. The result is a ConcurrentModificationException or other bad behavior. + * + * (If we decide that we really, really hate allocating two Iterators per cycle instead of + * one, we can optimistically store the new Iterator and then be willing to throw it out if + * the user calls remove().) + */ + return iterator.hasNext() || iterable.iterator().hasNext(); + } + + @Override + @ParametricNullness + public T next() { + if (!iterator.hasNext()) { + iterator = iterable.iterator(); + if (!iterator.hasNext()) { + throw new NoSuchElementException(); + } + } + return iterator.next(); + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + + /** + * Returns an iterator that cycles indefinitely over the provided elements. + * + *

The returned iterator supports {@code remove()}. After {@code remove()} is called, + * subsequent cycles omit the removed element, but {@code elements} does not change. The + * iterator's {@code hasNext()} method returns {@code true} until all of the original elements + * have been removed. + * + *

Warning: Typical uses of the resulting iterator may produce an infinite loop. You + * should use an explicit {@code break} or be certain that you will eventually remove all the + * elements. + */ + @SafeVarargs + public static Iterator cycle(T... elements) { + return cycle(Lists.newArrayList(elements)); + } + + /** + * Returns an Iterator that walks the specified array, nulling out elements behind it. This can + * avoid memory leaks when an element is no longer necessary. + * + *

This method accepts an array with element type {@code @Nullable T}, but callers must pass an + * array whose contents are initially non-null. The {@code @Nullable} annotation indicates that + * this method will write nulls into the array during iteration. + * + *

This is mainly just to avoid the intermediate ArrayDeque in ConsumingQueueIterator. + */ + private static > Iterator consumingForArray(I... elements) { + return new UnmodifiableIterator() { + int index = 0; + + @Override + public boolean hasNext() { + return index < elements.length; + } + + @Override + public I next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + /* + * requireNonNull is safe because our callers always pass non-null arguments. Each element + * of the array becomes null only when we iterate past it and then clear it. + */ + I result = requireNonNull(elements[index]); + elements[index] = null; + index++; + return result; + } + }; + } + + /** + * Combines two iterators into a single iterator. The returned iterator iterates across the + * elements in {@code a}, followed by the elements in {@code b}. The source iterators are not + * polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding input iterator + * supports it. + */ + public static Iterator concat( + Iterator a, Iterator b) { + checkNotNull(a); + checkNotNull(b); + return concat(consumingForArray(a, b)); + } + + /** + * Combines three iterators into a single iterator. The returned iterator iterates across the + * elements in {@code a}, followed by the elements in {@code b}, followed by the elements in + * {@code c}. The source iterators are not polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding input iterator + * supports it. + */ + public static Iterator concat( + Iterator a, Iterator b, Iterator c) { + checkNotNull(a); + checkNotNull(b); + checkNotNull(c); + return concat(consumingForArray(a, b, c)); + } + + /** + * Combines four iterators into a single iterator. The returned iterator iterates across the + * elements in {@code a}, followed by the elements in {@code b}, followed by the elements in + * {@code c}, followed by the elements in {@code d}. The source iterators are not polled until + * necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding input iterator + * supports it. + */ + public static Iterator concat( + Iterator a, + Iterator b, + Iterator c, + Iterator d) { + checkNotNull(a); + checkNotNull(b); + checkNotNull(c); + checkNotNull(d); + return concat(consumingForArray(a, b, c, d)); + } + + /** + * Combines multiple iterators into a single iterator. The returned iterator iterates across the + * elements of each iterator in {@code inputs}. The input iterators are not polled until + * necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding input iterator + * supports it. + * + * @throws NullPointerException if any of the provided iterators is null + */ + public static Iterator concat(Iterator... inputs) { + return concatNoDefensiveCopy(Arrays.copyOf(inputs, inputs.length)); + } + + /** + * Combines multiple iterators into a single iterator. The returned iterator iterates across the + * elements of each iterator in {@code inputs}. The input iterators are not polled until + * necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding input iterator + * supports it. The methods of the returned iterator may throw {@code NullPointerException} if any + * of the input iterators is null. + */ + public static Iterator concat( + Iterator> inputs) { + return new ConcatenatedIterator<>(inputs); + } + + /** Concats a varargs array of iterators without making a defensive copy of the array. */ + static Iterator concatNoDefensiveCopy(Iterator... inputs) { + for (Iterator input : checkNotNull(inputs)) { + checkNotNull(input); + } + return concat(consumingForArray(inputs)); + } + + /** + * Divides an iterator into unmodifiable sublists of the given size (the final list may be + * smaller). For example, partitioning an iterator containing {@code [a, b, c, d, e]} with a + * partition size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer iterator containing two + * inner lists of three and two elements, all in the original order. + * + *

The returned lists implement {@link java.util.RandomAccess}. + * + *

Note: The current implementation eagerly allocates storage for {@code size} elements. + * As a consequence, passing values like {@code Integer.MAX_VALUE} can lead to {@link + * OutOfMemoryError}. + * + * @param iterator the iterator to return a partitioned view of + * @param size the desired size of each partition (the last may be smaller) + * @return an iterator of immutable lists containing the elements of {@code iterator} divided into + * partitions + * @throws IllegalArgumentException if {@code size} is nonpositive + */ + public static UnmodifiableIterator> partition( + Iterator iterator, int size) { + return partitionImpl(iterator, size, false); + } + + /** + * Divides an iterator into unmodifiable sublists of the given size, padding the final iterator + * with null values if necessary. For example, partitioning an iterator containing {@code [a, b, + * c, d, e]} with a partition size of 3 yields {@code [[a, b, c], [d, e, null]]} -- an outer + * iterator containing two inner lists of three elements each, all in the original order. + * + *

The returned lists implement {@link java.util.RandomAccess}. + * + * @param iterator the iterator to return a partitioned view of + * @param size the desired size of each partition + * @return an iterator of immutable lists containing the elements of {@code iterator} divided into + * partitions (the final iterable may have trailing null elements) + * @throws IllegalArgumentException if {@code size} is nonpositive + */ + public static UnmodifiableIterator> paddedPartition( + Iterator iterator, int size) { + return partitionImpl(iterator, size, true); + } + + private static UnmodifiableIterator> partitionImpl( + Iterator iterator, int size, boolean pad) { + checkNotNull(iterator); + checkArgument(size > 0); + return new UnmodifiableIterator>() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public List next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + @SuppressWarnings("unchecked") // we only put Ts in it + T[] array = (T[]) new Object[size]; + int count = 0; + for (; count < size && iterator.hasNext(); count++) { + array[count] = iterator.next(); + } + for (int i = count; i < size; i++) { + array[i] = null; // for GWT + } + + List list = Collections.unmodifiableList(Arrays.asList(array)); + // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. + if (pad || count == size) { + return list; + } else { + return list.subList(0, count); + } + } + }; + } + + /** + * Returns a view of {@code unfiltered} containing all elements that satisfy the input predicate + * {@code retainIfTrue}. + */ + public static UnmodifiableIterator filter( + Iterator unfiltered, Predicate retainIfTrue) { + checkNotNull(unfiltered); + checkNotNull(retainIfTrue); + return new AbstractIterator() { + @Override + protected T computeNext() { + while (unfiltered.hasNext()) { + T element = unfiltered.next(); + if (retainIfTrue.apply(element)) { + return element; + } + } + return endOfData(); + } + }; + } + + /** + * Returns a view of {@code unfiltered} containing all elements that are of the type {@code + * desiredType}. + */ + @SuppressWarnings("unchecked") // can cast to because non-Ts are removed + @GwtIncompatible // Class.isInstance + public static UnmodifiableIterator filter(Iterator unfiltered, Class desiredType) { + return (UnmodifiableIterator) filter(unfiltered, instanceOf(desiredType)); + } + + /** + * Returns {@code true} if one or more elements returned by {@code iterator} satisfy the given + * predicate. + */ + public static boolean any( + Iterator iterator, Predicate predicate) { + return indexOf(iterator, predicate) != -1; + } + + /** + * Returns {@code true} if every element returned by {@code iterator} satisfies the given + * predicate. If {@code iterator} is empty, {@code true} is returned. + */ + public static boolean all( + Iterator iterator, Predicate predicate) { + checkNotNull(predicate); + while (iterator.hasNext()) { + T element = iterator.next(); + if (!predicate.apply(element)) { + return false; + } + } + return true; + } + + /** + * Returns the first element in {@code iterator} that satisfies the given predicate; use this + * method only when such an element is known to exist. If no such element is found, the iterator + * will be left exhausted: its {@code hasNext()} method will return {@code false}. If it is + * possible that no element will match, use {@link #tryFind} or {@link #find(Iterator, + * Predicate, Object)} instead. + * + * @throws NoSuchElementException if no element in {@code iterator} matches the given predicate + */ + @ParametricNullness + public static T find(Iterator iterator, Predicate predicate) { + checkNotNull(iterator); + checkNotNull(predicate); + while (iterator.hasNext()) { + T t = iterator.next(); + if (predicate.apply(t)) { + return t; + } + } + throw new NoSuchElementException(); + } + + /** + * Returns the first element in {@code iterator} that satisfies the given predicate. If no such + * element is found, {@code defaultValue} will be returned from this method and the iterator will + * be left exhausted: its {@code hasNext()} method will return {@code false}. Note that this can + * usually be handled more naturally using {@code tryFind(iterator, predicate).or(defaultValue)}. + * + * @since 7.0 + */ + // For discussion of this signature, see the corresponding overload of *Iterables*.find. + public static T find( + Iterator iterator, Predicate predicate, T defaultValue) { + checkNotNull(iterator); + checkNotNull(predicate); + while (iterator.hasNext()) { + T t = iterator.next(); + if (predicate.apply(t)) { + return t; + } + } + return defaultValue; + } + + /** + * Returns an {@link Optional} containing the first element in {@code iterator} that satisfies the + * given predicate, if such an element exists. If no such element is found, an empty {@link + * Optional} will be returned from this method and the iterator will be left exhausted: its {@code + * hasNext()} method will return {@code false}. + * + *

Warning: avoid using a {@code predicate} that matches {@code null}. If {@code null} + * is matched in {@code iterator}, a NullPointerException will be thrown. + * + * @since 11.0 + */ + public static Optional tryFind(Iterator iterator, Predicate predicate) { + checkNotNull(iterator); + checkNotNull(predicate); + while (iterator.hasNext()) { + T t = iterator.next(); + if (predicate.apply(t)) { + return Optional.of(t); + } + } + return Optional.absent(); + } + + /** + * Returns the index in {@code iterator} of the first element that satisfies the provided {@code + * predicate}, or {@code -1} if the Iterator has no such elements. + * + *

More formally, returns the lowest index {@code i} such that {@code + * predicate.apply(Iterators.get(iterator, i))} returns {@code true}, or {@code -1} if there is no + * such index. + * + *

If -1 is returned, the iterator will be left exhausted: its {@code hasNext()} method will + * return {@code false}. Otherwise, the iterator will be set to the element which satisfies the + * {@code predicate}. + * + * @since 2.0 + */ + public static int indexOf( + Iterator iterator, Predicate predicate) { + checkNotNull(predicate, "predicate"); + for (int i = 0; iterator.hasNext(); i++) { + T current = iterator.next(); + if (predicate.apply(current)) { + return i; + } + } + return -1; + } + + /** + * Returns a view containing the result of applying {@code function} to each element of {@code + * fromIterator}. + * + *

The returned iterator supports {@code remove()} if {@code fromIterator} does. After a + * successful {@code remove()} call, {@code fromIterator} no longer contains the corresponding + * element. + */ + public static Iterator transform( + Iterator fromIterator, Function function) { + checkNotNull(function); + return new TransformedIterator(fromIterator) { + @ParametricNullness + @Override + T transform(@ParametricNullness F from) { + return function.apply(from); + } + }; + } + + /** + * Advances {@code iterator} {@code position + 1} times, returning the element at the {@code + * position}th position. + * + * @param position position of the element to return + * @return the element at the specified position in {@code iterator} + * @throws IndexOutOfBoundsException if {@code position} is negative or greater than or equal to + * the number of elements remaining in {@code iterator} + */ + @ParametricNullness + public static T get(Iterator iterator, int position) { + checkNonnegative(position); + int skipped = advance(iterator, position); + if (!iterator.hasNext()) { + throw new IndexOutOfBoundsException( + "position (" + + position + + ") must be less than the number of elements that remained (" + + skipped + + ")"); + } + return iterator.next(); + } + + /** + * Advances {@code iterator} {@code position + 1} times, returning the element at the {@code + * position}th position or {@code defaultValue} otherwise. + * + * @param position position of the element to return + * @param defaultValue the default value to return if the iterator is empty or if {@code position} + * is greater than the number of elements remaining in {@code iterator} + * @return the element at the specified position in {@code iterator} or {@code defaultValue} if + * {@code iterator} produces fewer than {@code position + 1} elements. + * @throws IndexOutOfBoundsException if {@code position} is negative + * @since 4.0 + */ + @ParametricNullness + public static T get( + Iterator iterator, int position, @ParametricNullness T defaultValue) { + checkNonnegative(position); + advance(iterator, position); + return getNext(iterator, defaultValue); + } + + static void checkNonnegative(int position) { + if (position < 0) { + throw new IndexOutOfBoundsException("position (" + position + ") must not be negative"); + } + } + + /** + * Returns the next element in {@code iterator} or {@code defaultValue} if the iterator is empty. + * The {@link Iterables} analog to this method is {@link Iterables#getFirst}. + * + * @param defaultValue the default value to return if the iterator is empty + * @return the next element of {@code iterator} or the default value + * @since 7.0 + */ + @ParametricNullness + public static T getNext( + Iterator iterator, @ParametricNullness T defaultValue) { + return iterator.hasNext() ? iterator.next() : defaultValue; + } + + /** + * Advances {@code iterator} to the end, returning the last element. + * + * @return the last element of {@code iterator} + * @throws NoSuchElementException if the iterator is empty + */ + @ParametricNullness + public static T getLast(Iterator iterator) { + while (true) { + T current = iterator.next(); + if (!iterator.hasNext()) { + return current; + } + } + } + + /** + * Advances {@code iterator} to the end, returning the last element or {@code defaultValue} if the + * iterator is empty. + * + * @param defaultValue the default value to return if the iterator is empty + * @return the last element of {@code iterator} + * @since 3.0 + */ + @ParametricNullness + public static T getLast( + Iterator iterator, @ParametricNullness T defaultValue) { + return iterator.hasNext() ? getLast(iterator) : defaultValue; + } + + /** + * Calls {@code next()} on {@code iterator}, either {@code numberToAdvance} times or until {@code + * hasNext()} returns {@code false}, whichever comes first. + * + * @return the number of elements the iterator was advanced + * @since 13.0 (since 3.0 as {@code Iterators.skip}) + */ + public static int advance(Iterator iterator, int numberToAdvance) { + checkNotNull(iterator); + checkArgument(numberToAdvance >= 0, "numberToAdvance must be nonnegative"); + + int i; + for (i = 0; i < numberToAdvance && iterator.hasNext(); i++) { + iterator.next(); + } + return i; + } + + /** + * Returns a view containing the first {@code limitSize} elements of {@code iterator}. If {@code + * iterator} contains fewer than {@code limitSize} elements, the returned view contains all of its + * elements. The returned iterator supports {@code remove()} if {@code iterator} does. + * + * @param iterator the iterator to limit + * @param limitSize the maximum number of elements in the returned iterator + * @throws IllegalArgumentException if {@code limitSize} is negative + * @since 3.0 + */ + public static Iterator limit(Iterator iterator, int limitSize) { + checkNotNull(iterator); + checkArgument(limitSize >= 0, "limit is negative"); + return new Iterator() { + private int count; + + @Override + public boolean hasNext() { + return count < limitSize && iterator.hasNext(); + } + + @Override + @ParametricNullness + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + count++; + return iterator.next(); + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + + /** + * Returns a view of the supplied {@code iterator} that removes each element from the supplied + * {@code iterator} as it is returned. + * + *

The provided iterator must support {@link Iterator#remove()} or else the returned iterator + * will fail on the first call to {@code next}. The returned {@link Iterator} is also not + * thread-safe. + * + * @param iterator the iterator to remove and return elements from + * @return an iterator that removes and returns elements from the supplied iterator + * @since 2.0 + */ + public static Iterator consumingIterator(Iterator iterator) { + checkNotNull(iterator); + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + @ParametricNullness + public T next() { + T next = iterator.next(); + iterator.remove(); + return next; + } + + @Override + public String toString() { + return "Iterators.consumingIterator(...)"; + } + }; + } + + /** + * Deletes and returns the next value from the iterator, or returns {@code null} if there is no + * such value. + */ + static T pollNext(Iterator iterator) { + if (iterator.hasNext()) { + T result = iterator.next(); + iterator.remove(); + return result; + } else { + return null; + } + } + + // Methods only in Iterators, not in Iterables + + /** Clears the iterator using its remove method. */ + static void clear(Iterator iterator) { + checkNotNull(iterator); + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + } + + /** + * Returns an iterator containing the elements of {@code array} in order. The returned iterator is + * a view of the array; subsequent changes to the array will be reflected in the iterator. + * + *

Note: It is often preferable to represent your data using a collection type, for + * example using {@link Arrays#asList(Object[])}, making this method unnecessary. + * + *

The {@code Iterable} equivalent of this method is either {@link Arrays#asList(Object[])}, + * {@link ImmutableList#copyOf(Object[])}}, or {@link ImmutableList#of}. + */ + @SafeVarargs + public static UnmodifiableIterator forArray(T... array) { + return forArray(array, 0, array.length, 0); + } + + /** + * Returns a list iterator containing the elements in the specified range of {@code array} in + * order, starting at the specified index. + * + *

The {@code Iterable} equivalent of this method is {@code + * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}. + */ + static UnmodifiableListIterator forArray( + T[] array, int offset, int length, int index) { + checkArgument(length >= 0); + int end = offset + length; + + // Technically we should give a slightly more descriptive error on overflow + Preconditions.checkPositionIndexes(offset, end, array.length); + Preconditions.checkPositionIndex(index, length); + if (length == 0) { + return emptyListIterator(); + } + return new ArrayItr<>(array, offset, length, index); + } + + private static final class ArrayItr extends AbstractIndexedListIterator { + static final UnmodifiableListIterator EMPTY = new ArrayItr<>(new Object[0], 0, 0, 0); + + private final T[] array; + private final int offset; + + ArrayItr(T[] array, int offset, int length, int index) { + super(length, index); + this.array = array; + this.offset = offset; + } + + @Override + @ParametricNullness + protected T get(int index) { + return array[offset + index]; + } + } + + /** + * Returns an iterator containing only {@code value}. + * + *

The {@link Iterable} equivalent of this method is {@link Collections#singleton}. + */ + public static UnmodifiableIterator singletonIterator( + @ParametricNullness T value) { + return new UnmodifiableIterator() { + boolean done; + + @Override + public boolean hasNext() { + return !done; + } + + @Override + @ParametricNullness + public T next() { + if (done) { + throw new NoSuchElementException(); + } + done = true; + return value; + } + }; + } + + /** + * Adapts an {@code Enumeration} to the {@code Iterator} interface. + * + *

This method has no equivalent in {@link Iterables} because viewing an {@code Enumeration} as + * an {@code Iterable} is impossible. However, the contents can be copied into a collection + * using {@link Collections#list}. + * + *

Java 9 users: use {@code enumeration.asIterator()} instead, unless it is important to + * return an {@code UnmodifiableIterator} instead of a plain {@code Iterator}. + */ + public static UnmodifiableIterator forEnumeration( + Enumeration enumeration) { + checkNotNull(enumeration); + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return enumeration.hasMoreElements(); + } + + @Override + @ParametricNullness + public T next() { + return enumeration.nextElement(); + } + }; + } + + /** + * Adapts an {@code Iterator} to the {@code Enumeration} interface. + * + *

The {@code Iterable} equivalent of this method is either {@link Collections#enumeration} (if + * you have a {@link Collection}), or {@code Iterators.asEnumeration(collection.iterator())}. + */ + public static Enumeration asEnumeration(Iterator iterator) { + checkNotNull(iterator); + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + @Override + @ParametricNullness + public T nextElement() { + return iterator.next(); + } + }; + } + + /** Implementation of PeekingIterator that avoids peeking unless necessary. */ + private static class PeekingImpl implements PeekingIterator { + + private final Iterator iterator; + private boolean hasPeeked; + private E peekedElement; + + public PeekingImpl(Iterator iterator) { + this.iterator = checkNotNull(iterator); + } + + @Override + public boolean hasNext() { + return hasPeeked || iterator.hasNext(); + } + + @Override + @ParametricNullness + public E next() { + if (!hasPeeked) { + return iterator.next(); + } + // The cast is safe because of the hasPeeked check. + E result = uncheckedCastNullableTToT(peekedElement); + hasPeeked = false; + peekedElement = null; + return result; + } + + @Override + public void remove() { + checkState(!hasPeeked, "Can't remove after you've peeked at next"); + iterator.remove(); + } + + @Override + @ParametricNullness + public E peek() { + if (!hasPeeked) { + peekedElement = iterator.next(); + hasPeeked = true; + } + // The cast is safe because of the hasPeeked check. + return uncheckedCastNullableTToT(peekedElement); + } + } + + /** + * Returns a {@code PeekingIterator} backed by the given iterator. + * + *

Calls to the {@code peek} method with no intervening calls to {@code next} do not affect the + * iteration, and hence return the same object each time. A subsequent call to {@code next} is + * guaranteed to return the same object again. For example: + * + *

{@code
+   * PeekingIterator peekingIterator =
+   *     Iterators.peekingIterator(Iterators.forArray("a", "b"));
+   * String a1 = peekingIterator.peek(); // returns "a"
+   * String a2 = peekingIterator.peek(); // also returns "a"
+   * String a3 = peekingIterator.next(); // also returns "a"
+   * }
+ * + *

Any structural changes to the underlying iteration (aside from those performed by the + * iterator's own {@link PeekingIterator#remove()} method) will leave the iterator in an undefined + * state. + * + *

The returned iterator does not support removal after peeking, as explained by {@link + * PeekingIterator#remove()}. + * + *

Note: If the given iterator is already a {@code PeekingIterator}, it might be + * returned to the caller, although this is neither guaranteed to occur nor required to be + * consistent. For example, this method might choose to pass through recognized + * implementations of {@code PeekingIterator} when the behavior of the implementation is known to + * meet the contract guaranteed by this method. + * + *

There is no {@link Iterable} equivalent to this method, so use this method to wrap each + * individual iterator as it is generated. + * + * @param iterator the backing iterator. The {@link PeekingIterator} assumes ownership of this + * iterator, so users should cease making direct calls to it after calling this method. + * @return a peeking iterator backed by that iterator. Apart from the additional {@link + * PeekingIterator#peek()} method, this iterator behaves exactly the same as {@code iterator}. + */ + public static PeekingIterator peekingIterator( + Iterator iterator) { + if (iterator instanceof PeekingImpl) { + // Safe to cast to because PeekingImpl only uses T + // covariantly (and cannot be subclassed to add non-covariant uses). + @SuppressWarnings("unchecked") + PeekingImpl peeking = (PeekingImpl) iterator; + return peeking; + } + return new PeekingImpl<>(iterator); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static PeekingIterator peekingIterator(PeekingIterator iterator) { + return checkNotNull(iterator); + } + + /** + * Returns an iterator over the merged contents of all given {@code iterators}, traversing every + * element of the input iterators. Equivalent entries will not be de-duplicated. + * + *

Callers must ensure that the source {@code iterators} are in non-descending order as this + * method does not sort its input. + * + *

For any equivalent elements across all {@code iterators}, it is undefined which element is + * returned first. + * + * @since 11.0 + */ + public static UnmodifiableIterator mergeSorted( + Iterable> iterators, Comparator comparator) { + checkNotNull(iterators, "iterators"); + checkNotNull(comparator, "comparator"); + + return new MergingIterator<>(iterators, comparator); + } + + /** + * An iterator that performs a lazy N-way merge, calculating the next value each time the iterator + * is polled. This amortizes the sorting cost over the iteration and requires less memory than + * sorting all elements at once. + * + *

Retrieving a single element takes approximately O(log(M)) time, where M is the number of + * iterators. (Retrieving all elements takes approximately O(N*log(M)) time, where N is the total + * number of elements.) + */ + private static class MergingIterator extends UnmodifiableIterator { + final Queue> queue; + + public MergingIterator( + Iterable> iterators, Comparator itemComparator) { + // A comparator that's used by the heap, allowing the heap + // to be sorted based on the top of each iterator. + Comparator> heapComparator = + (PeekingIterator o1, PeekingIterator o2) -> + itemComparator.compare(o1.peek(), o2.peek()); + + queue = new PriorityQueue<>(2, heapComparator); + + for (Iterator iterator : iterators) { + if (iterator.hasNext()) { + queue.add(Iterators.peekingIterator(iterator)); + } + } + } + + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + @ParametricNullness + public T next() { + PeekingIterator nextIter = queue.remove(); + T next = nextIter.next(); + if (nextIter.hasNext()) { + queue.add(nextIter); + } + return next; + } + } + + private static class ConcatenatedIterator implements Iterator { + /* The last iterator to return an element. Calls to remove() go to this iterator. */ + private Iterator toRemove; + + /* The iterator currently returning elements. */ + private Iterator iterator; + + /* + * We track the "meta iterators," the iterators-of-iterators, below. Usually, topMetaIterator + * is the only one in use, but if we encounter nested concatenations, we start a deque of + * meta-iterators rather than letting the nesting get arbitrarily deep. This keeps each + * operation O(1). + */ + + private Iterator> topMetaIterator; + + // Only becomes nonnull if we encounter nested concatenations. + private Deque>> metaIterators; + + ConcatenatedIterator(Iterator> metaIterator) { + iterator = emptyIterator(); + topMetaIterator = checkNotNull(metaIterator); + } + + // Returns a nonempty meta-iterator or, if all meta-iterators are empty, null. + private Iterator> getTopMetaIterator() { + while (topMetaIterator == null || !topMetaIterator.hasNext()) { + if (metaIterators != null && !metaIterators.isEmpty()) { + topMetaIterator = metaIterators.removeFirst(); + } else { + return null; + } + } + return topMetaIterator; + } + + @Override + public boolean hasNext() { + while (!checkNotNull(iterator).hasNext()) { + // this weird checkNotNull positioning appears required by our tests, which expect + // both hasNext and next to throw NPE if an input iterator is null. + + topMetaIterator = getTopMetaIterator(); + if (topMetaIterator == null) { + return false; + } + + iterator = topMetaIterator.next(); + + if (iterator instanceof ConcatenatedIterator) { + // Instead of taking linear time in the number of nested concatenations, unpack + // them into the queue + @SuppressWarnings("unchecked") + ConcatenatedIterator topConcat = (ConcatenatedIterator) iterator; + iterator = topConcat.iterator; + + // topConcat.topMetaIterator, then topConcat.metaIterators, then this.topMetaIterator, + // then this.metaIterators + + if (this.metaIterators == null) { + this.metaIterators = new ArrayDeque<>(); + } + this.metaIterators.addFirst(this.topMetaIterator); + if (topConcat.metaIterators != null) { + while (!topConcat.metaIterators.isEmpty()) { + this.metaIterators.addFirst(topConcat.metaIterators.removeLast()); + } + } + this.topMetaIterator = topConcat.topMetaIterator; + } + } + return true; + } + + @Override + @ParametricNullness + public T next() { + if (hasNext()) { + toRemove = iterator; + return iterator.next(); + } else { + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + if (toRemove == null) { + throw new IllegalStateException("no calls to next() since the last call to remove()"); + } + toRemove.remove(); + toRemove = null; + } + } + + /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ + static ListIterator cast(Iterator iterator) { + return (ListIterator) iterator; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableBiMap.java new file mode 100644 index 0000000000000..87382bb2fa45a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableBiMap.java @@ -0,0 +1,125 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +/** + * Implementation of ImmutableBiMap backed by a pair of JDK HashMaps, which have smartness + * protecting against hash flooding. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class JdkBackedImmutableBiMap extends ImmutableBiMap { + @VisibleForTesting + static ImmutableBiMap create(int n, Entry[] entryArray) { + Map forwardDelegate = Maps.newHashMapWithExpectedSize(n); + Map backwardDelegate = Maps.newHashMapWithExpectedSize(n); + for (int i = 0; i < n; i++) { + // requireNonNull is safe because the first `n` elements have been filled in. + Entry e = RegularImmutableMap.makeImmutable(requireNonNull(entryArray[i])); + entryArray[i] = e; + V oldValue = forwardDelegate.putIfAbsent(e.getKey(), e.getValue()); + if (oldValue != null) { + throw conflictException("key", e.getKey() + "=" + oldValue, entryArray[i]); + } + K oldKey = backwardDelegate.putIfAbsent(e.getValue(), e.getKey()); + if (oldKey != null) { + throw conflictException("value", oldKey + "=" + e.getValue(), entryArray[i]); + } + } + ImmutableList> entryList = ImmutableList.asImmutableList(entryArray, n); + return new JdkBackedImmutableBiMap<>(entryList, forwardDelegate, backwardDelegate); + } + + private final transient ImmutableList> entries; + private final Map forwardDelegate; + private final Map backwardDelegate; + + private JdkBackedImmutableBiMap( + ImmutableList> entries, Map forwardDelegate, Map backwardDelegate) { + this.entries = entries; + this.forwardDelegate = forwardDelegate; + this.backwardDelegate = backwardDelegate; + } + + @Override + public int size() { + return entries.size(); + } + + private transient JdkBackedImmutableBiMap inverse; + + @Override + public ImmutableBiMap inverse() { + JdkBackedImmutableBiMap result = inverse; + if (result == null) { + inverse = + result = + new JdkBackedImmutableBiMap<>( + new InverseEntries(), backwardDelegate, forwardDelegate); + result.inverse = this; + } + return result; + } + + private final class InverseEntries extends ImmutableList> { + @Override + public Entry get(int index) { + Entry entry = entries.get(index); + return Maps.immutableEntry(entry.getValue(), entry.getKey()); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int size() { + return entries.size(); + } + } + + @Override + public V get(Object key) { + return forwardDelegate.get(key); + } + + @Override + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet.RegularEntrySet<>(this, entries); + } + + @Override + ImmutableSet createKeySet() { + return new ImmutableMapKeySet<>(this); + } + + @Override + boolean isPartialView() { + return false; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableMap.java new file mode 100644 index 0000000000000..e0b63c32731db --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableMap.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.RegularImmutableMap.makeImmutable; + +/** + * Implementation of ImmutableMap backed by a JDK HashMap, which has smartness protecting against + * hash flooding. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class JdkBackedImmutableMap extends ImmutableMap { + /** + * Creates an {@code ImmutableMap} backed by a JDK HashMap. Used when probable hash flooding is + * detected. This implementation may replace the entries in entryArray with its own entry objects + * (though they will have the same key/value contents), and will take ownership of entryArray. + */ + static ImmutableMap create( + int n, Entry[] entryArray, boolean throwIfDuplicateKeys) { + Map delegateMap = Maps.newHashMapWithExpectedSize(n); + // If duplicates are allowed, this map will track the last value for each duplicated key. + // A second pass will retain only the first entry for that key, but with this last value. The + // value will then be replaced by null, signaling that later entries with the same key should + // be deleted. + Map duplicates = null; + int dupCount = 0; + for (int i = 0; i < n; i++) { + // requireNonNull is safe because the first `n` elements have been filled in. + entryArray[i] = makeImmutable(requireNonNull(entryArray[i])); + K key = entryArray[i].getKey(); + V value = entryArray[i].getValue(); + V oldValue = delegateMap.put(key, value); + if (oldValue != null) { + if (throwIfDuplicateKeys) { + throw conflictException("key", entryArray[i], entryArray[i].getKey() + "=" + oldValue); + } + if (duplicates == null) { + duplicates = new HashMap<>(); + } + duplicates.put(key, value); + dupCount++; + } + } + if (duplicates != null) { + @SuppressWarnings({"rawtypes", "unchecked"}) + Entry[] newEntryArray = new Entry[n - dupCount]; + for (int inI = 0, outI = 0; inI < n; inI++) { + Entry entry = requireNonNull(entryArray[inI]); + K key = entry.getKey(); + if (duplicates.containsKey(key)) { + V value = duplicates.get(key); + if (value == null) { + continue; // delete this duplicate + } + entry = new ImmutableMapEntry<>(key, value); + duplicates.put(key, null); + } + newEntryArray[outI++] = entry; + } + entryArray = newEntryArray; + } + return new JdkBackedImmutableMap<>(delegateMap, ImmutableList.asImmutableList(entryArray, n)); + } + + private final transient Map delegateMap; + private final transient ImmutableList> entries; + + JdkBackedImmutableMap(Map delegateMap, ImmutableList> entries) { + this.delegateMap = delegateMap; + this.entries = entries; + } + + @Override + public int size() { + return entries.size(); + } + + @Override + public V get(Object key) { + return delegateMap.get(key); + } + + @Override + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet.RegularEntrySet<>(this, entries); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + entries.forEach(e -> action.accept(e.getKey(), e.getValue())); + } + + @Override + ImmutableSet createKeySet() { + return new ImmutableMapKeySet<>(this); + } + + @Override + ImmutableCollection createValues() { + return new ImmutableMapValues<>(this); + } + + @Override + boolean isPartialView() { + return false; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableMultiset.java new file mode 100644 index 0000000000000..a3e17ba7c79e0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableMultiset.java @@ -0,0 +1,96 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.Collection; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation of ImmutableMultiset backed by a JDK Map and a list of entries. Used to protect + * against hash flooding attacks. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class JdkBackedImmutableMultiset extends ImmutableMultiset { + private final Map delegateMap; + private final ImmutableList> entries; + private final long size; + + static ImmutableMultiset create(Collection> entries) { + @SuppressWarnings("unchecked") + Entry[] entriesArray = entries.toArray(new Entry[0]); + Map delegateMap = Maps.newHashMapWithExpectedSize(entriesArray.length); + long size = 0; + for (int i = 0; i < entriesArray.length; i++) { + Entry entry = entriesArray[i]; + int count = entry.getCount(); + size += count; + E element = checkNotNull(entry.getElement()); + delegateMap.put(element, count); + if (!(entry instanceof Multisets.ImmutableEntry)) { + entriesArray[i] = Multisets.immutableEntry(element, count); + } + } + return new JdkBackedImmutableMultiset<>( + delegateMap, ImmutableList.asImmutableList(entriesArray), size); + } + + private JdkBackedImmutableMultiset( + Map delegateMap, ImmutableList> entries, long size) { + this.delegateMap = delegateMap; + this.entries = entries; + this.size = size; + } + + @Override + public int count(Object element) { + return delegateMap.getOrDefault(element, 0); + } + + private transient ImmutableSet elementSet; + + @Override + public ImmutableSet elementSet() { + ImmutableSet result = elementSet; + return (result == null) ? elementSet = new ElementSet<>(entries, this) : result; + } + + @Override + Entry getEntry(int index) { + return entries.get(index); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int size() { + return Ints.saturatedCast(size); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableSet.java new file mode 100644 index 0000000000000..cec69882379c0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/JdkBackedImmutableSet.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Set; + +/** + * ImmutableSet implementation backed by a JDK HashSet, used to defend against apparent hash + * flooding. This implementation is never used on the GWT client side, but it must be present there + * for serialization to work. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class JdkBackedImmutableSet extends IndexedImmutableSet { + private final Set delegate; + private final ImmutableList delegateList; + + JdkBackedImmutableSet(Set delegate, ImmutableList delegateList) { + this.delegate = delegate; + this.delegateList = delegateList; + } + + @Override + E get(int index) { + return delegateList.get(index); + } + + @Override + public boolean contains(Object object) { + return delegate.contains(object); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int size() { + return delegateList.size(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LexicographicalOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LexicographicalOrdering.java new file mode 100644 index 0000000000000..46568854c3bcb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LexicographicalOrdering.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; + +/** An ordering which sorts iterables by comparing corresponding elements pairwise. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class LexicographicalOrdering extends Ordering> + implements Serializable { + final Comparator elementOrder; + + LexicographicalOrdering(Comparator elementOrder) { + this.elementOrder = elementOrder; + } + + @Override + public int compare(Iterable leftIterable, Iterable rightIterable) { + Iterator left = leftIterable.iterator(); + Iterator right = rightIterable.iterator(); + while (left.hasNext()) { + if (!right.hasNext()) { + return LEFT_IS_GREATER; // because it's longer + } + int result = elementOrder.compare(left.next(), right.next()); + if (result != 0) { + return result; + } + } + if (right.hasNext()) { + return RIGHT_IS_GREATER; // because it's longer + } + return 0; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof LexicographicalOrdering) { + LexicographicalOrdering that = (LexicographicalOrdering) object; + return this.elementOrder.equals(that.elementOrder); + } + return false; + } + + @Override + public int hashCode() { + return elementOrder.hashCode() ^ 2075626741; // meaningless + } + + @Override + public String toString() { + return elementOrder + ".lexicographical()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultimap.java new file mode 100644 index 0000000000000..d3ad0009438f2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultimap.java @@ -0,0 +1,661 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Objects; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Implementation of {@code Multimap} that does not allow duplicate key-value entries and that + * returns collections whose iterators follow the ordering in which the data was added to the + * multimap. + * + *

The collections returned by {@code keySet}, {@code keys}, and {@code asMap} iterate through + * the keys in the order they were first added to the multimap. Similarly, {@code get}, {@code + * removeAll}, and {@code replaceValues} return collections that iterate through the values in the + * order they were added. The collections generated by {@code entries} and {@code values} iterate + * across the key-value mappings in the order they were added to the multimap. + * + *

The iteration ordering of the collections generated by {@code keySet}, {@code keys}, and + * {@code asMap} has a few subtleties. As long as the set of keys remains unchanged, adding or + * removing mappings does not affect the key iteration order. However, if you remove all values + * associated with a key and then add the key back to the multimap, that key will come last in the + * key iteration order. + * + *

The multimap does not store duplicate key-value pairs. Adding a new key-value pair equal to an + * existing key-value pair has no effect. + * + *

Keys and values may be null. All optional multimap methods are supported, and all returned + * views are modifiable. + * + *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent + * read operations will work correctly. To allow concurrent update operations, wrap your multimap + * with a call to {@link Multimaps#synchronizedSetMultimap}. + * + *

Warning: Do not modify either a key or a value of a {@code LinkedHashMultimap} + * in a way that affects its {@link Object#equals} behavior. Undefined behavior and bugs will + * result. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public final class LinkedHashMultimap + extends LinkedHashMultimapGwtSerializationDependencies { + + /** Creates a new, empty {@code LinkedHashMultimap} with the default initial capacities. */ + public static LinkedHashMultimap create() { + return new LinkedHashMultimap<>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY); + } + + /** + * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold the specified + * numbers of keys and values without rehashing. + * + * @param expectedKeys the expected number of distinct keys + * @param expectedValuesPerKey the expected average number of values per key + * @throws IllegalArgumentException if {@code expectedKeys} or {@code expectedValuesPerKey} is + * negative + */ + public static LinkedHashMultimap create( + int expectedKeys, int expectedValuesPerKey) { + return new LinkedHashMultimap<>( + Maps.capacity(expectedKeys), Maps.capacity(expectedValuesPerKey)); + } + + /** + * Constructs a {@code LinkedHashMultimap} with the same mappings as the specified multimap. If a + * key-value mapping appears multiple times in the input multimap, it only appears once in the + * constructed multimap. The new multimap has the same {@link Multimap#entries()} iteration order + * as the input multimap, except for excluding duplicate mappings. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static LinkedHashMultimap create( + Multimap multimap) { + LinkedHashMultimap result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY); + result.putAll(multimap); + return result; + } + + private interface ValueSetLink { + ValueSetLink getPredecessorInValueSet(); + + ValueSetLink getSuccessorInValueSet(); + + void setPredecessorInValueSet(ValueSetLink entry); + + void setSuccessorInValueSet(ValueSetLink entry); + } + + private static void succeedsInValueSet( + ValueSetLink pred, ValueSetLink succ) { + pred.setSuccessorInValueSet(succ); + succ.setPredecessorInValueSet(pred); + } + + private static void succeedsInMultimap( + ValueEntry pred, ValueEntry succ) { + pred.setSuccessorInMultimap(succ); + succ.setPredecessorInMultimap(pred); + } + + private static void deleteFromValueSet( + ValueSetLink entry) { + succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); + } + + private static void deleteFromMultimap( + ValueEntry entry) { + succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); + } + + /** + * LinkedHashMultimap entries are in no less than three coexisting linked lists: a bucket in the + * hash table for a {@code Set} associated with a key, the linked list of insertion-ordered + * entries in that {@code Set}, and the linked list of entries in the LinkedHashMultimap as a + * whole. + */ + @VisibleForTesting + static final class ValueEntry extends ImmutableEntry + implements ValueSetLink { + final int smearedValueHash; + + ValueEntry nextInValueBucket; + /* + * The *InValueSet and *InMultimap fields below are null after construction, but we almost + * always call succeedsIn*() to initialize them immediately thereafter. + * + * The exception is the *InValueSet fields of multimapHeaderEntry, which are never set. (That + * works out fine as long as we continue to be careful not to try to delete them or iterate + * past them.) + * + * We could consider "lying" and omitting @CheckNotNull from all these fields. Normally, I'm not + * a fan of that: What if we someday implement (presumably to be enabled during tests only) + * bytecode rewriting that checks for any null value that passes through an API with a + * known-non-null type? But that particular problem might not arise here, since we're not + * actually reading from the fields in any case in which they might be null (as proven by the + * requireNonNull checks below). Plus, we're *already* lying here, since newHeader passes a null + * key and value, which we pass to the superconstructor, even though the key and value type for + * a given entry might not include null. The right fix for the header problems is probably to + * define a separate MultimapLink interface with a separate "header" implementation, which + * hopefully could avoid implementing Entry or ValueSetLink at all. (But note that that approach + * requires us to define extra classes -- unfortunate under Android.) *Then* we could consider + * lying about the fields below on the grounds that we always initialize them just after the + * constructor -- an example of the kind of lying that our hypothetical bytecode rewriter would + * already have to deal with, thanks to DI frameworks that perform field and method injection, + * frameworks like Android that define post-construct hooks like Activity.onCreate, etc. + */ + + ValueSetLink predecessorInValueSet; + ValueSetLink successorInValueSet; + + ValueEntry predecessorInMultimap; + ValueEntry successorInMultimap; + + ValueEntry( + @ParametricNullness K key, + @ParametricNullness V value, + int smearedValueHash, + ValueEntry nextInValueBucket) { + super(key, value); + this.smearedValueHash = smearedValueHash; + this.nextInValueBucket = nextInValueBucket; + } + + @SuppressWarnings("nullness") // see the comment on the class fields, especially about newHeader + static ValueEntry newHeader() { + return new ValueEntry<>(null, null, 0, null); + } + + boolean matchesValue(Object v, int smearedVHash) { + return smearedValueHash == smearedVHash && Objects.equal(getValue(), v); + } + + @Override + public ValueSetLink getPredecessorInValueSet() { + return requireNonNull(predecessorInValueSet); // see the comment on the class fields + } + + @Override + public ValueSetLink getSuccessorInValueSet() { + return requireNonNull(successorInValueSet); // see the comment on the class fields + } + + @Override + public void setPredecessorInValueSet(ValueSetLink entry) { + predecessorInValueSet = entry; + } + + @Override + public void setSuccessorInValueSet(ValueSetLink entry) { + successorInValueSet = entry; + } + + public ValueEntry getPredecessorInMultimap() { + return requireNonNull(predecessorInMultimap); // see the comment on the class fields + } + + public ValueEntry getSuccessorInMultimap() { + return requireNonNull(successorInMultimap); // see the comment on the class fields + } + + public void setSuccessorInMultimap(ValueEntry multimapSuccessor) { + this.successorInMultimap = multimapSuccessor; + } + + public void setPredecessorInMultimap(ValueEntry multimapPredecessor) { + this.predecessorInMultimap = multimapPredecessor; + } + } + + private static final int DEFAULT_KEY_CAPACITY = 16; + private static final int DEFAULT_VALUE_SET_CAPACITY = 2; + @VisibleForTesting static final double VALUE_SET_LOAD_FACTOR = 1.0; + + @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY; + private transient ValueEntry multimapHeaderEntry; + + private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) { + super(Platform.>newLinkedHashMapWithExpectedSize(keyCapacity)); + checkNonnegative(valueSetCapacity, "expectedValuesPerKey"); + + this.valueSetCapacity = valueSetCapacity; + this.multimapHeaderEntry = ValueEntry.newHeader(); + succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + } + + /** + * {@inheritDoc} + * + *

Creates an empty {@code LinkedHashSet} for a collection of values for one key. + * + * @return a new {@code LinkedHashSet} containing a collection of values for one key + */ + @Override + Set createCollection() { + return Platform.newLinkedHashSetWithExpectedSize(valueSetCapacity); + } + + /** + * {@inheritDoc} + * + *

Creates a decorated insertion-ordered set that also keeps track of the order in which + * key-value pairs are added to the multimap. + * + * @param key key to associate with values in the collection + * @return a new decorated set containing a collection of values for one key + */ + @Override + Collection createCollection(@ParametricNullness K key) { + return new ValueSet(key, valueSetCapacity); + } + + /** + * {@inheritDoc} + * + *

If {@code values} is not empty and the multimap already contains a mapping for {@code key}, + * the {@code keySet()} ordering is unchanged. However, the provided values always come last in + * the {@link #entries()} and {@link #values()} iteration orderings. + */ + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + return super.replaceValues(key, values); + } + + /** + * Returns a set of all key-value pairs. Changes to the returned set will update the underlying + * multimap, and vice versa. The entries set does not support the {@code add} or {@code addAll} + * operations. + * + *

The iterator generated by the returned set traverses the entries in the order they were + * added to the multimap. + * + *

Each entry is an immutable snapshot of a key-value mapping in the multimap, taken at the + * time the entry is returned by a method call to the collection or its iterator. + */ + @Override + public Set> entries() { + return super.entries(); + } + + /** + * Returns a view collection of all distinct keys contained in this multimap. Note that the + * key set contains a key if and only if this multimap maps that key to at least one value. + * + *

The iterator generated by the returned set traverses the keys in the order they were first + * added to the multimap. + * + *

Changes to the returned set will update the underlying multimap, and vice versa. However, + * adding to the returned set is not possible. + */ + @Override + public Set keySet() { + return super.keySet(); + } + + /** + * Returns a collection of all values in the multimap. Changes to the returned collection will + * update the underlying multimap, and vice versa. + * + *

The iterator generated by the returned collection traverses the values in the order they + * were added to the multimap. + */ + @Override + public Collection values() { + return super.values(); + } + + @VisibleForTesting + final class ValueSet extends Sets.ImprovedAbstractSet implements ValueSetLink { + /* + * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory + * consumption. + */ + + @ParametricNullness private final K key; + @VisibleForTesting ValueEntry[] hashTable; + private int size = 0; + private int modCount = 0; + + // We use the set object itself as the end of the linked list, avoiding an unnecessary + // entry object per key. + private ValueSetLink firstEntry; + private ValueSetLink lastEntry; + + ValueSet(@ParametricNullness K key, int expectedValues) { + this.key = key; + this.firstEntry = this; + this.lastEntry = this; + // Round expected values up to a power of 2 to get the table size. + int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR); + + @SuppressWarnings({"rawtypes", "unchecked"}) + ValueEntry[] hashTable = new ValueEntry[tableSize]; + this.hashTable = hashTable; + } + + private int mask() { + return hashTable.length - 1; + } + + @Override + public ValueSetLink getPredecessorInValueSet() { + return lastEntry; + } + + @Override + public ValueSetLink getSuccessorInValueSet() { + return firstEntry; + } + + @Override + public void setPredecessorInValueSet(ValueSetLink entry) { + lastEntry = entry; + } + + @Override + public void setSuccessorInValueSet(ValueSetLink entry) { + firstEntry = entry; + } + + @Override + public Iterator iterator() { + return new Iterator() { + ValueSetLink nextEntry = firstEntry; + ValueEntry toRemove; + int expectedModCount = modCount; + + private void checkForComodification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForComodification(); + return nextEntry != ValueSet.this; + } + + @Override + @ParametricNullness + public V next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + ValueEntry entry = (ValueEntry) nextEntry; + V result = entry.getValue(); + toRemove = entry; + nextEntry = entry.getSuccessorInValueSet(); + return result; + } + + @Override + public void remove() { + checkForComodification(); + checkState(toRemove != null, "no calls to next() since the last call to remove()"); + ValueSet.this.remove(toRemove.getValue()); + expectedModCount = modCount; + toRemove = null; + } + }; + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + for (ValueSetLink entry = firstEntry; + entry != ValueSet.this; + entry = entry.getSuccessorInValueSet()) { + action.accept(((ValueEntry) entry).getValue()); + } + } + + @Override + public int size() { + return size; + } + + @Override + public boolean contains(Object o) { + int smearedHash = Hashing.smearedHash(o); + for (ValueEntry entry = hashTable[smearedHash & mask()]; + entry != null; + entry = entry.nextInValueBucket) { + if (entry.matchesValue(o, smearedHash)) { + return true; + } + } + return false; + } + + @Override + public boolean add(@ParametricNullness V value) { + int smearedHash = Hashing.smearedHash(value); + int bucket = smearedHash & mask(); + ValueEntry rowHead = hashTable[bucket]; + for (ValueEntry entry = rowHead; entry != null; entry = entry.nextInValueBucket) { + if (entry.matchesValue(value, smearedHash)) { + return false; + } + } + + ValueEntry newEntry = new ValueEntry<>(key, value, smearedHash, rowHead); + succeedsInValueSet(lastEntry, newEntry); + succeedsInValueSet(newEntry, this); + succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry); + succeedsInMultimap(newEntry, multimapHeaderEntry); + hashTable[bucket] = newEntry; + size++; + modCount++; + rehashIfNecessary(); + return true; + } + + private void rehashIfNecessary() { + if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) { + @SuppressWarnings("unchecked") + ValueEntry[] hashTable = new ValueEntry[this.hashTable.length * 2]; + this.hashTable = hashTable; + int mask = hashTable.length - 1; + for (ValueSetLink entry = firstEntry; + entry != this; + entry = entry.getSuccessorInValueSet()) { + ValueEntry valueEntry = (ValueEntry) entry; + int bucket = valueEntry.smearedValueHash & mask; + valueEntry.nextInValueBucket = hashTable[bucket]; + hashTable[bucket] = valueEntry; + } + } + } + + @Override + public boolean remove(Object o) { + int smearedHash = Hashing.smearedHash(o); + int bucket = smearedHash & mask(); + ValueEntry prev = null; + for (ValueEntry entry = hashTable[bucket]; + entry != null; + prev = entry, entry = entry.nextInValueBucket) { + if (entry.matchesValue(o, smearedHash)) { + if (prev == null) { + // first entry in the bucket + hashTable[bucket] = entry.nextInValueBucket; + } else { + prev.nextInValueBucket = entry.nextInValueBucket; + } + deleteFromValueSet(entry); + deleteFromMultimap(entry); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public void clear() { + Arrays.fill(hashTable, null); + size = 0; + for (ValueSetLink entry = firstEntry; + entry != this; + entry = entry.getSuccessorInValueSet()) { + ValueEntry valueEntry = (ValueEntry) entry; + deleteFromMultimap(valueEntry); + } + succeedsInValueSet(this, this); + modCount++; + } + } + + @Override + Iterator> entryIterator() { + return new Iterator>() { + ValueEntry nextEntry = multimapHeaderEntry.getSuccessorInMultimap(); + ValueEntry toRemove; + + @Override + public boolean hasNext() { + return nextEntry != multimapHeaderEntry; + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + ValueEntry result = nextEntry; + toRemove = result; + nextEntry = nextEntry.getSuccessorInMultimap(); + return result; + } + + @Override + public void remove() { + checkState(toRemove != null, "no calls to next() since the last call to remove()"); + LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue()); + toRemove = null; + } + }; + } + + @Override + Spliterator> entrySpliterator() { + return Spliterators.spliterator(entries(), Spliterator.DISTINCT | Spliterator.ORDERED); + } + + @Override + Iterator valueIterator() { + return Maps.valueIterator(entryIterator()); + } + + @Override + Spliterator valueSpliterator() { + return CollectSpliterators.map(entrySpliterator(), Entry::getValue); + } + + @Override + public void clear() { + super.clear(); + succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + } + + /** + * @serialData the expected values per key, the number of distinct keys, the number of entries, + * and the entries in order + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(keySet().size()); + for (K key : keySet()) { + stream.writeObject(key); + } + stream.writeInt(size()); + for (Entry entry : entries()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + multimapHeaderEntry = ValueEntry.newHeader(); + succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); + valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY; + int distinctKeys = stream.readInt(); + Map> map = Platform.newLinkedHashMapWithExpectedSize(12); + for (int i = 0; i < distinctKeys; i++) { + @SuppressWarnings("unchecked") + K key = (K) stream.readObject(); + map.put(key, createCollection(key)); + } + int entries = stream.readInt(); + for (int i = 0; i < entries; i++) { + @SuppressWarnings("unchecked") + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") + V value = (V) stream.readObject(); + /* + * requireNonNull is safe for a properly serialized multimap: We've already inserted a + * collection for each key that we expect. + */ + requireNonNull(map.get(key)).add(value); + } + setMap(map); + } + + @GwtIncompatible // java serialization not supported + @J2ktIncompatible + private static final long serialVersionUID = 1; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java new file mode 100644 index 0000000000000..108d146746bdb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultimapGwtSerializationDependencies.java @@ -0,0 +1,42 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Map; + +/** + * A dummy superclass to support GWT serialization of the element types of a {@link + * LinkedHashMultimap}. The GWT supersource for this class contains a field for each type. + * + *

For details about this hack, see {@code GwtSerializationDependencies}, which takes the same + * approach but with a subclass rather than a superclass. + * + *

TODO(cpovirk): Consider applying this subclass approach to our other types. + */ +@GwtCompatible(emulated = true) +abstract class LinkedHashMultimapGwtSerializationDependencies + extends AbstractSetMultimap { + LinkedHashMultimapGwtSerializationDependencies(Map> map) { + super(map); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultiset.java new file mode 100644 index 0000000000000..61cb92716ab66 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedHashMultiset.java @@ -0,0 +1,109 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.LinkedHashMap; + +/** + * A {@code Multiset} implementation with predictable iteration order. Its iterator orders elements + * according to when the first occurrence of the element was added. When the multiset contains + * multiple instances of an element, those instances are consecutive in the iteration order. If all + * occurrences of an element are removed, after which that element is added to the multiset, the + * element will appear at the end of the iteration. + * + *

See the Guava User Guide article on {@code Multiset}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public final class LinkedHashMultiset extends AbstractMapBasedMultiset { + + /** Creates a new, empty {@code LinkedHashMultiset} using the default initial capacity. */ + public static LinkedHashMultiset create() { + return new LinkedHashMultiset(); + } + + /** + * Creates a new, empty {@code LinkedHashMultiset} with the specified expected number of distinct + * elements. + * + * @param distinctElements the expected number of distinct elements + * @throws IllegalArgumentException if {@code distinctElements} is negative + */ + public static LinkedHashMultiset create(int distinctElements) { + return new LinkedHashMultiset(distinctElements); + } + + /** + * Creates a new {@code LinkedHashMultiset} containing the specified elements. + * + *

This implementation is highly efficient when {@code elements} is itself a {@link Multiset}. + * + * @param elements the elements that the multiset should contain + */ + public static LinkedHashMultiset create(Iterable elements) { + LinkedHashMultiset multiset = create(Multisets.inferDistinctElements(elements)); + Iterables.addAll(multiset, elements); + return multiset; + } + + private LinkedHashMultiset() { + super(new LinkedHashMap()); + } + + private LinkedHashMultiset(int distinctElements) { + super(Maps.newLinkedHashMapWithExpectedSize(distinctElements)); + } + + /** + * @serialData the number of distinct elements, the first element, its count, the second element, + * its count, and so on + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int distinctElements = Serialization.readCount(stream); + setBackingMap(new LinkedHashMap()); + Serialization.populateMultiset(this, stream, distinctElements); + } + + @GwtIncompatible // not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedListMultimap.java new file mode 100644 index 0000000000000..bedd737dad14f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/LinkedListMultimap.java @@ -0,0 +1,881 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractSequentialList; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.Consumer; + +import static java.util.Collections.unmodifiableList; +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An implementation of {@code ListMultimap} that supports deterministic iteration order for both + * keys and values. The iteration order is preserved across non-distinct key values. For example, + * for the following multimap definition: + * + *

{@code
+ * Multimap multimap = LinkedListMultimap.create();
+ * multimap.put(key1, foo);
+ * multimap.put(key2, bar);
+ * multimap.put(key1, baz);
+ * }
+ * + * ... the iteration order for {@link #keys()} is {@code [key1, key2, key1]}, and similarly for + * {@link #entries()}. Unlike {@link LinkedHashMultimap}, the iteration order is kept consistent + * between keys, entries and values. For example, calling: + * + *
{@code
+ * multimap.remove(key1, foo);
+ * }
+ * + *

changes the entries iteration order to {@code [key2=bar, key1=baz]} and the key iteration + * order to {@code [key2, key1]}. The {@link #entries()} iterator returns mutable map entries, and + * {@link #replaceValues} attempts to preserve iteration order as much as possible. + * + *

The collections returned by {@link #keySet()} and {@link #asMap} iterate through the keys in + * the order they were first added to the multimap. Similarly, {@link #get}, {@link #removeAll}, and + * {@link #replaceValues} return collections that iterate through the values in the order they were + * added. The collections generated by {@link #entries()}, {@link #keys()}, and {@link #values} + * iterate across the key-value mappings in the order they were added to the multimap. + * + *

The {@link #values()} and {@link #entries()} methods both return a {@code List}, instead of + * the {@code Collection} specified by the {@link ListMultimap} interface. + * + *

The methods {@link #get}, {@link #keySet()}, {@link #keys()}, {@link #values}, {@link + * #entries()}, and {@link #asMap} return collections that are views of the multimap. If the + * multimap is modified while an iteration over any of those collections is in progress, except + * through the iterator's methods, the results of the iteration are undefined. + * + *

Keys and values may be null. All optional multimap methods are supported, and all returned + * views are modifiable. + * + *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent + * read operations will work correctly. To allow concurrent update operations, wrap your multimap + * with a call to {@link Multimaps#synchronizedListMultimap}. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Mike Bostock + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public class LinkedListMultimap extends AbstractMultimap + implements ListMultimap, Serializable { + /* + * Order is maintained using a linked list containing all key-value pairs. In + * addition, a series of disjoint linked lists of "siblings", each containing + * the values for a specific key, is used to implement {@link + * ValueForKeyIterator} in constant time. + */ + + private static final class Node + extends AbstractMapEntry { + @ParametricNullness final K key; + @ParametricNullness V value; + Node next; // the next node (with any key) + Node previous; // the previous node (with any key) + Node nextSibling; // the next node with the same key + Node previousSibling; // the previous node with the same key + + Node(@ParametricNullness K key, @ParametricNullness V value) { + this.key = key; + this.value = value; + } + + @Override + @ParametricNullness + public K getKey() { + return key; + } + + @Override + @ParametricNullness + public V getValue() { + return value; + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V newValue) { + V result = value; + this.value = newValue; + return result; + } + } + + private static class KeyList { + Node head; + Node tail; + int count; + + KeyList(Node firstNode) { + this.head = firstNode; + this.tail = firstNode; + firstNode.previousSibling = null; + firstNode.nextSibling = null; + this.count = 1; + } + } + + private transient Node head; // the head for all keys + private transient Node tail; // the tail for all keys + private transient Map> keyToKeyList; + private transient int size; + + /* + * Tracks modifications to keyToKeyList so that addition or removal of keys invalidates + * preexisting iterators. This does *not* track simple additions and removals of values + * that are not the first to be added or last to be removed for their key. + */ + private transient int modCount; + + /** Creates a new, empty {@code LinkedListMultimap} with the default initial capacity. */ + public static LinkedListMultimap create() { + return new LinkedListMultimap<>(); + } + + /** + * Constructs an empty {@code LinkedListMultimap} with enough capacity to hold the specified + * number of keys without rehashing. + * + * @param expectedKeys the expected number of distinct keys + * @throws IllegalArgumentException if {@code expectedKeys} is negative + */ + public static LinkedListMultimap create( + int expectedKeys) { + return new LinkedListMultimap<>(expectedKeys); + } + + /** + * Constructs a {@code LinkedListMultimap} with the same mappings as the specified {@code + * Multimap}. The new multimap has the same {@link Multimap#entries()} iteration order as the + * input multimap. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static LinkedListMultimap create( + Multimap multimap) { + return new LinkedListMultimap<>(multimap); + } + + LinkedListMultimap() { + this(12); + } + + private LinkedListMultimap(int expectedKeys) { + keyToKeyList = Platform.newHashMapWithExpectedSize(expectedKeys); + } + + private LinkedListMultimap(Multimap multimap) { + this(multimap.keySet().size()); + putAll(multimap); + } + + /** + * Adds a new node for the specified key-value pair before the specified {@code nextSibling} + * element, or at the end of the list if {@code nextSibling} is null. Note: if {@code nextSibling} + * is specified, it MUST be for a node for the same {@code key}! + */ + private Node addNode( + @ParametricNullness K key, @ParametricNullness V value, Node nextSibling) { + Node node = new Node<>(key, value); + if (head == null) { // empty list + head = tail = node; + keyToKeyList.put(key, new KeyList(node)); + modCount++; + } else if (nextSibling == null) { // non-empty list, add to tail + // requireNonNull is safe because the list is non-empty. + requireNonNull(tail).next = node; + node.previous = tail; + tail = node; + KeyList keyList = keyToKeyList.get(key); + if (keyList == null) { + keyToKeyList.put(key, keyList = new KeyList<>(node)); + modCount++; + } else { + keyList.count++; + Node keyTail = keyList.tail; + keyTail.nextSibling = node; + node.previousSibling = keyTail; + keyList.tail = node; + } + } else { // non-empty list, insert before nextSibling + /* + * requireNonNull is safe as long as callers pass a nextSibling that (a) has the same key and + * (b) is present in the multimap. (And they do, except maybe in case of concurrent + * modification, in which case all bets are off.) + */ + KeyList keyList = requireNonNull(keyToKeyList.get(key)); + keyList.count++; + node.previous = nextSibling.previous; + node.previousSibling = nextSibling.previousSibling; + node.next = nextSibling; + node.nextSibling = nextSibling; + if (nextSibling.previousSibling == null) { // nextSibling was key head + keyList.head = node; + } else { + nextSibling.previousSibling.nextSibling = node; + } + if (nextSibling.previous == null) { // nextSibling was head + head = node; + } else { + nextSibling.previous.next = node; + } + nextSibling.previous = node; + nextSibling.previousSibling = node; + } + size++; + return node; + } + + /** + * Removes the specified node from the linked list. This method is only intended to be used from + * the {@code Iterator} classes. See also {@link LinkedListMultimap#removeAllNodes(Object)}. + */ + private void removeNode(Node node) { + if (node.previous != null) { + node.previous.next = node.next; + } else { // node was head + head = node.next; + } + if (node.next != null) { + node.next.previous = node.previous; + } else { // node was tail + tail = node.previous; + } + if (node.previousSibling == null && node.nextSibling == null) { + /* + * requireNonNull is safe as long as we call removeNode only for nodes that are still in the + * Multimap. This should be the case (except in case of concurrent modification, when all bets + * are off). + */ + KeyList keyList = requireNonNull(keyToKeyList.remove(node.key)); + keyList.count = 0; + modCount++; + } else { + // requireNonNull is safe (under the conditions listed in the comment in the branch above). + KeyList keyList = requireNonNull(keyToKeyList.get(node.key)); + keyList.count--; + + if (node.previousSibling == null) { + // requireNonNull is safe because we checked that not *both* siblings were null. + keyList.head = requireNonNull(node.nextSibling); + } else { + node.previousSibling.nextSibling = node.nextSibling; + } + + if (node.nextSibling == null) { + // requireNonNull is safe because we checked that not *both* siblings were null. + keyList.tail = requireNonNull(node.previousSibling); + } else { + node.nextSibling.previousSibling = node.previousSibling; + } + } + size--; + } + + /** Removes all nodes for the specified key. */ + private void removeAllNodes(@ParametricNullness K key) { + Iterators.clear(new ValueForKeyIterator(key)); + } + + /** An {@code Iterator} over all nodes. */ + private class NodeIterator implements ListIterator> { + int nextIndex; + Node next; + Node current; + Node previous; + int expectedModCount = modCount; + + NodeIterator(int index) { + int size = size(); + checkPositionIndex(index, size); + if (index >= (size / 2)) { + previous = tail; + nextIndex = size; + while (index++ < size) { + previous(); + } + } else { + next = head; + while (index-- > 0) { + next(); + } + } + current = null; + } + + private void checkForConcurrentModification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForConcurrentModification(); + return next != null; + } + + @Override + public Node next() { + checkForConcurrentModification(); + if (next == null) { + throw new NoSuchElementException(); + } + previous = current = next; + next = next.next; + nextIndex++; + return current; + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkState(current != null, "no calls to next() since the last call to remove()"); + if (current != next) { // after call to next() + previous = current.previous; + nextIndex--; + } else { // after call to previous() + next = current.next; + } + removeNode(current); + current = null; + expectedModCount = modCount; + } + + @Override + public boolean hasPrevious() { + checkForConcurrentModification(); + return previous != null; + } + + @Override + public Node previous() { + checkForConcurrentModification(); + if (previous == null) { + throw new NoSuchElementException(); + } + next = current = previous; + previous = previous.previous; + nextIndex--; + return current; + } + + @Override + public int nextIndex() { + return nextIndex; + } + + @Override + public int previousIndex() { + return nextIndex - 1; + } + + @Override + public void set(Entry e) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(Entry e) { + throw new UnsupportedOperationException(); + } + + void setValue(@ParametricNullness V value) { + checkState(current != null); + current.value = value; + } + } + + /** An {@code Iterator} over distinct keys in key head order. */ + private class DistinctKeyIterator implements Iterator { + final Set seenKeys = Sets.newHashSetWithExpectedSize(keySet().size()); + Node next = head; + Node current; + int expectedModCount = modCount; + + private void checkForConcurrentModification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForConcurrentModification(); + return next != null; + } + + @Override + @ParametricNullness + public K next() { + checkForConcurrentModification(); + if (next == null) { + throw new NoSuchElementException(); + } + current = next; + seenKeys.add(current.key); + do { // skip ahead to next unseen key + next = next.next; + } while ((next != null) && !seenKeys.add(next.key)); + return current.key; + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkState(current != null, "no calls to next() since the last call to remove()"); + removeAllNodes(current.key); + current = null; + expectedModCount = modCount; + } + } + + /** A {@code ListIterator} over values for a specified key. */ + private class ValueForKeyIterator implements ListIterator { + @ParametricNullness final K key; + int nextIndex; + Node next; + Node current; + Node previous; + + /** Constructs a new iterator over all values for the specified key. */ + ValueForKeyIterator(@ParametricNullness K key) { + this.key = key; + KeyList keyList = keyToKeyList.get(key); + next = (keyList == null) ? null : keyList.head; + } + + /** + * Constructs a new iterator over all values for the specified key starting at the specified + * index. This constructor is optimized so that it starts at either the head or the tail, + * depending on which is closer to the specified index. This allows adds to the tail to be done + * in constant time. + * + * @throws IndexOutOfBoundsException if index is invalid + */ + public ValueForKeyIterator(@ParametricNullness K key, int index) { + KeyList keyList = keyToKeyList.get(key); + int size = (keyList == null) ? 0 : keyList.count; + checkPositionIndex(index, size); + if (index >= (size / 2)) { + previous = (keyList == null) ? null : keyList.tail; + nextIndex = size; + while (index++ < size) { + previous(); + } + } else { + next = (keyList == null) ? null : keyList.head; + while (index-- > 0) { + next(); + } + } + this.key = key; + current = null; + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + @ParametricNullness + public V next() { + if (next == null) { + throw new NoSuchElementException(); + } + previous = current = next; + next = next.nextSibling; + nextIndex++; + return current.value; + } + + @Override + public boolean hasPrevious() { + return previous != null; + } + + @Override + @ParametricNullness + public V previous() { + if (previous == null) { + throw new NoSuchElementException(); + } + next = current = previous; + previous = previous.previousSibling; + nextIndex--; + return current.value; + } + + @Override + public int nextIndex() { + return nextIndex; + } + + @Override + public int previousIndex() { + return nextIndex - 1; + } + + @Override + public void remove() { + checkState(current != null, "no calls to next() since the last call to remove()"); + if (current != next) { // after call to next() + previous = current.previousSibling; + nextIndex--; + } else { // after call to previous() + next = current.nextSibling; + } + removeNode(current); + current = null; + } + + @Override + public void set(@ParametricNullness V value) { + checkState(current != null); + current.value = value; + } + + @Override + public void add(@ParametricNullness V value) { + previous = addNode(key, value, next); + nextIndex++; + current = null; + } + } + + // Query Operations + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return head == null; + } + + @Override + public boolean containsKey(Object key) { + return keyToKeyList.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return values().contains(value); + } + + // Modification Operations + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} always + */ + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + addNode(key, value, null); + return true; + } + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

If any entries for the specified {@code key} already exist in the multimap, their values are + * changed in-place without affecting the iteration order. + * + *

The returned list is immutable and implements {@link java.util.RandomAccess}. + */ + @Override + public List replaceValues(@ParametricNullness K key, Iterable values) { + List oldValues = getCopy(key); + ListIterator keyValues = new ValueForKeyIterator(key); + Iterator newValues = values.iterator(); + + // Replace existing values, if any. + while (keyValues.hasNext() && newValues.hasNext()) { + keyValues.next(); + keyValues.set(newValues.next()); + } + + // Remove remaining old values, if any. + while (keyValues.hasNext()) { + keyValues.next(); + keyValues.remove(); + } + + // Add remaining new values, if any. + while (newValues.hasNext()) { + keyValues.add(newValues.next()); + } + + return oldValues; + } + + private List getCopy(@ParametricNullness K key) { + return unmodifiableList(Lists.newArrayList(new ValueForKeyIterator(key))); + } + + /** + * {@inheritDoc} + * + *

The returned list is immutable and implements {@link java.util.RandomAccess}. + */ + @Override + public List removeAll(Object key) { + /* + * Safe because all we do is remove values for the key, not add them. (If we wanted to make sure + * to call getCopy and removeAllNodes only with a true K, then we could check containsKey first. + * But that check wouldn't eliminate the warnings.) + */ + @SuppressWarnings({"unchecked", "nullness"}) + K castKey = (K) key; + List oldValues = getCopy(castKey); + removeAllNodes(castKey); + return oldValues; + } + + @Override + public void clear() { + head = null; + tail = null; + keyToKeyList.clear(); + size = 0; + modCount++; + } + + // Views + + /** + * {@inheritDoc} + * + *

If the multimap is modified while an iteration over the list is in progress (except through + * the iterator's own {@code add}, {@code set} or {@code remove} operations) the results of the + * iteration are undefined. + * + *

The returned list is not serializable and does not have random access. + */ + @Override + public List get(@ParametricNullness final K key) { + return new AbstractSequentialList() { + @Override + public int size() { + KeyList keyList = keyToKeyList.get(key); + return (keyList == null) ? 0 : keyList.count; + } + + @Override + public ListIterator listIterator(int index) { + return new ValueForKeyIterator(key, index); + } + }; + } + + @Override + Set createKeySet() { + class KeySetImpl extends Sets.ImprovedAbstractSet { + @Override + public int size() { + return keyToKeyList.size(); + } + + @Override + public Iterator iterator() { + return new DistinctKeyIterator(); + } + + @Override + public boolean contains(Object key) { // for performance + return containsKey(key); + } + + @Override + public boolean remove(Object o) { // for performance + return !LinkedListMultimap.this.removeAll(o).isEmpty(); + } + } + return new KeySetImpl(); + } + + @Override + Multiset createKeys() { + return new Multimaps.Keys(this); + } + + /** + * {@inheritDoc} + * + *

The iterator generated by the returned collection traverses the values in the order they + * were added to the multimap. Because the values may have duplicates and follow the insertion + * ordering, this method returns a {@link List}, instead of the {@link Collection} specified in + * the {@link ListMultimap} interface. + */ + @Override + public List values() { + return (List) super.values(); + } + + @Override + List createValues() { + class ValuesImpl extends AbstractSequentialList { + @Override + public int size() { + return size; + } + + @Override + public ListIterator listIterator(int index) { + final NodeIterator nodeItr = new NodeIterator(index); + return new TransformedListIterator, V>(nodeItr) { + @Override + @ParametricNullness + V transform(Entry entry) { + return entry.getValue(); + } + + @Override + public void set(@ParametricNullness V value) { + nodeItr.setValue(value); + } + }; + } + } + return new ValuesImpl(); + } + + /** + * {@inheritDoc} + * + *

The iterator generated by the returned collection traverses the entries in the order they + * were added to the multimap. Because the entries may have duplicates and follow the insertion + * ordering, this method returns a {@link List}, instead of the {@link Collection} specified in + * the {@link ListMultimap} interface. + * + *

An entry's {@link Entry#getKey} method always returns the same key, regardless of what + * happens subsequently. As long as the corresponding key-value mapping is not removed from the + * multimap, {@link Entry#getValue} returns the value from the multimap, which may change over + * time, and {@link Entry#setValue} modifies that value. Removing the mapping from the multimap + * does not alter the value returned by {@code getValue()}, though a subsequent {@code setValue()} + * call won't update the multimap but will lead to a revised value being returned by {@code + * getValue()}. + */ + @Override + public List> entries() { + return (List>) super.entries(); + } + + @Override + List> createEntries() { + class EntriesImpl extends AbstractSequentialList> { + @Override + public int size() { + return size; + } + + @Override + public ListIterator> listIterator(int index) { + return new NodeIterator(index); + } + + @Override + public void forEach(Consumer> action) { + checkNotNull(action); + for (Node node = head; node != null; node = node.next) { + action.accept(node); + } + } + } + return new EntriesImpl(); + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Map> createAsMap() { + return new Multimaps.AsMap<>(this); + } + + /** + * @serialData the number of distinct keys, and then for each distinct key: the first key, the + * number of values for that key, and the key's values, followed by successive keys and values + * from the entries() ordering + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(size()); + for (Entry entry : entries()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyToKeyList = Maps.newLinkedHashMap(); + int size = stream.readInt(); + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeObject + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") // reading data stored by writeObject + V value = (V) stream.readObject(); + put(key, value); + } + } + + @GwtIncompatible // java serialization not supported + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ListMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ListMultimap.java new file mode 100644 index 0000000000000..ddd418cb8ea27 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ListMultimap.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * A {@code Multimap} that can hold duplicate key-value pairs and that maintains the insertion + * ordering of values for a given key. See the {@link Multimap} documentation for information common + * to all multimaps. + * + *

The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods each return a {@link + * List} of values. Though the method signature doesn't say so explicitly, the map returned by + * {@link #asMap} has {@code List} values. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface ListMultimap extends Multimap { + /** + * {@inheritDoc} + * + *

Because the values for a given key may have duplicates and follow the insertion ordering, + * this method returns a {@link List}, instead of the {@link java.util.Collection} specified in + * the {@link Multimap} interface. + */ + @Override + List get(@ParametricNullness K key); + + /** + * {@inheritDoc} + * + *

Because the values for a given key may have duplicates and follow the insertion ordering, + * this method returns a {@link List}, instead of the {@link java.util.Collection} specified in + * the {@link Multimap} interface. + */ + @Override + List removeAll(Object key); + + /** + * {@inheritDoc} + * + *

Because the values for a given key may have duplicates and follow the insertion ordering, + * this method returns a {@link List}, instead of the {@link java.util.Collection} specified in + * the {@link Multimap} interface. + */ + @Override + List replaceValues(@ParametricNullness K key, Iterable values); + + /** + * {@inheritDoc} + * + *

Note: The returned map's values are guaranteed to be of type {@link List}. To obtain + * this map with the more specific generic type {@code Map>}, call {@link + * Multimaps#asMap(ListMultimap)} instead. + */ + @Override + Map> asMap(); + + /** + * Compares the specified object to this multimap for equality. + * + *

Two {@code ListMultimap} instances are equal if, for each key, they contain the same values + * in the same order. If the value orderings disagree, the multimaps will not be considered equal. + * + *

An empty {@code ListMultimap} is equal to any other empty {@code Multimap}, including an + * empty {@code SetMultimap}. + */ + @Override + boolean equals(Object obj); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Lists.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Lists.java new file mode 100644 index 0000000000000..1620f3617c3a8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Lists.java @@ -0,0 +1,1185 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.Serializable; +import java.math.RoundingMode; +import java.util.AbstractList; +import java.util.AbstractSequentialList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * Static utility methods pertaining to {@link List} instances. Also see this class's counterparts + * {@link Sets}, {@link Maps} and {@link Queues}. + * + *

See the Guava User Guide article on {@code Lists}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Lists { + private Lists() {} + + // ArrayList + + /** + * Creates a mutable, empty {@code ArrayList} instance (for Java 6 and earlier). + * + *

Note: if mutability is not required, use {@link ImmutableList#of()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code ArrayList} {@linkplain ArrayList#ArrayList() constructor} directly, taking + * advantage of "diamond" syntax. + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList() { + return new ArrayList<>(); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given elements. + * + *

Note: essentially the only reason to use this method is when you will need to add or + * remove elements later. Otherwise, for non-null elements use {@link ImmutableList#of()} (for + * varargs) or {@link ImmutableList#copyOf(Object[])} (for an array) instead. If any elements + * might be null, or you need support for {@link List#set(int, Object)}, use {@link + * Arrays#asList}. + * + *

Note that even when you do need the ability to add or remove, this method provides only a + * tiny bit of syntactic sugar for {@code newArrayList(}{@link Arrays#asList asList}{@code + * (...))}, or for creating an empty list then calling {@link Collections#addAll}. This method is + * not actually very useful and will likely be deprecated in the future. + */ + @SafeVarargs + @GwtCompatible(serializable = true) + public static ArrayList newArrayList(E... elements) { + checkNotNull(elements); // for GWT + // Avoid integer overflow when a large array is passed in + int capacity = computeArrayListCapacity(elements.length); + ArrayList list = new ArrayList<>(capacity); + Collections.addAll(list, elements); + return list; + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given elements; a very thin + * shortcut for creating an empty list then calling {@link Iterables#addAll}. + * + *

Note: if mutability is not required and the elements are non-null, use {@link + * ImmutableList#copyOf(Iterable)} instead. (Or, change {@code elements} to be a {@link + * FluentIterable} and call {@code elements.toList()}.) + * + *

Note: if {@code elements} is a {@link Collection}, you don't need this method. Use + * the {@code ArrayList} {@linkplain ArrayList#ArrayList(Collection) constructor} directly, taking + * advantage of "diamond" syntax. + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList(Iterable elements) { + checkNotNull(elements); // for GWT + // Let ArrayList's sizing logic work, if possible + return (elements instanceof Collection) + ? new ArrayList<>((Collection) elements) + : newArrayList(elements.iterator()); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given elements; a very thin + * shortcut for creating an empty list and then calling {@link Iterators#addAll}. + * + *

Note: if mutability is not required and the elements are non-null, use {@link + * ImmutableList#copyOf(Iterator)} instead. + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + Iterators.addAll(list, elements); + return list; + } + + @VisibleForTesting + static int computeArrayListCapacity(int arraySize) { + checkNonnegative(arraySize, "arraySize"); + + // TODO(kevinb): Figure out the right behavior, and document it + return Ints.saturatedCast(5L + arraySize + (arraySize / 10)); + } + + /** + * Creates an {@code ArrayList} instance backed by an array with the specified initial size; + * simply delegates to {@link ArrayList#ArrayList(int)}. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use {@code new }{@link ArrayList#ArrayList(int) ArrayList}{@code <>(int)} directly, taking + * advantage of "diamond" syntax. (Unlike here, there is no risk + * of overload ambiguity, since the {@code ArrayList} constructors very wisely did not accept + * varargs.) + * + * @param initialArraySize the exact size of the initial backing array for the returned array list + * ({@code ArrayList} documentation calls this value the "capacity") + * @return a new, empty {@code ArrayList} which is guaranteed not to resize itself unless its size + * reaches {@code initialArraySize + 1} + * @throws IllegalArgumentException if {@code initialArraySize} is negative + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayListWithCapacity(int initialArraySize) { + checkNonnegative(initialArraySize, "initialArraySize"); // for GWT. + return new ArrayList<>(initialArraySize); + } + + /** + * Creates an {@code ArrayList} instance to hold {@code estimatedSize} elements, plus an + * unspecified amount of padding; you almost certainly mean to call {@link + * #newArrayListWithCapacity} (see that method for further advice on usage). + * + *

Note: This method will soon be deprecated. Even in the rare case that you do want + * some amount of padding, it's best if you choose your desired amount explicitly. + * + * @param estimatedSize an estimate of the eventual {@link List#size()} of the new list + * @return a new, empty {@code ArrayList}, sized appropriately to hold the estimated number of + * elements + * @throws IllegalArgumentException if {@code estimatedSize} is negative + */ + @GwtCompatible(serializable = true) + public static ArrayList newArrayListWithExpectedSize(int estimatedSize) { + return new ArrayList<>(computeArrayListCapacity(estimatedSize)); + } + + // LinkedList + + /** + * Creates a mutable, empty {@code LinkedList} instance (for Java 6 and earlier). + * + *

Note: if you won't be adding any elements to the list, use {@link ImmutableList#of()} + * instead. + * + *

Performance note: {@link ArrayList} and {@link java.util.ArrayDeque} consistently + * outperform {@code LinkedList} except in certain rare and specific situations. Unless you have + * spent a lot of time benchmarking your specific needs, use one of those instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedList} {@linkplain LinkedList#LinkedList() constructor} directly, taking + * advantage of "diamond" syntax. + */ + @GwtCompatible(serializable = true) + public static LinkedList newLinkedList() { + return new LinkedList<>(); + } + + /** + * Creates a mutable {@code LinkedList} instance containing the given elements; a very thin + * shortcut for creating an empty list then calling {@link Iterables#addAll}. + * + *

Note: if mutability is not required and the elements are non-null, use {@link + * ImmutableList#copyOf(Iterable)} instead. (Or, change {@code elements} to be a {@link + * FluentIterable} and call {@code elements.toList()}.) + * + *

Performance note: {@link ArrayList} and {@link java.util.ArrayDeque} consistently + * outperform {@code LinkedList} except in certain rare and specific situations. Unless you have + * spent a lot of time benchmarking your specific needs, use one of those instead. + * + *

Note: if {@code elements} is a {@link Collection}, you don't need this method. Use + * the {@code LinkedList} {@linkplain LinkedList#LinkedList(Collection) constructor} directly, + * taking advantage of "diamond" syntax. + */ + @GwtCompatible(serializable = true) + public static LinkedList newLinkedList(Iterable elements) { + LinkedList list = newLinkedList(); + Iterables.addAll(list, elements); + return list; + } + + /** + * Creates an empty {@code CopyOnWriteArrayList} instance. + * + *

Note: if you need an immutable empty {@link List}, use {@link Collections#emptyList} + * instead. + * + * @return a new, empty {@code CopyOnWriteArrayList} + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // CopyOnWriteArrayList + public static CopyOnWriteArrayList newCopyOnWriteArrayList() { + return new CopyOnWriteArrayList<>(); + } + + /** + * Creates a {@code CopyOnWriteArrayList} instance containing the given elements. + * + * @param elements the elements that the list should contain, in order + * @return a new {@code CopyOnWriteArrayList} containing those elements + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // CopyOnWriteArrayList + public static CopyOnWriteArrayList newCopyOnWriteArrayList( + Iterable elements) { + // We copy elements to an ArrayList first, rather than incurring the + // quadratic cost of adding them to the COWAL directly. + Collection elementsCollection = + (elements instanceof Collection) + ? (Collection) elements + : newArrayList(elements); + return new CopyOnWriteArrayList<>(elementsCollection); + } + + /** + * Returns an unmodifiable list containing the specified first element and backed by the specified + * array of additional elements. Changes to the {@code rest} array will be reflected in the + * returned list. Unlike {@link Arrays#asList}, the returned list is unmodifiable. + * + *

This is useful when a varargs method needs to use a signature such as {@code (Foo firstFoo, + * Foo... moreFoos)}, in order to avoid overload ambiguity or to enforce a minimum argument count. + * + *

The returned list is serializable and implements {@link RandomAccess}. + * + * @param first the first element + * @param rest an array of additional elements, possibly empty + * @return an unmodifiable list containing the specified elements + */ + public static List asList(@ParametricNullness E first, E[] rest) { + return new OnePlusArrayList<>(first, rest); + } + + /** + * Returns an unmodifiable list containing the specified first and second element, and backed by + * the specified array of additional elements. Changes to the {@code rest} array will be reflected + * in the returned list. Unlike {@link Arrays#asList}, the returned list is unmodifiable. + * + *

This is useful when a varargs method needs to use a signature such as {@code (Foo firstFoo, + * Foo secondFoo, Foo... moreFoos)}, in order to avoid overload ambiguity or to enforce a minimum + * argument count. + * + *

The returned list is serializable and implements {@link RandomAccess}. + * + * @param first the first element + * @param second the second element + * @param rest an array of additional elements, possibly empty + * @return an unmodifiable list containing the specified elements + */ + public static List asList( + @ParametricNullness E first, @ParametricNullness E second, E[] rest) { + return new TwoPlusArrayList<>(first, second, rest); + } + + /** + * @see Lists#asList(Object, Object[]) + */ + private static class OnePlusArrayList extends AbstractList + implements Serializable, RandomAccess { + @ParametricNullness final E first; + final E[] rest; + + OnePlusArrayList(@ParametricNullness E first, E[] rest) { + this.first = first; + this.rest = checkNotNull(rest); + } + + @Override + public int size() { + return IntMath.saturatedAdd(rest.length, 1); + } + + @Override + @ParametricNullness + public E get(int index) { + // check explicitly so the IOOBE will have the right message + checkElementIndex(index, size()); + return (index == 0) ? first : rest[index - 1]; + } + + @J2ktIncompatible private static final long serialVersionUID = 0; + } + + /** + * @see Lists#asList(Object, Object, Object[]) + */ + private static class TwoPlusArrayList extends AbstractList + implements Serializable, RandomAccess { + @ParametricNullness final E first; + @ParametricNullness final E second; + final E[] rest; + + TwoPlusArrayList(@ParametricNullness E first, @ParametricNullness E second, E[] rest) { + this.first = first; + this.second = second; + this.rest = checkNotNull(rest); + } + + @Override + public int size() { + return IntMath.saturatedAdd(rest.length, 2); + } + + @Override + @ParametricNullness + public E get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + default: + // check explicitly so the IOOBE will have the right message + checkElementIndex(index, size()); + return rest[index - 2]; + } + } + + @J2ktIncompatible private static final long serialVersionUID = 0; + } + + /** + * Returns every possible list that can be formed by choosing one element from each of the given + * lists in order; the "n-ary Cartesian + * product" of the lists. For example: + * + *

{@code
+   * Lists.cartesianProduct(ImmutableList.of(
+   *     ImmutableList.of(1, 2),
+   *     ImmutableList.of("A", "B", "C")))
+   * }
+ * + *

returns a list containing six lists in the following order: + * + *

    + *
  • {@code ImmutableList.of(1, "A")} + *
  • {@code ImmutableList.of(1, "B")} + *
  • {@code ImmutableList.of(1, "C")} + *
  • {@code ImmutableList.of(2, "A")} + *
  • {@code ImmutableList.of(2, "B")} + *
  • {@code ImmutableList.of(2, "C")} + *
+ * + *

The result is guaranteed to be in the "traditional", lexicographical order for Cartesian + * products that you would get from nesting for loops: + * + *

{@code
+   * for (B b0 : lists.get(0)) {
+   *   for (B b1 : lists.get(1)) {
+   *     ...
+   *     ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+   *     // operate on tuple
+   *   }
+   * }
+   * }
+ * + *

Note that if any input list is empty, the Cartesian product will also be empty. If no lists + * at all are provided (an empty list), the resulting Cartesian product has one element, an empty + * list (counter-intuitive, but mathematically consistent). + * + *

Performance notes: while the cartesian product of lists of size {@code m, n, p} is a + * list of size {@code m x n x p}, its actual memory consumption is much smaller. When the + * cartesian product is constructed, the input lists are merely copied. Only as the resulting list + * is iterated are the individual lists created, and these are not retained after iteration. + * + * @param lists the lists to choose elements from, in the order that the elements chosen from + * those lists should appear in the resulting lists + * @param any common base class shared by all axes (often just {@link Object}) + * @return the Cartesian product, as an immutable list containing immutable lists + * @throws IllegalArgumentException if the size of the cartesian product would be greater than + * {@link Integer#MAX_VALUE} + * @throws NullPointerException if {@code lists}, any one of the {@code lists}, or any element of + * a provided list is null + * @since 19.0 + */ + public static List> cartesianProduct(List> lists) { + return CartesianList.create(lists); + } + + /** + * Returns every possible list that can be formed by choosing one element from each of the given + * lists in order; the "n-ary Cartesian + * product" of the lists. For example: + * + *

{@code
+   * Lists.cartesianProduct(ImmutableList.of(
+   *     ImmutableList.of(1, 2),
+   *     ImmutableList.of("A", "B", "C")))
+   * }
+ * + *

returns a list containing six lists in the following order: + * + *

    + *
  • {@code ImmutableList.of(1, "A")} + *
  • {@code ImmutableList.of(1, "B")} + *
  • {@code ImmutableList.of(1, "C")} + *
  • {@code ImmutableList.of(2, "A")} + *
  • {@code ImmutableList.of(2, "B")} + *
  • {@code ImmutableList.of(2, "C")} + *
+ * + *

The result is guaranteed to be in the "traditional", lexicographical order for Cartesian + * products that you would get from nesting for loops: + * + *

{@code
+   * for (B b0 : lists.get(0)) {
+   *   for (B b1 : lists.get(1)) {
+   *     ...
+   *     ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+   *     // operate on tuple
+   *   }
+   * }
+   * }
+ * + *

Note that if any input list is empty, the Cartesian product will also be empty. If no lists + * at all are provided (an empty list), the resulting Cartesian product has one element, an empty + * list (counter-intuitive, but mathematically consistent). + * + *

Performance notes: while the cartesian product of lists of size {@code m, n, p} is a + * list of size {@code m x n x p}, its actual memory consumption is much smaller. When the + * cartesian product is constructed, the input lists are merely copied. Only as the resulting list + * is iterated are the individual lists created, and these are not retained after iteration. + * + * @param lists the lists to choose elements from, in the order that the elements chosen from + * those lists should appear in the resulting lists + * @param any common base class shared by all axes (often just {@link Object}) + * @return the Cartesian product, as an immutable list containing immutable lists + * @throws IllegalArgumentException if the size of the cartesian product would be greater than + * {@link Integer#MAX_VALUE} + * @throws NullPointerException if {@code lists}, any one of the {@code lists}, or any element of + * a provided list is null + * @since 19.0 + */ + @SafeVarargs + public static List> cartesianProduct(List... lists) { + return cartesianProduct(Arrays.asList(lists)); + } + + /** + * Returns a list that applies {@code function} to each element of {@code fromList}. The returned + * list is a transformed view of {@code fromList}; changes to {@code fromList} will be reflected + * in the returned list and vice versa. + * + *

Since functions are not reversible, the transform is one-way and new items cannot be stored + * in the returned list. The {@code add}, {@code addAll} and {@code set} methods are unsupported + * in the returned list. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned list + * to be a view, but it means that the function will be applied many times for bulk operations + * like {@link List#contains} and {@link List#hashCode}. For this to perform well, {@code + * function} should be fast. To avoid lazy evaluation when the returned list doesn't need to be a + * view, copy the returned list into a new list of your choosing. + * + *

If {@code fromList} implements {@link RandomAccess}, so will the returned list. The returned + * list is threadsafe if the supplied list and function are. + * + *

If only a {@code Collection} or {@code Iterable} input is available, use {@link + * Collections2#transform} or {@link Iterables#transform}. + * + *

Note: serializing the returned list is implemented by serializing {@code fromList}, + * its contents, and {@code function} -- not by serializing the transformed values. This + * can lead to surprising behavior, so serializing the returned list is not recommended. + * Instead, copy the list using {@link ImmutableList#copyOf(Collection)} (for example), then + * serialize the copy. Other methods similar to this do not implement serialization at all for + * this reason. + * + *

Java 8 users: many use cases for this method are better addressed by {@link + * java.util.stream.Stream#map}. This method is not being deprecated, but we gently encourage you + * to migrate to streams. + */ + public static List transform( + List fromList, Function function) { + return (fromList instanceof RandomAccess) + ? new TransformingRandomAccessList<>(fromList, function) + : new TransformingSequentialList<>(fromList, function); + } + + /** + * Implementation of a sequential transforming list. + * + * @see Lists#transform + */ + private static class TransformingSequentialList + extends AbstractSequentialList implements Serializable { + final List fromList; + final Function function; + + TransformingSequentialList(List fromList, Function function) { + this.fromList = checkNotNull(fromList); + this.function = checkNotNull(function); + } + + /** + * The default implementation inherited is based on iteration and removal of each element which + * can be overkill. That's why we forward this call directly to the backing list. + */ + @Override + protected void removeRange(int fromIndex, int toIndex) { + fromList.subList(fromIndex, toIndex).clear(); + } + + @Override + public int size() { + return fromList.size(); + } + + @Override + public ListIterator listIterator(final int index) { + return new TransformedListIterator(fromList.listIterator(index)) { + @Override + @ParametricNullness + T transform(@ParametricNullness F from) { + return function.apply(from); + } + }; + } + + @Override + public boolean removeIf(Predicate filter) { + checkNotNull(filter); + return fromList.removeIf(element -> filter.test(function.apply(element))); + } + + private static final long serialVersionUID = 0; + } + + /** + * Implementation of a transforming random access list. We try to make as many of these methods + * pass-through to the source list as possible so that the performance characteristics of the + * source list and transformed list are similar. + * + * @see Lists#transform + */ + private static class TransformingRandomAccessList + extends AbstractList implements RandomAccess, Serializable { + final List fromList; + final Function function; + + TransformingRandomAccessList(List fromList, Function function) { + this.fromList = checkNotNull(fromList); + this.function = checkNotNull(function); + } + + /** + * The default implementation inherited is based on iteration and removal of each element which + * can be overkill. That's why we forward this call directly to the backing list. + */ + @Override + protected void removeRange(int fromIndex, int toIndex) { + fromList.subList(fromIndex, toIndex).clear(); + } + + @Override + @ParametricNullness + public T get(int index) { + return function.apply(fromList.get(index)); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return new TransformedListIterator(fromList.listIterator(index)) { + @Override + T transform(F from) { + return function.apply(from); + } + }; + } + + // TODO: cpovirk - Why override `isEmpty` here but not in TransformingSequentialList? + + @Override + public boolean isEmpty() { + return fromList.isEmpty(); + } + + @Override + public boolean removeIf(Predicate filter) { + checkNotNull(filter); + return fromList.removeIf(element -> filter.test(function.apply(element))); + } + + @Override + @ParametricNullness + public T remove(int index) { + return function.apply(fromList.remove(index)); + } + + @Override + public int size() { + return fromList.size(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns consecutive {@linkplain List#subList(int, int) sublists} of a list, each of the same + * size (the final list may be smaller). For example, partitioning a list containing {@code [a, b, + * c, d, e]} with a partition size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list + * containing two inner lists of three and two elements, all in the original order. + * + *

The outer list is unmodifiable, but reflects the latest state of the source list. The inner + * lists are sublist views of the original list, produced on demand using {@link List#subList(int, + * int)}, and are subject to all the usual caveats about modification as explained in that API. + * + * @param list the list to return consecutive sublists of + * @param size the desired size of each sublist (the last may be smaller) + * @return a list of consecutive sublists + * @throws IllegalArgumentException if {@code partitionSize} is nonpositive + */ + public static List> partition(List list, int size) { + checkNotNull(list); + checkArgument(size > 0); + return (list instanceof RandomAccess) + ? new RandomAccessPartition<>(list, size) + : new Partition<>(list, size); + } + + private static class Partition extends AbstractList> { + final List list; + final int size; + + Partition(List list, int size) { + this.list = list; + this.size = size; + } + + @Override + public List get(int index) { + checkElementIndex(index, size()); + int start = index * size; + int end = Math.min(start + size, list.size()); + return list.subList(start, end); + } + + @Override + public int size() { + return IntMath.divide(list.size(), size, RoundingMode.CEILING); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + } + + private static class RandomAccessPartition extends Partition + implements RandomAccess { + RandomAccessPartition(List list, int size) { + super(list, size); + } + } + + /** + * Returns a view of the specified string as an immutable list of {@code Character} values. + * + * @since 7.0 + */ + public static ImmutableList charactersOf(String string) { + return new StringAsImmutableList(checkNotNull(string)); + } + + /** + * Returns a view of the specified {@code CharSequence} as a {@code List}, viewing + * {@code sequence} as a sequence of Unicode code units. The view does not support any + * modification operations, but reflects any changes to the underlying character sequence. + * + * @param sequence the character sequence to view as a {@code List} of characters + * @return an {@code List} view of the character sequence + * @since 7.0 + */ + public static List charactersOf(CharSequence sequence) { + return new CharSequenceAsList(checkNotNull(sequence)); + } + + @SuppressWarnings("serial") // serialized using ImmutableList serialization + private static final class StringAsImmutableList extends ImmutableList { + + private final String string; + + StringAsImmutableList(String string) { + this.string = string; + } + + @Override + public int indexOf(Object object) { + return (object instanceof Character) ? string.indexOf((Character) object) : -1; + } + + @Override + public int lastIndexOf(Object object) { + return (object instanceof Character) ? string.lastIndexOf((Character) object) : -1; + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); // for GWT + return charactersOf(string.substring(fromIndex, toIndex)); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public Character get(int index) { + checkElementIndex(index, size()); // for GWT + return string.charAt(index); + } + + @Override + public int size() { + return string.length(); + } + } + + private static final class CharSequenceAsList extends AbstractList { + private final CharSequence sequence; + + CharSequenceAsList(CharSequence sequence) { + this.sequence = sequence; + } + + @Override + public Character get(int index) { + checkElementIndex(index, size()); // for GWT + return sequence.charAt(index); + } + + @Override + public int size() { + return sequence.length(); + } + } + + /** + * Returns a reversed view of the specified list. For example, {@code + * Lists.reverse(Arrays.asList(1, 2, 3))} returns a list containing {@code 3, 2, 1}. The returned + * list is backed by this list, so changes in the returned list are reflected in this list, and + * vice-versa. The returned list supports all of the optional list operations supported by this + * list. + * + *

The returned list is random-access if the specified list is random access. + * + * @since 7.0 + */ + public static List reverse(List list) { + if (list instanceof ImmutableList) { + // Avoid nullness warnings. + List reversed = ((ImmutableList) list).reverse(); + @SuppressWarnings("unchecked") + List result = (List) reversed; + return result; + } else if (list instanceof ReverseList) { + return ((ReverseList) list).getForwardList(); + } else if (list instanceof RandomAccess) { + return new RandomAccessReverseList<>(list); + } else { + return new ReverseList<>(list); + } + } + + private static class ReverseList extends AbstractList { + private final List forwardList; + + ReverseList(List forwardList) { + this.forwardList = checkNotNull(forwardList); + } + + List getForwardList() { + return forwardList; + } + + private int reverseIndex(int index) { + int size = size(); + checkElementIndex(index, size); + return (size - 1) - index; + } + + private int reversePosition(int index) { + int size = size(); + checkPositionIndex(index, size); + return size - index; + } + + @Override + public void add(int index, @ParametricNullness T element) { + forwardList.add(reversePosition(index), element); + } + + @Override + public void clear() { + forwardList.clear(); + } + + @Override + @ParametricNullness + public T remove(int index) { + return forwardList.remove(reverseIndex(index)); + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + subList(fromIndex, toIndex).clear(); + } + + @Override + @ParametricNullness + public T set(int index, @ParametricNullness T element) { + return forwardList.set(reverseIndex(index), element); + } + + @Override + @ParametricNullness + public T get(int index) { + return forwardList.get(reverseIndex(index)); + } + + @Override + public int size() { + return forwardList.size(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + return reverse(forwardList.subList(reversePosition(toIndex), reversePosition(fromIndex))); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + int start = reversePosition(index); + final ListIterator forwardIterator = forwardList.listIterator(start); + return new ListIterator() { + + boolean canRemoveOrSet; + + @Override + public void add(@ParametricNullness T e) { + forwardIterator.add(e); + forwardIterator.previous(); + canRemoveOrSet = false; + } + + @Override + public boolean hasNext() { + return forwardIterator.hasPrevious(); + } + + @Override + public boolean hasPrevious() { + return forwardIterator.hasNext(); + } + + @Override + @ParametricNullness + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + canRemoveOrSet = true; + return forwardIterator.previous(); + } + + @Override + public int nextIndex() { + return reversePosition(forwardIterator.nextIndex()); + } + + @Override + @ParametricNullness + public T previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + canRemoveOrSet = true; + return forwardIterator.next(); + } + + @Override + public int previousIndex() { + return nextIndex() - 1; + } + + @Override + public void remove() { + checkRemove(canRemoveOrSet); + forwardIterator.remove(); + canRemoveOrSet = false; + } + + @Override + public void set(@ParametricNullness T e) { + checkState(canRemoveOrSet); + forwardIterator.set(e); + } + }; + } + } + + private static class RandomAccessReverseList extends ReverseList + implements RandomAccess { + RandomAccessReverseList(List forwardList) { + super(forwardList); + } + } + + /** An implementation of {@link List#hashCode()}. */ + static int hashCodeImpl(List list) { + // TODO(lowasser): worth optimizing for RandomAccess? + int hashCode = 1; + for (Object o : list) { + hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); + + hashCode = ~~hashCode; + // needed to deal with GWT integer overflow + } + return hashCode; + } + + /** An implementation of {@link List#equals(Object)}. */ + static boolean equalsImpl(List thisList, Object other) { + if (other == checkNotNull(thisList)) { + return true; + } + if (!(other instanceof List)) { + return false; + } + List otherList = (List) other; + int size = thisList.size(); + if (size != otherList.size()) { + return false; + } + if (thisList instanceof RandomAccess && otherList instanceof RandomAccess) { + // avoid allocation and use the faster loop + for (int i = 0; i < size; i++) { + if (!Objects.equal(thisList.get(i), otherList.get(i))) { + return false; + } + } + return true; + } else { + return Iterators.elementsEqual(thisList.iterator(), otherList.iterator()); + } + } + + /** An implementation of {@link List#addAll(int, Collection)}. */ + static boolean addAllImpl( + List list, int index, Iterable elements) { + boolean changed = false; + ListIterator listIterator = list.listIterator(index); + for (E e : elements) { + listIterator.add(e); + changed = true; + } + return changed; + } + + /** An implementation of {@link List#indexOf(Object)}. */ + static int indexOfImpl(List list, Object element) { + if (list instanceof RandomAccess) { + return indexOfRandomAccess(list, element); + } else { + ListIterator listIterator = list.listIterator(); + while (listIterator.hasNext()) { + if (Objects.equal(element, listIterator.next())) { + return listIterator.previousIndex(); + } + } + return -1; + } + } + + private static int indexOfRandomAccess(List list, Object element) { + int size = list.size(); + if (element == null) { + for (int i = 0; i < size; i++) { + if (list.get(i) == null) { + return i; + } + } + } else { + for (int i = 0; i < size; i++) { + if (element.equals(list.get(i))) { + return i; + } + } + } + return -1; + } + + /** An implementation of {@link List#lastIndexOf(Object)}. */ + static int lastIndexOfImpl(List list, Object element) { + if (list instanceof RandomAccess) { + return lastIndexOfRandomAccess(list, element); + } else { + ListIterator listIterator = list.listIterator(list.size()); + while (listIterator.hasPrevious()) { + if (Objects.equal(element, listIterator.previous())) { + return listIterator.nextIndex(); + } + } + return -1; + } + } + + private static int lastIndexOfRandomAccess(List list, Object element) { + if (element == null) { + for (int i = list.size() - 1; i >= 0; i--) { + if (list.get(i) == null) { + return i; + } + } + } else { + for (int i = list.size() - 1; i >= 0; i--) { + if (element.equals(list.get(i))) { + return i; + } + } + } + return -1; + } + + /** Returns an implementation of {@link List#listIterator(int)}. */ + static ListIterator listIteratorImpl(List list, int index) { + return new AbstractListWrapper<>(list).listIterator(index); + } + + /** An implementation of {@link List#subList(int, int)}. */ + static List subListImpl(final List list, int fromIndex, int toIndex) { + List wrapper; + if (list instanceof RandomAccess) { + wrapper = + new RandomAccessListWrapper(list) { + @Override + public ListIterator listIterator(int index) { + return backingList.listIterator(index); + } + + @J2ktIncompatible private static final long serialVersionUID = 0; + }; + } else { + wrapper = + new AbstractListWrapper(list) { + @Override + public ListIterator listIterator(int index) { + return backingList.listIterator(index); + } + + @J2ktIncompatible private static final long serialVersionUID = 0; + }; + } + return wrapper.subList(fromIndex, toIndex); + } + + private static class AbstractListWrapper extends AbstractList { + final List backingList; + + AbstractListWrapper(List backingList) { + this.backingList = checkNotNull(backingList); + } + + @Override + public void add(int index, @ParametricNullness E element) { + backingList.add(index, element); + } + + @Override + public boolean addAll(int index, Collection c) { + return backingList.addAll(index, c); + } + + @Override + @ParametricNullness + public E get(int index) { + return backingList.get(index); + } + + @Override + @ParametricNullness + public E remove(int index) { + return backingList.remove(index); + } + + @Override + @ParametricNullness + public E set(int index, @ParametricNullness E element) { + return backingList.set(index, element); + } + + @Override + public boolean contains(Object o) { + return backingList.contains(o); + } + + @Override + public int size() { + return backingList.size(); + } + } + + private static class RandomAccessListWrapper extends AbstractListWrapper + implements RandomAccess { + RandomAccessListWrapper(List backingList) { + super(backingList); + } + } + + /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ + static List cast(Iterable iterable) { + return (List) iterable; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapDifference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapDifference.java new file mode 100644 index 0000000000000..9b6b422811261 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapDifference.java @@ -0,0 +1,113 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; + +/** + * An object representing the differences between two maps. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface MapDifference { + /** + * Returns {@code true} if there are no differences between the two maps; that is, if the maps are + * equal. + */ + boolean areEqual(); + + /** + * Returns an unmodifiable map containing the entries from the left map whose keys are not present + * in the right map. + */ + Map entriesOnlyOnLeft(); + + /** + * Returns an unmodifiable map containing the entries from the right map whose keys are not + * present in the left map. + */ + Map entriesOnlyOnRight(); + + /** + * Returns an unmodifiable map containing the entries that appear in both maps; that is, the + * intersection of the two maps. + */ + Map entriesInCommon(); + + /** + * Returns an unmodifiable map describing keys that appear in both maps, but with different + * values. + */ + Map> entriesDiffering(); + + /** + * Compares the specified object with this instance for equality. Returns {@code true} if the + * given object is also a {@code MapDifference} and the values returned by the {@link + * #entriesOnlyOnLeft()}, {@link #entriesOnlyOnRight()}, {@link #entriesInCommon()} and {@link + * #entriesDiffering()} of the two instances are equal. + */ + @Override + boolean equals(Object object); + + /** + * Returns the hash code for this instance. This is defined as the hash code of + * + *

{@code
+   * Arrays.asList(entriesOnlyOnLeft(), entriesOnlyOnRight(),
+   *     entriesInCommon(), entriesDiffering())
+   * }
+ */ + @Override + int hashCode(); + + /** + * A difference between the mappings from two maps with the same key. The {@link #leftValue} and + * {@link #rightValue} are not equal, and one but not both of them may be null. + * + * @since 2.0 + */ + interface ValueDifference { + /** Returns the value from the left map (possibly null). */ + @ParametricNullness + V leftValue(); + + /** Returns the value from the right map (possibly null). */ + @ParametricNullness + V rightValue(); + + /** + * Two instances are considered equal if their {@link #leftValue()} values are equal and their + * {@link #rightValue()} values are also equal. + */ + @Override + boolean equals(Object other); + + /** + * The hash code equals the value {@code Arrays.asList(leftValue(), rightValue()).hashCode()}. + */ + @Override + int hashCode(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapMaker.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapMaker.java new file mode 100644 index 0000000000000..b779d0079e77b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapMaker.java @@ -0,0 +1,312 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Ascii; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.collect.MapMakerInternalMap.Strength; + +import java.lang.ref.WeakReference; +import java.util.ConcurrentModificationException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * A builder of {@link ConcurrentMap} instances that can have keys or values automatically wrapped + * in {@linkplain WeakReference weak} references. + * + *

Usage example: + * + *

{@code
+ * ConcurrentMap timers = new MapMaker()
+ *     .concurrencyLevel(4)
+ *     .weakKeys()
+ *     .makeMap();
+ * }
+ * + *

These features are all optional; {@code new MapMaker().makeMap()} returns a valid concurrent + * map that behaves similarly to a {@link ConcurrentHashMap}. + * + *

The returned map is implemented as a hash table with similar performance characteristics to + * {@link ConcurrentHashMap}. It supports all optional operations of the {@code ConcurrentMap} + * interface. It does not permit null keys or values. + * + *

Note: by default, the returned map uses equality comparisons (the {@link Object#equals + * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} was + * specified, the map uses identity ({@code ==}) comparisons instead for keys. Likewise, if {@link + * #weakValues} was specified, the map uses identity comparisons for values. + * + *

The view collections of the returned map have weakly consistent iterators. This means + * that they are safe for concurrent use, but if other threads modify the map after the iterator is + * created, it is undefined which of these changes, if any, are reflected in that iterator. These + * iterators never throw {@link ConcurrentModificationException}. + * + *

If {@link #weakKeys} or {@link #weakValues} are requested, it is possible for a key or value + * present in the map to be reclaimed by the garbage collector. Entries with reclaimed keys or + * values may be removed from the map on each map modification or on occasional map accesses; such + * entries may be counted by {@link Map#size}, but will never be visible to read or write + * operations. A partially-reclaimed entry is never exposed to the user. Any {@link Map.Entry} + * instance retrieved from the map's {@linkplain Map#entrySet entry set} is a snapshot of that + * entry's state at the time of retrieval; such entries do, however, support {@link + * Map.Entry#setValue}, which simply calls {@link Map#put} on the entry's key. + * + *

The maps produced by {@code MapMaker} are serializable, and the deserialized maps retain all + * the configuration properties of the original map. During deserialization, if the original map had + * used weak references, the entries are reconstructed as they were, but it's not unlikely they'll + * be quickly garbage-collected before they are ever accessed. + * + *

{@code new MapMaker().weakKeys().makeMap()} is a recommended replacement for {@link + * java.util.WeakHashMap}, but note that it compares keys using object identity whereas {@code + * WeakHashMap} uses {@link Object#equals}. + * + * @author Bob Lee + * @author Charles Fry + * @author Kevin Bourrillion + * @since 2.0 + */ +@J2ktIncompatible +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class MapMaker { + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_CONCURRENCY_LEVEL = 4; + + static final int UNSET_INT = -1; + + // TODO(kevinb): dispense with this after benchmarking + boolean useCustomMap; + + int initialCapacity = UNSET_INT; + int concurrencyLevel = UNSET_INT; + + Strength keyStrength; + Strength valueStrength; + + Equivalence keyEquivalence; + + /** + * Constructs a new {@code MapMaker} instance with default settings, including strong keys, strong + * values, and no automatic eviction of any kind. + */ + public MapMaker() {} + + /** + * Sets a custom {@code Equivalence} strategy for comparing keys. + * + *

By default, the map uses {@link Equivalence#identity} to determine key equality when {@link + * #weakKeys} is specified, and {@link Equivalence#equals()} otherwise. The only place this is + * used is in {@link Interners.WeakInterner}. + */ + @GwtIncompatible // To be supported + MapMaker keyEquivalence(Equivalence equivalence) { + checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence); + keyEquivalence = checkNotNull(equivalence); + this.useCustomMap = true; + return this; + } + + Equivalence getKeyEquivalence() { + return MoreObjects.firstNonNull(keyEquivalence, getKeyStrength().defaultEquivalence()); + } + + /** + * Sets the minimum total size for the internal hash tables. For example, if the initial capacity + * is {@code 60}, and the concurrency level is {@code 8}, then eight segments are created, each + * having a hash table of size eight. Providing a large enough estimate at construction time + * avoids the need for expensive resizing operations later, but setting this value unnecessarily + * high wastes memory. + * + * @throws IllegalArgumentException if {@code initialCapacity} is negative + * @throws IllegalStateException if an initial capacity was already set + */ + public MapMaker initialCapacity(int initialCapacity) { + checkState( + this.initialCapacity == UNSET_INT, + "initial capacity was already set to %s", + this.initialCapacity); + checkArgument(initialCapacity >= 0); + this.initialCapacity = initialCapacity; + return this; + } + + int getInitialCapacity() { + return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity; + } + + /** + * Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The + * table is internally partitioned to try to permit the indicated number of concurrent updates + * without contention. Because assignment of entries to these partitions is not necessarily + * uniform, the actual concurrency observed may vary. Ideally, you should choose a value to + * accommodate as many threads as will ever concurrently modify the table. Using a significantly + * higher value than you need can waste space and time, and a significantly lower value can lead + * to thread contention. But overestimates and underestimates within an order of magnitude do not + * usually have much noticeable impact. A value of one permits only one thread to modify the map + * at a time, but since read operations can proceed concurrently, this still yields higher + * concurrency than full synchronization. Defaults to 4. + * + *

Note: Prior to Guava release 9.0, the default was 16. It is possible the default will + * change again in the future. If you care about this value, you should always choose it + * explicitly. + * + * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive + * @throws IllegalStateException if a concurrency level was already set + */ + public MapMaker concurrencyLevel(int concurrencyLevel) { + checkState( + this.concurrencyLevel == UNSET_INT, + "concurrency level was already set to %s", + this.concurrencyLevel); + checkArgument(concurrencyLevel > 0); + this.concurrencyLevel = concurrencyLevel; + return this; + } + + int getConcurrencyLevel() { + return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel; + } + + /** + * Specifies that each key (not value) stored in the map should be wrapped in a {@link + * WeakReference} (by default, strong references are used). + * + *

Warning: when this method is used, the resulting map will use identity ({@code ==}) + * comparison to determine equality of keys, which is a technical violation of the {@link Map} + * specification, and may not be what you expect. + * + * @throws IllegalStateException if the key strength was already set + * @see WeakReference + */ + @GwtIncompatible // java.lang.ref.WeakReference + public MapMaker weakKeys() { + return setKeyStrength(Strength.WEAK); + } + + MapMaker setKeyStrength(Strength strength) { + checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); + keyStrength = checkNotNull(strength); + if (strength != Strength.STRONG) { + // STRONG could be used during deserialization. + useCustomMap = true; + } + return this; + } + + Strength getKeyStrength() { + return MoreObjects.firstNonNull(keyStrength, Strength.STRONG); + } + + /** + * Specifies that each value (not key) stored in the map should be wrapped in a {@link + * WeakReference} (by default, strong references are used). + * + *

Weak values will be garbage collected once they are weakly reachable. This makes them a poor + * candidate for caching. + * + *

Warning: when this method is used, the resulting map will use identity ({@code ==}) + * comparison to determine equality of values. This technically violates the specifications of the + * methods {@link Map#containsValue containsValue}, {@link ConcurrentMap#remove(Object, Object) + * remove(Object, Object)} and {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, + * V)}, and may not be what you expect. + * + * @throws IllegalStateException if the value strength was already set + * @see WeakReference + */ + @GwtIncompatible // java.lang.ref.WeakReference + public MapMaker weakValues() { + return setValueStrength(Strength.WEAK); + } + + /** + * A dummy singleton value type used by {@link Interners}. + * + *

{@link MapMakerInternalMap} can optimize for memory usage in this case; see {@link + * MapMakerInternalMap#createWithDummyValues}. + */ + enum Dummy { + VALUE + } + + MapMaker setValueStrength(Strength strength) { + checkState(valueStrength == null, "Value strength was already set to %s", valueStrength); + valueStrength = checkNotNull(strength); + if (strength != Strength.STRONG) { + // STRONG could be used during deserialization. + useCustomMap = true; + } + return this; + } + + Strength getValueStrength() { + return MoreObjects.firstNonNull(valueStrength, Strength.STRONG); + } + + /** + * Builds a thread-safe map. This method does not alter the state of this {@code MapMaker} + * instance, so it can be invoked again to create multiple independent maps. + * + *

The bulk operations {@code putAll}, {@code equals}, and {@code clear} are not guaranteed to + * be performed atomically on the returned map. Additionally, {@code size} and {@code + * containsValue} are implemented as bulk read operations, and thus may fail to observe concurrent + * writes. + * + * @return a serializable concurrent map having the requested features + */ + public ConcurrentMap makeMap() { + if (!useCustomMap) { + return new ConcurrentHashMap<>(getInitialCapacity(), 0.75f, getConcurrencyLevel()); + } + return MapMakerInternalMap.create(this); + } + + /** + * Returns a string representation for this MapMaker instance. The exact form of the returned + * string is not specified. + */ + @Override + public String toString() { + MoreObjects.ToStringHelper s = MoreObjects.toStringHelper(this); + if (initialCapacity != UNSET_INT) { + s.add("initialCapacity", initialCapacity); + } + if (concurrencyLevel != UNSET_INT) { + s.add("concurrencyLevel", concurrencyLevel); + } + if (keyStrength != null) { + s.add("keyStrength", Ascii.toLowerCase(keyStrength.toString())); + } + if (valueStrength != null) { + s.add("valueStrength", Ascii.toLowerCase(valueStrength.toString())); + } + if (keyEquivalence != null) { + s.addValue("keyEquivalence"); + } + return s.toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapMakerInternalMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapMakerInternalMap.java new file mode 100644 index 0000000000000..c5808a7e50f72 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MapMakerInternalMap.java @@ -0,0 +1,2943 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.collect.MapMaker.Dummy; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.ReentrantLock; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * The concurrent hash map implementation built by {@link MapMaker}. + * + *

This implementation is heavily derived from revision 1.96 of ConcurrentHashMap.java. + * + * @param the type of the keys in the map + * @param the type of the values in the map + * @param the type of the {@link InternalEntry} entry implementation used internally + * @param the type of the {@link Segment} entry implementation used internally + * @author Bob Lee + * @author Charles Fry + * @author Doug Lea ({@code ConcurrentHashMap}) + */ +// TODO(kak): Consider removing @CanIgnoreReturnValue from this class. +@J2ktIncompatible +@GwtIncompatible +@SuppressWarnings({ + "GuardedBy", // TODO(b/35466881): Fix or suppress. + "nullness", // too much trouble for the payoff +}) +// TODO(cpovirk): Annotate for nullness. +class MapMakerInternalMap< + K, + V, + E extends MapMakerInternalMap.InternalEntry, + S extends MapMakerInternalMap.Segment> + extends AbstractMap implements ConcurrentMap, Serializable { + + /* + * The basic strategy is to subdivide the table among Segments, each of which itself is a + * concurrently readable hash table. The map supports non-blocking reads and concurrent writes + * across different segments. + * + * The page replacement algorithm's data structures are kept casually consistent with the map. The + * ordering of writes to a segment is sequentially consistent. An update to the map and recording + * of reads may not be immediately reflected on the algorithm's data structures. These structures + * are guarded by a lock and operations are applied in batches to avoid lock contention. The + * penalty of applying the batches is spread across threads so that the amortized cost is slightly + * higher than performing just the operation without enforcing the capacity constraint. + * + * This implementation uses a per-segment queue to record a memento of the additions, removals, + * and accesses that were performed on the map. The queue is drained on writes and when it exceeds + * its capacity threshold. + * + * The Least Recently Used page replacement algorithm was chosen due to its simplicity, high hit + * rate, and ability to be implemented with O(1) time complexity. The initial LRU implementation + * operates per-segment rather than globally for increased implementation simplicity. We expect + * the cache hit rate to be similar to that of a global LRU algorithm. + */ + + // Constants + + /** + * The maximum capacity, used if a higher value is implicitly specified by either of the + * constructors with arguments. MUST be a power of two no greater than {@code 1<<30} to ensure + * that entries are indexable using ints. + */ + static final int MAXIMUM_CAPACITY = Ints.MAX_POWER_OF_TWO; + + /** The maximum number of segments to allow; used to bound constructor arguments. */ + static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + + /** Number of (unsynchronized) retries in the containsValue method. */ + static final int CONTAINS_VALUE_RETRIES = 3; + + /** + * Number of cache access operations that can be buffered per segment before the cache's recency + * ordering information is updated. This is used to avoid lock contention by recording a memento + * of reads and delaying a lock acquisition until the threshold is crossed or a mutation occurs. + * + *

This must be a (2^n)-1 as it is used as a mask. + */ + static final int DRAIN_THRESHOLD = 0x3F; + + /** + * Maximum number of entries to be drained in a single cleanup run. This applies independently to + * the cleanup queue and both reference queues. + */ + // TODO(fry): empirically optimize this + static final int DRAIN_MAX = 16; + + // Fields + + /** + * Mask value for indexing into segments. The upper bits of a key's hash code are used to choose + * the segment. + */ + final transient int segmentMask; + + /** + * Shift value for indexing within segments. Helps prevent entries that end up in the same segment + * from also ending up in the same bucket. + */ + final transient int segmentShift; + + /** The segments, each of which is a specialized hash table. */ + final transient Segment[] segments; + + /** The concurrency level. */ + final int concurrencyLevel; + + /** Strategy for comparing keys. */ + final Equivalence keyEquivalence; + + /** Strategy for handling entries and segments in a type-safe and efficient manner. */ + final transient InternalEntryHelper entryHelper; + + /** + * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. + */ + private MapMakerInternalMap(MapMaker builder, InternalEntryHelper entryHelper) { + concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); + + keyEquivalence = builder.getKeyEquivalence(); + this.entryHelper = entryHelper; + + int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); + + // Find power-of-two sizes best matching arguments. Constraints: + // (segmentCount > concurrencyLevel) + int segmentShift = 0; + int segmentCount = 1; + while (segmentCount < concurrencyLevel) { + ++segmentShift; + segmentCount <<= 1; + } + this.segmentShift = 32 - segmentShift; + segmentMask = segmentCount - 1; + + this.segments = newSegmentArray(segmentCount); + + int segmentCapacity = initialCapacity / segmentCount; + if (segmentCapacity * segmentCount < initialCapacity) { + ++segmentCapacity; + } + + int segmentSize = 1; + while (segmentSize < segmentCapacity) { + segmentSize <<= 1; + } + + for (int i = 0; i < this.segments.length; ++i) { + this.segments[i] = createSegment(segmentSize); + } + } + + /** Returns a fresh {@link MapMakerInternalMap} as specified by the given {@code builder}. */ + static MapMakerInternalMap, ?> create( + MapMaker builder) { + if (builder.getKeyStrength() == Strength.STRONG + && builder.getValueStrength() == Strength.STRONG) { + return new MapMakerInternalMap<>(builder, StrongKeyStrongValueEntry.Helper.instance()); + } + if (builder.getKeyStrength() == Strength.STRONG + && builder.getValueStrength() == Strength.WEAK) { + return new MapMakerInternalMap<>(builder, StrongKeyWeakValueEntry.Helper.instance()); + } + if (builder.getKeyStrength() == Strength.WEAK + && builder.getValueStrength() == Strength.STRONG) { + return new MapMakerInternalMap<>(builder, WeakKeyStrongValueEntry.Helper.instance()); + } + if (builder.getKeyStrength() == Strength.WEAK && builder.getValueStrength() == Strength.WEAK) { + return new MapMakerInternalMap<>(builder, WeakKeyWeakValueEntry.Helper.instance()); + } + throw new AssertionError(); + } + + /** + * Returns a fresh {@link MapMakerInternalMap} with {@link MapMaker.Dummy} values but otherwise as + * specified by the given {@code builder}. The returned {@link MapMakerInternalMap} will be + * optimized to saved memory. Since {@link MapMaker.Dummy} is a singleton, we don't need to store + * any values at all. Because of this optimization, {@code build.getValueStrength()} must be + * {@link Strength#STRONG}. + * + *

This method is intended to only be used by the internal implementation of {@link Interners}, + * since a map of dummy values is the exact use case there. + */ + static + MapMakerInternalMap, ?> createWithDummyValues( + MapMaker builder) { + if (builder.getKeyStrength() == Strength.STRONG + && builder.getValueStrength() == Strength.STRONG) { + return new MapMakerInternalMap<>(builder, StrongKeyDummyValueEntry.Helper.instance()); + } + if (builder.getKeyStrength() == Strength.WEAK + && builder.getValueStrength() == Strength.STRONG) { + return new MapMakerInternalMap<>(builder, WeakKeyDummyValueEntry.Helper.instance()); + } + if (builder.getValueStrength() == Strength.WEAK) { + throw new IllegalArgumentException("Map cannot have both weak and dummy values"); + } + throw new AssertionError(); + } + + enum Strength { + STRONG { + @Override + Equivalence defaultEquivalence() { + return Equivalence.equals(); + } + }, + + WEAK { + @Override + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }; + + /** + * Returns the default equivalence strategy used to compare and hash keys or values referenced + * at this strength. This strategy will be used unless the user explicitly specifies an + * alternate strategy. + */ + abstract Equivalence defaultEquivalence(); + } + + /** + * A helper object for operating on {@link InternalEntry} instances in a type-safe and efficient + * manner. + * + *

For each of the four combinations of strong/weak key and strong/weak value, there are + * corresponding {@link InternalEntry}, {@link Segment}, and {@link InternalEntryHelper} + * implementations. + * + * @param the type of the key in each entry + * @param the type of the value in each entry + * @param the type of the {@link InternalEntry} entry implementation + * @param the type of the {@link Segment} entry implementation + */ + interface InternalEntryHelper< + K, V, E extends InternalEntry, S extends Segment> { + /** The strength of the key type in each entry. */ + Strength keyStrength(); + + /** The strength of the value type in each entry. */ + Strength valueStrength(); + + /** Returns a freshly created segment, typed at the {@code S} type. */ + S newSegment(MapMakerInternalMap map, int initialCapacity); + + /** + * Returns a freshly created entry, typed at the {@code E} type, for the given {@code segment}. + */ + E newEntry(S segment, K key, int hash, E next); + + /** + * Returns a freshly created entry, typed at the {@code E} type, for the given {@code segment}, + * that is a copy of the given {@code entry}. + */ + E copy(S segment, E entry, E newNext); + + /** + * Sets the value of the given {@code entry} in the given {@code segment} to be the given {@code + * value} + */ + void setValue(S segment, E entry, V value); + } + + /** + * An entry in a hash table of a {@link Segment}. + * + *

Entries in the map can be in the following states: + * + *

Valid: - Live: valid key/value are set + * + *

Invalid: - Collected: key/value was partially collected, but not yet cleaned up + */ + interface InternalEntry> { + /** Gets the next entry in the chain. */ + E getNext(); + + /** Gets the entry's hash. */ + int getHash(); + + /** Gets the key for this entry. */ + K getKey(); + + /** Gets the value for the entry. */ + V getValue(); + } + + /* + * Note: the following classes have a lot of duplicate code. It sucks, but it saves a lot of + * memory. If only Java had mixins! + */ + + /** Base class for {@link InternalEntry} implementations for strong keys. */ + abstract static class AbstractStrongKeyEntry> + implements InternalEntry { + final K key; + final int hash; + + AbstractStrongKeyEntry(K key, int hash) { + this.key = key; + this.hash = hash; + } + + @Override + public final K getKey() { + return key; + } + + @Override + public final int getHash() { + return hash; + } + + @Override + public E getNext() { + return null; + } + } + + /** Marker interface for {@link InternalEntry} implementations for strong values. */ + interface StrongValueEntry> + extends InternalEntry {} + + /** Marker interface for {@link InternalEntry} implementations for weak values. */ + interface WeakValueEntry> extends InternalEntry { + /** Gets the weak value reference held by entry. */ + WeakValueReference getValueReference(); + } + + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + static > + WeakValueReference unsetWeakValueReference() { + return (WeakValueReference) UNSET_WEAK_VALUE_REFERENCE; + } + + /** Concrete implementation of {@link InternalEntry} for strong keys and strong values. */ + static class StrongKeyStrongValueEntry + extends AbstractStrongKeyEntry> + implements StrongValueEntry> { + private volatile V value = null; + + private StrongKeyStrongValueEntry(K key, int hash) { + super(key, hash); + } + + @Override + public final V getValue() { + return value; + } + + private static final class LinkedStrongKeyStrongValueEntry + extends StrongKeyStrongValueEntry { + private final StrongKeyStrongValueEntry next; + + LinkedStrongKeyStrongValueEntry(K key, int hash, StrongKeyStrongValueEntry next) { + super(key, hash); + this.next = next; + } + + @Override + public StrongKeyStrongValueEntry getNext() { + return next; + } + } + + /** Concrete implementation of {@link InternalEntryHelper} for strong keys and strong values. */ + static final class Helper + implements InternalEntryHelper< + K, V, StrongKeyStrongValueEntry, StrongKeyStrongValueSegment> { + private static final Helper INSTANCE = new Helper<>(); + + @SuppressWarnings("unchecked") + static Helper instance() { + return (Helper) INSTANCE; + } + + @Override + public Strength keyStrength() { + return Strength.STRONG; + } + + @Override + public Strength valueStrength() { + return Strength.STRONG; + } + + @Override + public StrongKeyStrongValueSegment newSegment( + MapMakerInternalMap< + K, V, StrongKeyStrongValueEntry, StrongKeyStrongValueSegment> + map, + int initialCapacity) { + return new StrongKeyStrongValueSegment<>(map, initialCapacity); + } + + @Override + public StrongKeyStrongValueEntry copy( + StrongKeyStrongValueSegment segment, + StrongKeyStrongValueEntry entry, + StrongKeyStrongValueEntry newNext) { + StrongKeyStrongValueEntry newEntry = + newEntry(segment, entry.key, entry.hash, newNext); + newEntry.value = entry.value; + return newEntry; + } + + @Override + public void setValue( + StrongKeyStrongValueSegment segment, + StrongKeyStrongValueEntry entry, + V value) { + entry.value = value; + } + + @Override + public StrongKeyStrongValueEntry newEntry( + StrongKeyStrongValueSegment segment, + K key, + int hash, + StrongKeyStrongValueEntry next) { + return next == null + ? new StrongKeyStrongValueEntry<>(key, hash) + : new LinkedStrongKeyStrongValueEntry<>(key, hash, next); + } + } + } + + /** Concrete implementation of {@link InternalEntry} for strong keys and weak values. */ + static class StrongKeyWeakValueEntry + extends AbstractStrongKeyEntry> + implements WeakValueEntry> { + private volatile WeakValueReference> valueReference = + unsetWeakValueReference(); + + private StrongKeyWeakValueEntry(K key, int hash) { + super(key, hash); + } + + @Override + public final V getValue() { + return valueReference.get(); + } + + @Override + public final WeakValueReference> getValueReference() { + return valueReference; + } + + private static final class LinkedStrongKeyWeakValueEntry + extends StrongKeyWeakValueEntry { + private final StrongKeyWeakValueEntry next; + + LinkedStrongKeyWeakValueEntry(K key, int hash, StrongKeyWeakValueEntry next) { + super(key, hash); + this.next = next; + } + + @Override + public StrongKeyWeakValueEntry getNext() { + return next; + } + } + + /** Concrete implementation of {@link InternalEntryHelper} for strong keys and weak values. */ + static final class Helper + implements InternalEntryHelper< + K, V, StrongKeyWeakValueEntry, StrongKeyWeakValueSegment> { + private static final Helper INSTANCE = new Helper<>(); + + @SuppressWarnings("unchecked") + static Helper instance() { + return (Helper) INSTANCE; + } + + @Override + public Strength keyStrength() { + return Strength.STRONG; + } + + @Override + public Strength valueStrength() { + return Strength.WEAK; + } + + @Override + public StrongKeyWeakValueSegment newSegment( + MapMakerInternalMap, StrongKeyWeakValueSegment> + map, + int initialCapacity) { + return new StrongKeyWeakValueSegment<>(map, initialCapacity); + } + + @Override + public StrongKeyWeakValueEntry copy( + StrongKeyWeakValueSegment segment, + StrongKeyWeakValueEntry entry, + StrongKeyWeakValueEntry newNext) { + if (Segment.isCollected(entry)) { + return null; + } + StrongKeyWeakValueEntry newEntry = newEntry(segment, entry.key, entry.hash, newNext); + newEntry.valueReference = entry.valueReference.copyFor(segment.queueForValues, newEntry); + return newEntry; + } + + @Override + public void setValue( + StrongKeyWeakValueSegment segment, StrongKeyWeakValueEntry entry, V value) { + WeakValueReference> previous = entry.valueReference; + entry.valueReference = new WeakValueReferenceImpl<>(segment.queueForValues, value, entry); + previous.clear(); + } + + @Override + public StrongKeyWeakValueEntry newEntry( + StrongKeyWeakValueSegment segment, + K key, + int hash, + StrongKeyWeakValueEntry next) { + return next == null + ? new StrongKeyWeakValueEntry<>(key, hash) + : new LinkedStrongKeyWeakValueEntry<>(key, hash, next); + } + } + } + + /** Concrete implementation of {@link InternalEntry} for strong keys and {@link Dummy} values. */ + static class StrongKeyDummyValueEntry + extends AbstractStrongKeyEntry> + implements StrongValueEntry> { + + private StrongKeyDummyValueEntry(K key, int hash) { + super(key, hash); + } + + @Override + public final Dummy getValue() { + return Dummy.VALUE; + } + + private static final class LinkedStrongKeyDummyValueEntry + extends StrongKeyDummyValueEntry { + private final StrongKeyDummyValueEntry next; + + LinkedStrongKeyDummyValueEntry(K key, int hash, StrongKeyDummyValueEntry next) { + super(key, hash); + this.next = next; + } + + @Override + public StrongKeyDummyValueEntry getNext() { + return next; + } + } + + /** + * Concrete implementation of {@link InternalEntryHelper} for strong keys and {@link Dummy} + * values. + */ + static final class Helper + implements InternalEntryHelper< + K, Dummy, StrongKeyDummyValueEntry, StrongKeyDummyValueSegment> { + private static final Helper INSTANCE = new Helper<>(); + + @SuppressWarnings("unchecked") + static Helper instance() { + return (Helper) INSTANCE; + } + + @Override + public Strength keyStrength() { + return Strength.STRONG; + } + + @Override + public Strength valueStrength() { + return Strength.STRONG; + } + + @Override + public StrongKeyDummyValueSegment newSegment( + MapMakerInternalMap, StrongKeyDummyValueSegment> + map, + int initialCapacity) { + return new StrongKeyDummyValueSegment(map, initialCapacity); + } + + @Override + public StrongKeyDummyValueEntry copy( + StrongKeyDummyValueSegment segment, + StrongKeyDummyValueEntry entry, + StrongKeyDummyValueEntry newNext) { + return newEntry(segment, entry.key, entry.hash, newNext); + } + + @Override + public void setValue( + StrongKeyDummyValueSegment segment, StrongKeyDummyValueEntry entry, Dummy value) {} + + @Override + public StrongKeyDummyValueEntry newEntry( + StrongKeyDummyValueSegment segment, + K key, + int hash, + StrongKeyDummyValueEntry next) { + return next == null + ? new StrongKeyDummyValueEntry(key, hash) + : new LinkedStrongKeyDummyValueEntry<>(key, hash, next); + } + } + } + + /** Base class for {@link InternalEntry} implementations for weak keys. */ + abstract static class AbstractWeakKeyEntry> + extends WeakReference implements InternalEntry { + final int hash; + + AbstractWeakKeyEntry(ReferenceQueue queue, K key, int hash) { + super(key, queue); + this.hash = hash; + } + + @Override + public final K getKey() { + return get(); + } + + @Override + public final int getHash() { + return hash; + } + + @Override + public E getNext() { + return null; + } + } + + /** Concrete implementation of {@link InternalEntry} for weak keys and {@link Dummy} values. */ + static class WeakKeyDummyValueEntry + extends AbstractWeakKeyEntry> + implements StrongValueEntry> { + + private WeakKeyDummyValueEntry(ReferenceQueue queue, K key, int hash) { + super(queue, key, hash); + } + + @Override + public final Dummy getValue() { + return Dummy.VALUE; + } + + private static final class LinkedWeakKeyDummyValueEntry extends WeakKeyDummyValueEntry { + private final WeakKeyDummyValueEntry next; + + private LinkedWeakKeyDummyValueEntry( + ReferenceQueue queue, K key, int hash, WeakKeyDummyValueEntry next) { + super(queue, key, hash); + this.next = next; + } + + @Override + public WeakKeyDummyValueEntry getNext() { + return next; + } + } + + /** + * Concrete implementation of {@link InternalEntryHelper} for weak keys and {@link Dummy} + * values. + */ + static final class Helper + implements InternalEntryHelper< + K, Dummy, WeakKeyDummyValueEntry, WeakKeyDummyValueSegment> { + private static final Helper INSTANCE = new Helper<>(); + + @SuppressWarnings("unchecked") + static Helper instance() { + return (Helper) INSTANCE; + } + + @Override + public Strength keyStrength() { + return Strength.WEAK; + } + + @Override + public Strength valueStrength() { + return Strength.STRONG; + } + + @Override + public WeakKeyDummyValueSegment newSegment( + MapMakerInternalMap, WeakKeyDummyValueSegment> map, + int initialCapacity) { + return new WeakKeyDummyValueSegment<>(map, initialCapacity); + } + + @Override + public WeakKeyDummyValueEntry copy( + WeakKeyDummyValueSegment segment, + WeakKeyDummyValueEntry entry, + WeakKeyDummyValueEntry newNext) { + K key = entry.getKey(); + if (key == null) { + // key collected + return null; + } + return newEntry(segment, key, entry.hash, newNext); + } + + @Override + public void setValue( + WeakKeyDummyValueSegment segment, WeakKeyDummyValueEntry entry, Dummy value) {} + + @Override + public WeakKeyDummyValueEntry newEntry( + WeakKeyDummyValueSegment segment, K key, int hash, WeakKeyDummyValueEntry next) { + return next == null + ? new WeakKeyDummyValueEntry<>(segment.queueForKeys, key, hash) + : new LinkedWeakKeyDummyValueEntry<>(segment.queueForKeys, key, hash, next); + } + } + } + + /** Concrete implementation of {@link InternalEntry} for weak keys and strong values. */ + static class WeakKeyStrongValueEntry + extends AbstractWeakKeyEntry> + implements StrongValueEntry> { + private volatile V value = null; + + private WeakKeyStrongValueEntry(ReferenceQueue queue, K key, int hash) { + super(queue, key, hash); + } + + @Override + public final V getValue() { + return value; + } + + private static final class LinkedWeakKeyStrongValueEntry + extends WeakKeyStrongValueEntry { + private final WeakKeyStrongValueEntry next; + + private LinkedWeakKeyStrongValueEntry( + ReferenceQueue queue, K key, int hash, WeakKeyStrongValueEntry next) { + super(queue, key, hash); + this.next = next; + } + + @Override + public WeakKeyStrongValueEntry getNext() { + return next; + } + } + + /** Concrete implementation of {@link InternalEntryHelper} for weak keys and strong values. */ + static final class Helper + implements InternalEntryHelper< + K, V, WeakKeyStrongValueEntry, WeakKeyStrongValueSegment> { + private static final Helper INSTANCE = new Helper<>(); + + @SuppressWarnings("unchecked") + static Helper instance() { + return (Helper) INSTANCE; + } + + @Override + public Strength keyStrength() { + return Strength.WEAK; + } + + @Override + public Strength valueStrength() { + return Strength.STRONG; + } + + @Override + public WeakKeyStrongValueSegment newSegment( + MapMakerInternalMap, WeakKeyStrongValueSegment> + map, + int initialCapacity) { + return new WeakKeyStrongValueSegment<>(map, initialCapacity); + } + + @Override + public WeakKeyStrongValueEntry copy( + WeakKeyStrongValueSegment segment, + WeakKeyStrongValueEntry entry, + WeakKeyStrongValueEntry newNext) { + K key = entry.getKey(); + if (key == null) { + // key collected + return null; + } + WeakKeyStrongValueEntry newEntry = newEntry(segment, key, entry.hash, newNext); + newEntry.value = entry.value; + return newEntry; + } + + @Override + public void setValue( + WeakKeyStrongValueSegment segment, WeakKeyStrongValueEntry entry, V value) { + entry.value = value; + } + + @Override + public WeakKeyStrongValueEntry newEntry( + WeakKeyStrongValueSegment segment, + K key, + int hash, + WeakKeyStrongValueEntry next) { + return next == null + ? new WeakKeyStrongValueEntry<>(segment.queueForKeys, key, hash) + : new LinkedWeakKeyStrongValueEntry<>(segment.queueForKeys, key, hash, next); + } + } + } + + /** Concrete implementation of {@link InternalEntry} for weak keys and weak values. */ + static class WeakKeyWeakValueEntry + extends AbstractWeakKeyEntry> + implements WeakValueEntry> { + private volatile WeakValueReference> valueReference = + unsetWeakValueReference(); + + WeakKeyWeakValueEntry(ReferenceQueue queue, K key, int hash) { + super(queue, key, hash); + } + + @Override + public final V getValue() { + return valueReference.get(); + } + + @Override + public final WeakValueReference> getValueReference() { + return valueReference; + } + + private static final class LinkedWeakKeyWeakValueEntry + extends WeakKeyWeakValueEntry { + private final WeakKeyWeakValueEntry next; + + LinkedWeakKeyWeakValueEntry( + ReferenceQueue queue, K key, int hash, WeakKeyWeakValueEntry next) { + super(queue, key, hash); + this.next = next; + } + + @Override + public WeakKeyWeakValueEntry getNext() { + return next; + } + } + + /** Concrete implementation of {@link InternalEntryHelper} for weak keys and weak values. */ + static final class Helper + implements InternalEntryHelper< + K, V, WeakKeyWeakValueEntry, WeakKeyWeakValueSegment> { + private static final Helper INSTANCE = new Helper<>(); + + @SuppressWarnings("unchecked") + static Helper instance() { + return (Helper) INSTANCE; + } + + @Override + public Strength keyStrength() { + return Strength.WEAK; + } + + @Override + public Strength valueStrength() { + return Strength.WEAK; + } + + @Override + public WeakKeyWeakValueSegment newSegment( + MapMakerInternalMap, WeakKeyWeakValueSegment> map, + int initialCapacity) { + return new WeakKeyWeakValueSegment<>(map, initialCapacity); + } + + @Override + public WeakKeyWeakValueEntry copy( + WeakKeyWeakValueSegment segment, + WeakKeyWeakValueEntry entry, + WeakKeyWeakValueEntry newNext) { + K key = entry.getKey(); + if (key == null) { + // key collected + return null; + } + if (Segment.isCollected(entry)) { + return null; + } + WeakKeyWeakValueEntry newEntry = newEntry(segment, key, entry.hash, newNext); + newEntry.valueReference = entry.valueReference.copyFor(segment.queueForValues, newEntry); + return newEntry; + } + + @Override + public void setValue( + WeakKeyWeakValueSegment segment, WeakKeyWeakValueEntry entry, V value) { + WeakValueReference> previous = entry.valueReference; + entry.valueReference = new WeakValueReferenceImpl<>(segment.queueForValues, value, entry); + previous.clear(); + } + + @Override + public WeakKeyWeakValueEntry newEntry( + WeakKeyWeakValueSegment segment, + K key, + int hash, + WeakKeyWeakValueEntry next) { + return next == null + ? new WeakKeyWeakValueEntry<>(segment.queueForKeys, key, hash) + : new LinkedWeakKeyWeakValueEntry<>(segment.queueForKeys, key, hash, next); + } + } + } + + /** A weakly referenced value that also has a reference to its containing entry. */ + interface WeakValueReference> { + /** + * Returns the current value being referenced, or {@code null} if there is none (e.g. because + * either it got collected, or {@link #clear} was called, or it wasn't set in the first place). + */ + V get(); + + /** Returns the entry which contains this {@link WeakValueReference}. */ + E getEntry(); + + /** Unsets the referenced value. Subsequent calls to {@link #get} will return {@code null}. */ + void clear(); + + /** + * Returns a freshly created {@link WeakValueReference} for the given {@code entry} (and on the + * given {@code queue}) with the same value as this {@link WeakValueReference}. + */ + WeakValueReference copyFor(ReferenceQueue queue, E entry); + } + + /** + * A dummy implementation of {@link InternalEntry}, solely for use in the type signature of {@link + * #UNSET_WEAK_VALUE_REFERENCE} below. + */ + static final class DummyInternalEntry + implements InternalEntry { + private DummyInternalEntry() { + throw new AssertionError(); + } + + @Override + public DummyInternalEntry getNext() { + throw new AssertionError(); + } + + @Override + public int getHash() { + throw new AssertionError(); + } + + @Override + public Object getKey() { + throw new AssertionError(); + } + + @Override + public Object getValue() { + throw new AssertionError(); + } + } + + /** + * A singleton {@link WeakValueReference} used to denote an unset value in an entry with weak + * values. + */ + static final WeakValueReference UNSET_WEAK_VALUE_REFERENCE = + new WeakValueReference() { + @Override + public DummyInternalEntry getEntry() { + return null; + } + + @Override + public void clear() {} + + @Override + public Object get() { + return null; + } + + @Override + public WeakValueReference copyFor( + ReferenceQueue queue, DummyInternalEntry entry) { + return this; + } + }; + + /** Concrete implementation of {@link WeakValueReference}. */ + static final class WeakValueReferenceImpl> + extends WeakReference implements WeakValueReference { + final E entry; + + WeakValueReferenceImpl(ReferenceQueue queue, V referent, E entry) { + super(referent, queue); + this.entry = entry; + } + + @Override + public E getEntry() { + return entry; + } + + @Override + public WeakValueReference copyFor(ReferenceQueue queue, E entry) { + return new WeakValueReferenceImpl<>(queue, get(), entry); + } + } + + /** + * Applies a supplemental hash function to a given hash code, which defends against poor quality + * hash functions. This is critical when the concurrent hash map uses power-of-two length hash + * tables, that otherwise encounter collisions for hash codes that do not differ in lower or upper + * bits. + * + * @param h hash code + */ + static int rehash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + // TODO(kevinb): use Hashing/move this to Hashing? + h += (h << 15) ^ 0xffffcd7d; + h ^= (h >>> 10); + h += (h << 3); + h ^= (h >>> 6); + h += (h << 2) + (h << 14); + return h ^ (h >>> 16); + } + + /** + * This method is a convenience for testing. Code should call {@link Segment#copyEntry} directly. + */ + // Guarded By Segment.this + @VisibleForTesting + E copyEntry(E original, E newNext) { + int hash = original.getHash(); + return segmentFor(hash).copyEntry(original, newNext); + } + + int hash(Object key) { + int h = keyEquivalence.hash(key); + return rehash(h); + } + + void reclaimValue(WeakValueReference valueReference) { + E entry = valueReference.getEntry(); + int hash = entry.getHash(); + segmentFor(hash).reclaimValue(entry.getKey(), hash, valueReference); + } + + void reclaimKey(E entry) { + int hash = entry.getHash(); + segmentFor(hash).reclaimKey(entry, hash); + } + + /** + * This method is a convenience for testing. Code should call {@link Segment#getLiveValue} + * instead. + */ + @VisibleForTesting + boolean isLiveForTesting(InternalEntry entry) { + return segmentFor(entry.getHash()).getLiveValueForTesting(entry) != null; + } + + /** + * Returns the segment that should be used for a key with the given hash. + * + * @param hash the hash code for the key + * @return the segment + */ + Segment segmentFor(int hash) { + // TODO(fry): Lazily create segments? + return segments[(hash >>> segmentShift) & segmentMask]; + } + + Segment createSegment(int initialCapacity) { + return entryHelper.newSegment(this, initialCapacity); + } + + /** + * Gets the value from an entry. Returns {@code null} if the entry is invalid, partially-collected + * or computing. + */ + V getLiveValue(E entry) { + if (entry.getKey() == null) { + return null; + } + return entry.getValue(); + } + + @SuppressWarnings("unchecked") + final Segment[] newSegmentArray(int ssize) { + return new Segment[ssize]; + } + + // Inner Classes + + /** + * Segments are specialized versions of hash tables. This subclass inherits from ReentrantLock + * opportunistically, just to simplify some locking and avoid separate construction. + */ + @SuppressWarnings("serial") // This class is never serialized. + abstract static class Segment< + K, V, E extends InternalEntry, S extends Segment> + extends ReentrantLock { + + /* + * Segments maintain a table of entry lists that are ALWAYS kept in a consistent state, so can + * be read without locking. Next fields of nodes are immutable (final). All list additions are + * performed at the front of each bin. This makes it easy to check changes, and also fast to + * traverse. When nodes would otherwise be changed, new nodes are created to replace them. This + * works well for hash tables since the bin lists tend to be short. (The average length is less + * than two.) + * + * Read operations can thus proceed without locking, but rely on selected uses of volatiles to + * ensure that completed write operations performed by other threads are noticed. For most + * purposes, the "count" field, tracking the number of elements, serves as that volatile + * variable ensuring visibility. This is convenient because this field needs to be read in many + * read operations anyway: + * + * - All (unsynchronized) read operations must first read the "count" field, and should not + * look at table entries if it is 0. + * + * - All (synchronized) write operations should write to the "count" field after structurally + * changing any bin. The operations must not take any action that could even momentarily + * cause a concurrent read operation to see inconsistent data. This is made easier by the + * nature of the read operations in Map. For example, no operation can reveal that the table + * has grown but the threshold has not yet been updated, so there are no atomicity requirements + * for this with respect to reads. + * + * As a guide, all critical volatile reads and writes to the count field are marked in code + * comments. + */ + + final MapMakerInternalMap map; + + /** + * The number of live elements in this segment's region. This does not include unset elements + * which are awaiting cleanup. + */ + volatile int count; + + /** + * Number of updates that alter the size of the table. This is used during bulk-read methods to + * make sure they see a consistent snapshot: If modCounts change during a traversal of segments + * computing size or checking containsValue, then we might have an inconsistent view of state so + * (usually) must retry. + */ + int modCount; + + /** + * The table is expanded when its size exceeds this threshold. (The value of this field is + * always {@code (int) (capacity * 0.75)}.) + */ + int threshold; + + /** The per-segment table. */ + volatile AtomicReferenceArray table; + + /** + * A counter of the number of reads since the last write, used to drain queues on a small + * fraction of read operations. + */ + final AtomicInteger readCount = new AtomicInteger(); + + Segment(MapMakerInternalMap map, int initialCapacity) { + this.map = map; + initTable(newEntryArray(initialCapacity)); + } + + /** + * Returns {@code this} up-casted to the specific {@link Segment} implementation type {@code S}. + * + *

This method exists so that the {@link Segment} code can be generic in terms of {@code S}, + * the type of the concrete implementation. + */ + abstract S self(); + + /** Drains the reference queues used by this segment, if any. */ + void maybeDrainReferenceQueues() {} + + /** Clears the reference queues used by this segment, if any. */ + void maybeClearReferenceQueues() {} + + /** Sets the value of the given {@code entry}. */ + void setValue(E entry, V value) { + this.map.entryHelper.setValue(self(), entry, value); + } + + /** Returns a copy of the given {@code entry}. */ + E copyEntry(E original, E newNext) { + return this.map.entryHelper.copy(self(), original, newNext); + } + + AtomicReferenceArray newEntryArray(int size) { + return new AtomicReferenceArray(size); + } + + void initTable(AtomicReferenceArray newTable) { + this.threshold = newTable.length() * 3 / 4; // 0.75 + this.table = newTable; + } + + // Convenience methods for testing + + /** + * Unsafe cast of the given entry to {@code E}, the type of the specific {@link InternalEntry} + * implementation type. + * + *

This method is provided as a convenience for tests. Otherwise they'd need to be + * knowledgeable about all the implementation details of our type system trickery. + */ + abstract E castForTesting(InternalEntry entry); + + /** Unsafely extracts the key reference queue used by this segment. */ + ReferenceQueue getKeyReferenceQueueForTesting() { + throw new AssertionError(); + } + + /** Unsafely extracts the value reference queue used by this segment. */ + ReferenceQueue getValueReferenceQueueForTesting() { + throw new AssertionError(); + } + + /** Unsafely extracts the weak value reference inside of the given {@code entry}. */ + WeakValueReference getWeakValueReferenceForTesting(InternalEntry entry) { + throw new AssertionError(); + } + + /** + * Unsafely creates of a fresh {@link WeakValueReference}, referencing the given {@code value}, + * for the given {@code entry} + */ + WeakValueReference newWeakValueReferenceForTesting( + InternalEntry entry, V value) { + throw new AssertionError(); + } + + /** + * Unsafely sets the weak value reference inside the given {@code entry} to be the given {@code + * valueReference} + */ + void setWeakValueReferenceForTesting( + InternalEntry entry, + WeakValueReference> valueReference) { + throw new AssertionError(); + } + + /** + * Unsafely sets the given index of this segment's internal hash table to be the given entry. + */ + void setTableEntryForTesting(int i, InternalEntry entry) { + table.set(i, castForTesting(entry)); + } + + /** Unsafely returns a copy of the given entry. */ + E copyForTesting(InternalEntry entry, InternalEntry newNext) { + return this.map.entryHelper.copy(self(), castForTesting(entry), castForTesting(newNext)); + } + + /** Unsafely sets the value of the given entry. */ + void setValueForTesting(InternalEntry entry, V value) { + this.map.entryHelper.setValue(self(), castForTesting(entry), value); + } + + /** Unsafely returns a fresh entry. */ + E newEntryForTesting(K key, int hash, InternalEntry next) { + return this.map.entryHelper.newEntry(self(), key, hash, castForTesting(next)); + } + + /** Unsafely removes the given entry from this segment's hash table. */ + boolean removeTableEntryForTesting(InternalEntry entry) { + return removeEntryForTesting(castForTesting(entry)); + } + + /** Unsafely removes the given entry from the given chain in this segment's hash table. */ + E removeFromChainForTesting(InternalEntry first, InternalEntry entry) { + return removeFromChain(castForTesting(first), castForTesting(entry)); + } + + /** + * Unsafely returns the value of the given entry if it's still live, or {@code null} otherwise. + */ + V getLiveValueForTesting(InternalEntry entry) { + return getLiveValue(castForTesting(entry)); + } + + // reference queues, for garbage collection cleanup + + /** Cleanup collected entries when the lock is available. */ + void tryDrainReferenceQueues() { + if (tryLock()) { + try { + maybeDrainReferenceQueues(); + } finally { + unlock(); + } + } + } + + void drainKeyReferenceQueue(ReferenceQueue keyReferenceQueue) { + Reference ref; + int i = 0; + while ((ref = keyReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + E entry = (E) ref; + map.reclaimKey(entry); + if (++i == DRAIN_MAX) { + break; + } + } + } + + void drainValueReferenceQueue(ReferenceQueue valueReferenceQueue) { + Reference ref; + int i = 0; + while ((ref = valueReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + WeakValueReference valueReference = (WeakValueReference) ref; + map.reclaimValue(valueReference); + if (++i == DRAIN_MAX) { + break; + } + } + } + + void clearReferenceQueue(ReferenceQueue referenceQueue) { + while (referenceQueue.poll() != null) {} + } + + /** Returns first entry of bin for given hash. */ + E getFirst(int hash) { + // read this volatile field only once + AtomicReferenceArray table = this.table; + return table.get(hash & (table.length() - 1)); + } + + // Specialized implementations of map methods + + E getEntry(Object key, int hash) { + if (count != 0) { // read-volatile + for (E e = getFirst(hash); e != null; e = e.getNext()) { + if (e.getHash() != hash) { + continue; + } + + K entryKey = e.getKey(); + if (entryKey == null) { + tryDrainReferenceQueues(); + continue; + } + + if (map.keyEquivalence.equivalent(key, entryKey)) { + return e; + } + } + } + + return null; + } + + E getLiveEntry(Object key, int hash) { + return getEntry(key, hash); + } + + V get(Object key, int hash) { + try { + E e = getLiveEntry(key, hash); + if (e == null) { + return null; + } + + V value = e.getValue(); + if (value == null) { + tryDrainReferenceQueues(); + } + return value; + } finally { + postReadCleanup(); + } + } + + boolean containsKey(Object key, int hash) { + try { + if (count != 0) { // read-volatile + E e = getLiveEntry(key, hash); + return e != null && e.getValue() != null; + } + + return false; + } finally { + postReadCleanup(); + } + } + + /** + * This method is a convenience for testing. Code should call {@link + * MapMakerInternalMap#containsValue} directly. + */ + @VisibleForTesting + boolean containsValue(Object value) { + try { + if (count != 0) { // read-volatile + AtomicReferenceArray table = this.table; + int length = table.length(); + for (int i = 0; i < length; ++i) { + for (E e = table.get(i); e != null; e = e.getNext()) { + V entryValue = getLiveValue(e); + if (entryValue == null) { + continue; + } + if (map.valueEquivalence().equivalent(value, entryValue)) { + return true; + } + } + } + } + + return false; + } finally { + postReadCleanup(); + } + } + + V put(K key, int hash, V value, boolean onlyIfAbsent) { + lock(); + try { + preWriteCleanup(); + + int newCount = this.count + 1; + if (newCount > this.threshold) { // ensure capacity + expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + // Look for an existing entry. + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // We found an existing entry. + + V entryValue = e.getValue(); + + if (entryValue == null) { + ++modCount; + setValue(e, value); + newCount = this.count; // count remains unchanged + this.count = newCount; // write-volatile + return null; + } else if (onlyIfAbsent) { + // Mimic + // "if (!map.containsKey(key)) ... + // else return map.get(key); + return entryValue; + } else { + // clobber existing entry, count remains unchanged + ++modCount; + setValue(e, value); + return entryValue; + } + } + } + + // Create a new entry. + ++modCount; + E newEntry = map.entryHelper.newEntry(self(), key, hash, first); + setValue(newEntry, value); + table.set(index, newEntry); + this.count = newCount; // write-volatile + return null; + } finally { + unlock(); + } + } + + /** Expands the table if possible. */ + void expand() { + AtomicReferenceArray oldTable = table; + int oldCapacity = oldTable.length(); + if (oldCapacity >= MAXIMUM_CAPACITY) { + return; + } + + /* + * Reclassify nodes in each list to new Map. Because we are using power-of-two expansion, the + * elements from each bin must either stay at same index, or move with a power of two offset. + * We eliminate unnecessary node creation by catching cases where old nodes can be reused + * because their next fields won't change. Statistically, at the default threshold, only + * about one-sixth of them need cloning when a table doubles. The nodes they replace will be + * garbage collectable as soon as they are no longer referenced by any reader thread that may + * be in the midst of traversing table right now. + */ + + int newCount = count; + AtomicReferenceArray newTable = newEntryArray(oldCapacity << 1); + threshold = newTable.length() * 3 / 4; + int newMask = newTable.length() - 1; + for (int oldIndex = 0; oldIndex < oldCapacity; ++oldIndex) { + // We need to guarantee that any existing reads of old Map can + // proceed. So we cannot yet null out each bin. + E head = oldTable.get(oldIndex); + + if (head != null) { + E next = head.getNext(); + int headIndex = head.getHash() & newMask; + + // Single node on list + if (next == null) { + newTable.set(headIndex, head); + } else { + // Reuse the consecutive sequence of nodes with the same target + // index from the end of the list. tail points to the first + // entry in the reusable list. + E tail = head; + int tailIndex = headIndex; + for (E e = next; e != null; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + if (newIndex != tailIndex) { + // The index changed. We'll need to copy the previous entry. + tailIndex = newIndex; + tail = e; + } + } + newTable.set(tailIndex, tail); + + // Clone nodes leading up to the tail. + for (E e = head; e != tail; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + E newNext = newTable.get(newIndex); + E newFirst = copyEntry(e, newNext); + if (newFirst != null) { + newTable.set(newIndex, newFirst); + } else { + newCount--; + } + } + } + } + } + table = newTable; + this.count = newCount; + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + lock(); + try { + preWriteCleanup(); + + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // If the value disappeared, this entry is partially collected, + // and we should pretend like it doesn't exist. + V entryValue = e.getValue(); + if (entryValue == null) { + if (isCollected(e)) { + int newCount = this.count - 1; + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return false; + } + + if (map.valueEquivalence().equivalent(oldValue, entryValue)) { + ++modCount; + setValue(e, newValue); + return true; + } else { + // Mimic + // "if (map.containsKey(key) && map.get(key).equals(oldValue))..." + return false; + } + } + } + + return false; + } finally { + unlock(); + } + } + + V replace(K key, int hash, V newValue) { + lock(); + try { + preWriteCleanup(); + + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // If the value disappeared, this entry is partially collected, + // and we should pretend like it doesn't exist. + V entryValue = e.getValue(); + if (entryValue == null) { + if (isCollected(e)) { + int newCount = this.count - 1; + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return null; + } + + ++modCount; + setValue(e, newValue); + return entryValue; + } + } + + return null; + } finally { + unlock(); + } + } + + V remove(Object key, int hash) { + lock(); + try { + preWriteCleanup(); + + int newCount = this.count - 1; + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + V entryValue = e.getValue(); + + if (entryValue != null) { + // TODO(kak): Remove this branch + } else if (isCollected(e)) { + // TODO(kak): Remove this branch + } else { + return null; + } + + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return entryValue; + } + } + + return null; + } finally { + unlock(); + } + } + + boolean remove(Object key, int hash, Object value) { + lock(); + try { + preWriteCleanup(); + + int newCount = this.count - 1; + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + V entryValue = e.getValue(); + + boolean explicitRemoval = false; + if (map.valueEquivalence().equivalent(value, entryValue)) { + explicitRemoval = true; + } else if (isCollected(e)) { + // TODO(kak): Remove this branch + } else { + return false; + } + + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return explicitRemoval; + } + } + + return false; + } finally { + unlock(); + } + } + + void clear() { + if (count != 0) { + lock(); + try { + AtomicReferenceArray table = this.table; + for (int i = 0; i < table.length(); ++i) { + table.set(i, null); + } + maybeClearReferenceQueues(); + readCount.set(0); + + ++modCount; + count = 0; // write-volatile + } finally { + unlock(); + } + } + } + + /** + * Removes an entry from within a table. All entries following the removed node can stay, but + * all preceding ones need to be cloned. + * + *

This method does not decrement count for the removed entry, but does decrement count for + * all partially collected entries which are skipped. As such callers which are modifying count + * must re-read it after calling removeFromChain. + * + * @param first the first entry of the table + * @param entry the entry being removed from the table + * @return the new first entry for the table + */ + E removeFromChain(E first, E entry) { + int newCount = count; + E newFirst = entry.getNext(); + for (E e = first; e != entry; e = e.getNext()) { + E next = copyEntry(e, newFirst); + if (next != null) { + newFirst = next; + } else { + newCount--; + } + } + this.count = newCount; + return newFirst; + } + + /** Removes an entry whose key has been garbage collected. */ + boolean reclaimKey(E entry, int hash) { + lock(); + try { + int newCount = count - 1; + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } finally { + unlock(); + } + } + + /** Removes an entry whose value has been garbage collected. */ + boolean reclaimValue(K key, int hash, WeakValueReference valueReference) { + lock(); + try { + int newCount = this.count - 1; + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + WeakValueReference v = ((WeakValueEntry) e).getValueReference(); + if (v == valueReference) { + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + } + } + + /** Clears a value that has not yet been set, and thus does not require count to be modified. */ + boolean clearValueForTesting( + K key, + int hash, + WeakValueReference> valueReference) { + lock(); + try { + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash + && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + WeakValueReference v = ((WeakValueEntry) e).getValueReference(); + if (v == valueReference) { + E newFirst = removeFromChain(first, e); + table.set(index, newFirst); + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + } + } + + boolean removeEntryForTesting(E entry) { + int hash = entry.getHash(); + int newCount = this.count - 1; + AtomicReferenceArray table = this.table; + int index = hash & (table.length() - 1); + E first = table.get(index); + + for (E e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + E newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } + + /** + * Returns {@code true} if the value has been partially collected, meaning that the value is + * null. + */ + static > boolean isCollected(E entry) { + return entry.getValue() == null; + } + + /** + * Gets the value from an entry. Returns {@code null} if the entry is invalid or + * partially-collected. + */ + V getLiveValue(E entry) { + if (entry.getKey() == null) { + tryDrainReferenceQueues(); + return null; + } + V value = entry.getValue(); + if (value == null) { + tryDrainReferenceQueues(); + return null; + } + + return value; + } + + /** + * Performs routine cleanup following a read. Normally cleanup happens during writes, or from + * the cleanupExecutor. If cleanup is not observed after a sufficient number of reads, try + * cleaning up from the read thread. + */ + void postReadCleanup() { + if ((readCount.incrementAndGet() & DRAIN_THRESHOLD) == 0) { + runCleanup(); + } + } + + /** + * Performs routine cleanup prior to executing a write. This should be called every time a write + * thread acquires the segment lock, immediately after acquiring the lock. + */ + void preWriteCleanup() { + runLockedCleanup(); + } + + void runCleanup() { + runLockedCleanup(); + } + + void runLockedCleanup() { + if (tryLock()) { + try { + maybeDrainReferenceQueues(); + readCount.set(0); + } finally { + unlock(); + } + } + } + } + + /** Concrete implementation of {@link Segment} for strong keys and strong values. */ + static final class StrongKeyStrongValueSegment + extends Segment, StrongKeyStrongValueSegment> { + StrongKeyStrongValueSegment( + MapMakerInternalMap< + K, V, StrongKeyStrongValueEntry, StrongKeyStrongValueSegment> + map, + int initialCapacity) { + super(map, initialCapacity); + } + + @Override + StrongKeyStrongValueSegment self() { + return this; + } + + @SuppressWarnings("unchecked") + @Override + public StrongKeyStrongValueEntry castForTesting(InternalEntry entry) { + return (StrongKeyStrongValueEntry) entry; + } + } + + /** Concrete implementation of {@link Segment} for strong keys and weak values. */ + static final class StrongKeyWeakValueSegment + extends Segment, StrongKeyWeakValueSegment> { + private final ReferenceQueue queueForValues = new ReferenceQueue(); + + StrongKeyWeakValueSegment( + MapMakerInternalMap, StrongKeyWeakValueSegment> + map, + int initialCapacity) { + super(map, initialCapacity); + } + + @Override + StrongKeyWeakValueSegment self() { + return this; + } + + @Override + ReferenceQueue getValueReferenceQueueForTesting() { + return queueForValues; + } + + @SuppressWarnings("unchecked") + @Override + public StrongKeyWeakValueEntry castForTesting(InternalEntry entry) { + return (StrongKeyWeakValueEntry) entry; + } + + @Override + public WeakValueReference> getWeakValueReferenceForTesting( + InternalEntry e) { + return castForTesting(e).getValueReference(); + } + + @Override + public WeakValueReference> newWeakValueReferenceForTesting( + InternalEntry e, V value) { + return new WeakValueReferenceImpl<>(queueForValues, value, castForTesting(e)); + } + + @Override + public void setWeakValueReferenceForTesting( + InternalEntry e, + WeakValueReference> valueReference) { + StrongKeyWeakValueEntry entry = castForTesting(e); + @SuppressWarnings("unchecked") + WeakValueReference> newValueReference = + (WeakValueReference>) valueReference; + WeakValueReference> previous = entry.valueReference; + entry.valueReference = newValueReference; + previous.clear(); + } + + @Override + void maybeDrainReferenceQueues() { + drainValueReferenceQueue(queueForValues); + } + + @Override + void maybeClearReferenceQueues() { + clearReferenceQueue(queueForValues); + } + } + + /** Concrete implementation of {@link Segment} for strong keys and {@link Dummy} values. */ + static final class StrongKeyDummyValueSegment + extends Segment, StrongKeyDummyValueSegment> { + StrongKeyDummyValueSegment( + MapMakerInternalMap, StrongKeyDummyValueSegment> + map, + int initialCapacity) { + super(map, initialCapacity); + } + + @Override + StrongKeyDummyValueSegment self() { + return this; + } + + @SuppressWarnings("unchecked") + @Override + public StrongKeyDummyValueEntry castForTesting(InternalEntry entry) { + return (StrongKeyDummyValueEntry) entry; + } + } + + /** Concrete implementation of {@link Segment} for weak keys and strong values. */ + static final class WeakKeyStrongValueSegment + extends Segment, WeakKeyStrongValueSegment> { + private final ReferenceQueue queueForKeys = new ReferenceQueue(); + + WeakKeyStrongValueSegment( + MapMakerInternalMap, WeakKeyStrongValueSegment> + map, + int initialCapacity) { + super(map, initialCapacity); + } + + @Override + WeakKeyStrongValueSegment self() { + return this; + } + + @Override + ReferenceQueue getKeyReferenceQueueForTesting() { + return queueForKeys; + } + + @SuppressWarnings("unchecked") + @Override + public WeakKeyStrongValueEntry castForTesting(InternalEntry entry) { + return (WeakKeyStrongValueEntry) entry; + } + + @Override + void maybeDrainReferenceQueues() { + drainKeyReferenceQueue(queueForKeys); + } + + @Override + void maybeClearReferenceQueues() { + clearReferenceQueue(queueForKeys); + } + } + + /** Concrete implementation of {@link Segment} for weak keys and weak values. */ + static final class WeakKeyWeakValueSegment + extends Segment, WeakKeyWeakValueSegment> { + private final ReferenceQueue queueForKeys = new ReferenceQueue(); + private final ReferenceQueue queueForValues = new ReferenceQueue(); + + WeakKeyWeakValueSegment( + MapMakerInternalMap, WeakKeyWeakValueSegment> map, + int initialCapacity) { + super(map, initialCapacity); + } + + @Override + WeakKeyWeakValueSegment self() { + return this; + } + + @Override + ReferenceQueue getKeyReferenceQueueForTesting() { + return queueForKeys; + } + + @Override + ReferenceQueue getValueReferenceQueueForTesting() { + return queueForValues; + } + + @SuppressWarnings("unchecked") + @Override + public WeakKeyWeakValueEntry castForTesting(InternalEntry entry) { + return (WeakKeyWeakValueEntry) entry; + } + + @Override + public WeakValueReference> getWeakValueReferenceForTesting( + InternalEntry e) { + return castForTesting(e).getValueReference(); + } + + @Override + public WeakValueReference> newWeakValueReferenceForTesting( + InternalEntry e, V value) { + return new WeakValueReferenceImpl<>(queueForValues, value, castForTesting(e)); + } + + @Override + public void setWeakValueReferenceForTesting( + InternalEntry e, + WeakValueReference> valueReference) { + WeakKeyWeakValueEntry entry = castForTesting(e); + @SuppressWarnings("unchecked") + WeakValueReference> newValueReference = + (WeakValueReference>) valueReference; + WeakValueReference> previous = entry.valueReference; + entry.valueReference = newValueReference; + previous.clear(); + } + + @Override + void maybeDrainReferenceQueues() { + drainKeyReferenceQueue(queueForKeys); + drainValueReferenceQueue(queueForValues); + } + + @Override + void maybeClearReferenceQueues() { + clearReferenceQueue(queueForKeys); + } + } + + /** Concrete implementation of {@link Segment} for weak keys and {@link Dummy} values. */ + static final class WeakKeyDummyValueSegment + extends Segment, WeakKeyDummyValueSegment> { + private final ReferenceQueue queueForKeys = new ReferenceQueue(); + + WeakKeyDummyValueSegment( + MapMakerInternalMap, WeakKeyDummyValueSegment> map, + int initialCapacity) { + super(map, initialCapacity); + } + + @Override + WeakKeyDummyValueSegment self() { + return this; + } + + @Override + ReferenceQueue getKeyReferenceQueueForTesting() { + return queueForKeys; + } + + @SuppressWarnings("unchecked") + @Override + public WeakKeyDummyValueEntry castForTesting(InternalEntry entry) { + return (WeakKeyDummyValueEntry) entry; + } + + @Override + void maybeDrainReferenceQueues() { + drainKeyReferenceQueue(queueForKeys); + } + + @Override + void maybeClearReferenceQueues() { + clearReferenceQueue(queueForKeys); + } + } + + static final class CleanupMapTask implements Runnable { + final WeakReference> mapReference; + + public CleanupMapTask(MapMakerInternalMap map) { + this.mapReference = new WeakReference>(map); + } + + @Override + public void run() { + MapMakerInternalMap map = mapReference.get(); + if (map == null) { + throw new CancellationException(); + } + + for (Segment segment : map.segments) { + segment.runCleanup(); + } + } + } + + @VisibleForTesting + Strength keyStrength() { + return entryHelper.keyStrength(); + } + + @VisibleForTesting + Strength valueStrength() { + return entryHelper.valueStrength(); + } + + @VisibleForTesting + Equivalence valueEquivalence() { + return entryHelper.valueStrength().defaultEquivalence(); + } + + // ConcurrentMap methods + + @Override + public boolean isEmpty() { + /* + * Sum per-segment modCounts to avoid mis-reporting when elements are concurrently added and + * removed in one segment while checking another, in which case the table was never actually + * empty at any point. (The sum ensures accuracy up through at least 1<<31 per-segment + * modifications before recheck.) Method containsValue() uses similar constructions for + * stability checks. + */ + long sum = 0L; + Segment[] segments = this.segments; + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + sum += segments[i].modCount; + } + + if (sum != 0L) { // recheck unless no modifications + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + sum -= segments[i].modCount; + } + return sum == 0L; + } + return true; + } + + @Override + public int size() { + Segment[] segments = this.segments; + long sum = 0; + for (int i = 0; i < segments.length; ++i) { + sum += segments[i].count; + } + return Ints.saturatedCast(sum); + } + + @Override + public V get(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).get(key, hash); + } + + /** + * Returns the internal entry for the specified key. The entry may be computing or partially + * collected. Does not impact recency ordering. + */ + E getEntry(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).getEntry(key, hash); + } + + @Override + public boolean containsKey(Object key) { + if (key == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).containsKey(key, hash); + } + + @Override + public boolean containsValue(Object value) { + if (value == null) { + return false; + } + + // This implementation is patterned after ConcurrentHashMap, but without the locking. The only + // way for it to return a false negative would be for the target value to jump around in the map + // such that none of the subsequent iterations observed it, despite the fact that at every point + // in time it was present somewhere int the map. This becomes increasingly unlikely as + // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. + final Segment[] segments = this.segments; + long last = -1L; + for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { + long sum = 0L; + for (Segment segment : segments) { + // ensure visibility of most recent completed write + int unused = segment.count; // read-volatile + + AtomicReferenceArray table = segment.table; + for (int j = 0; j < table.length(); j++) { + for (E e = table.get(j); e != null; e = e.getNext()) { + V v = segment.getLiveValue(e); + if (v != null && valueEquivalence().equivalent(value, v)) { + return true; + } + } + } + sum += segment.modCount; + } + if (sum == last) { + break; + } + last = sum; + } + return false; + } + + @Override + public V put(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, false); + } + + @Override + public V putIfAbsent(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, true); + } + + @Override + public void putAll(Map m) { + for (Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + @Override + public V remove(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash); + } + + @Override + public boolean remove(Object key, Object value) { + if (key == null || value == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + checkNotNull(key); + checkNotNull(newValue); + if (oldValue == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).replace(key, hash, oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).replace(key, hash, value); + } + + @Override + public void clear() { + for (Segment segment : segments) { + segment.clear(); + } + } + + transient Set keySet; + + @Override + public Set keySet() { + Set ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet()); + } + + transient Collection values; + + @Override + public Collection values() { + Collection vs = values; + return (vs != null) ? vs : (values = new Values()); + } + + transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet()); + } + + // Iterator Support + + abstract class HashIterator implements Iterator { + + int nextSegmentIndex; + int nextTableIndex; + Segment currentSegment; + AtomicReferenceArray currentTable; + E nextEntry; + WriteThroughEntry nextExternal; + WriteThroughEntry lastReturned; + + HashIterator() { + nextSegmentIndex = segments.length - 1; + nextTableIndex = -1; + advance(); + } + + @Override + public abstract T next(); + + final void advance() { + nextExternal = null; + + if (nextInChain()) { + return; + } + + if (nextInTable()) { + return; + } + + while (nextSegmentIndex >= 0) { + currentSegment = segments[nextSegmentIndex--]; + if (currentSegment.count != 0) { + currentTable = currentSegment.table; + nextTableIndex = currentTable.length() - 1; + if (nextInTable()) { + return; + } + } + } + } + + /** Finds the next entry in the current chain. Returns {@code true} if an entry was found. */ + boolean nextInChain() { + if (nextEntry != null) { + for (nextEntry = nextEntry.getNext(); nextEntry != null; nextEntry = nextEntry.getNext()) { + if (advanceTo(nextEntry)) { + return true; + } + } + } + return false; + } + + /** Finds the next entry in the current table. Returns {@code true} if an entry was found. */ + boolean nextInTable() { + while (nextTableIndex >= 0) { + if ((nextEntry = currentTable.get(nextTableIndex--)) != null) { + if (advanceTo(nextEntry) || nextInChain()) { + return true; + } + } + } + return false; + } + + /** + * Advances to the given entry. Returns {@code true} if the entry was valid, {@code false} if it + * should be skipped. + */ + boolean advanceTo(E entry) { + try { + K key = entry.getKey(); + V value = getLiveValue(entry); + if (value != null) { + nextExternal = new WriteThroughEntry(key, value); + return true; + } else { + // Skip stale entry. + return false; + } + } finally { + currentSegment.postReadCleanup(); + } + } + + @Override + public boolean hasNext() { + return nextExternal != null; + } + + WriteThroughEntry nextEntry() { + if (nextExternal == null) { + throw new NoSuchElementException(); + } + lastReturned = nextExternal; + advance(); + return lastReturned; + } + + @Override + public void remove() { + checkRemove(lastReturned != null); + MapMakerInternalMap.this.remove(lastReturned.getKey()); + lastReturned = null; + } + } + + final class KeyIterator extends HashIterator { + + @Override + public K next() { + return nextEntry().getKey(); + } + } + + final class ValueIterator extends HashIterator { + + @Override + public V next() { + return nextEntry().getValue(); + } + } + + /** + * Custom Entry class used by EntryIterator.next(), that relays setValue changes to the underlying + * map. + */ + final class WriteThroughEntry extends AbstractMapEntry { + final K key; // non-null + V value; // non-null + + WriteThroughEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public boolean equals(Object object) { + // Cannot use key and value equivalence + if (object instanceof Entry) { + Entry that = (Entry) object; + return key.equals(that.getKey()) && value.equals(that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + // Cannot use key and value equivalence + return key.hashCode() ^ value.hashCode(); + } + + @Override + public V setValue(V newValue) { + V oldValue = put(key, newValue); + value = newValue; // only if put succeeds + return oldValue; + } + } + + final class EntryIterator extends HashIterator> { + + @Override + public Entry next() { + return nextEntry(); + } + } + + final class KeySet extends SafeToArraySet { + + @Override + public Iterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return MapMakerInternalMap.this.size(); + } + + @Override + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return MapMakerInternalMap.this.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return MapMakerInternalMap.this.remove(o) != null; + } + + @Override + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + final class Values extends AbstractCollection { + + @Override + public Iterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return MapMakerInternalMap.this.size(); + } + + @Override + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return MapMakerInternalMap.this.containsValue(o); + } + + @Override + public void clear() { + MapMakerInternalMap.this.clear(); + } + + // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. + // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 + + @Override + public Object[] toArray() { + return toArrayList(this).toArray(); + } + + @Override + public T[] toArray(T[] a) { + return toArrayList(this).toArray(a); + } + } + + final class EntrySet extends SafeToArraySet> { + + @Override + public Iterator> iterator() { + return new EntryIterator(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + if (key == null) { + return false; + } + V v = MapMakerInternalMap.this.get(key); + + return v != null && valueEquivalence().equivalent(e.getValue(), v); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + return key != null && MapMakerInternalMap.this.remove(key, e.getValue()); + } + + @Override + public int size() { + return MapMakerInternalMap.this.size(); + } + + @Override + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + @Override + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + private abstract static class SafeToArraySet extends AbstractSet { + // super.toArray() may misbehave if size() is inaccurate, at least on old versions of Android. + // https://code.google.com/p/android/issues/detail?id=36519 / http://r.android.com/47508 + + @Override + public Object[] toArray() { + return toArrayList(this).toArray(); + } + + @Override + public T[] toArray(T[] a) { + return toArrayList(this).toArray(a); + } + } + + private static ArrayList toArrayList(Collection c) { + // Avoid calling ArrayList(Collection), which may call back into toArray. + ArrayList result = new ArrayList<>(c.size()); + Iterators.addAll(result, c.iterator()); + return result; + } + + // Serialization Support + + private static final long serialVersionUID = 5; + + Object writeReplace() { + return new SerializationProxy<>( + entryHelper.keyStrength(), + entryHelper.valueStrength(), + keyEquivalence, + entryHelper.valueStrength().defaultEquivalence(), + concurrencyLevel, + this); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream in) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializationProxy"); + } + + /** + * The actual object that gets serialized. Unfortunately, readResolve() doesn't get called when a + * circular dependency is present, so the proxy must be able to behave as the map itself. + */ + abstract static class AbstractSerializationProxy extends ForwardingConcurrentMap + implements Serializable { + private static final long serialVersionUID = 3; + + final Strength keyStrength; + final Strength valueStrength; + final Equivalence keyEquivalence; + final Equivalence valueEquivalence; + final int concurrencyLevel; + + transient ConcurrentMap delegate; + + AbstractSerializationProxy( + Strength keyStrength, + Strength valueStrength, + Equivalence keyEquivalence, + Equivalence valueEquivalence, + int concurrencyLevel, + ConcurrentMap delegate) { + this.keyStrength = keyStrength; + this.valueStrength = valueStrength; + this.keyEquivalence = keyEquivalence; + this.valueEquivalence = valueEquivalence; + this.concurrencyLevel = concurrencyLevel; + this.delegate = delegate; + } + + @Override + protected ConcurrentMap delegate() { + return delegate; + } + + void writeMapTo(ObjectOutputStream out) throws IOException { + out.writeInt(delegate.size()); + for (Entry entry : delegate.entrySet()) { + out.writeObject(entry.getKey()); + out.writeObject(entry.getValue()); + } + out.writeObject(null); // terminate entries + } + + @SuppressWarnings("deprecation") // serialization of deprecated feature + @J2ktIncompatible // java.io.ObjectInputStream + MapMaker readMapMaker(ObjectInputStream in) throws IOException { + int size = in.readInt(); + return new MapMaker() + .initialCapacity(size) + .setKeyStrength(keyStrength) + .setValueStrength(valueStrength) + .keyEquivalence(keyEquivalence) + .concurrencyLevel(concurrencyLevel); + } + + @SuppressWarnings("unchecked") + @J2ktIncompatible // java.io.ObjectInputStream + void readEntries(ObjectInputStream in) throws IOException, ClassNotFoundException { + while (true) { + K key = (K) in.readObject(); + if (key == null) { + break; // terminator + } + V value = (V) in.readObject(); + delegate.put(key, value); + } + } + } + + /** + * The actual object that gets serialized. Unfortunately, readResolve() doesn't get called when a + * circular dependency is present, so the proxy must be able to behave as the map itself. + */ + private static final class SerializationProxy extends AbstractSerializationProxy { + private static final long serialVersionUID = 3; + + SerializationProxy( + Strength keyStrength, + Strength valueStrength, + Equivalence keyEquivalence, + Equivalence valueEquivalence, + int concurrencyLevel, + ConcurrentMap delegate) { + super( + keyStrength, valueStrength, keyEquivalence, valueEquivalence, concurrencyLevel, delegate); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + writeMapTo(out); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + MapMaker mapMaker = readMapMaker(in); + delegate = mapMaker.makeMap(); + readEntries(in); + } + + private Object readResolve() { + return delegate; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Maps.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Maps.java new file mode 100644 index 0000000000000..2de6513a7278e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Maps.java @@ -0,0 +1,4493 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Converter; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.collect.MapDifference.ValueDifference; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Properties; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.stream.Collector; + +import static java.util.Collections.singletonMap; +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Predicates.compose; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * Static utility methods pertaining to {@link Map} instances (including instances of {@link + * SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts {@link Lists}, {@link Sets} + * and {@link Queues}. + * + *

See the Guava User Guide article on {@code Maps}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Isaac Shum + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Maps { + private Maps() {} + + private enum EntryFunction implements Function, Object> { + KEY { + @Override + public Object apply(Entry entry) { + return entry.getKey(); + } + }, + VALUE { + @Override + public Object apply(Entry entry) { + return entry.getValue(); + } + }; + } + + @SuppressWarnings("unchecked") + static Function, K> keyFunction() { + return (Function) EntryFunction.KEY; + } + + @SuppressWarnings("unchecked") + static Function, V> valueFunction() { + return (Function) EntryFunction.VALUE; + } + + static Iterator keyIterator( + Iterator> entryIterator) { + return new TransformedIterator, K>(entryIterator) { + @Override + @ParametricNullness + K transform(Entry entry) { + return entry.getKey(); + } + }; + } + + static Iterator valueIterator( + Iterator> entryIterator) { + return new TransformedIterator, V>(entryIterator) { + @Override + @ParametricNullness + V transform(Entry entry) { + return entry.getValue(); + } + }; + } + + /** + * Returns an immutable map instance containing the given entries. Internally, the returned map + * will be backed by an {@link EnumMap}. + * + *

The iteration order of the returned map follows the enum's iteration order, not the order in + * which the elements appear in the given map. + * + * @param map the map to make an immutable copy of + * @return an immutable map containing those entries + * @since 14.0 + */ + @GwtCompatible(serializable = true) + @J2ktIncompatible + public static , V> ImmutableMap immutableEnumMap( + Map map) { + if (map instanceof ImmutableEnumMap) { + @SuppressWarnings("unchecked") // safe covariant cast + ImmutableEnumMap result = (ImmutableEnumMap) map; + return result; + } + Iterator> entryItr = map.entrySet().iterator(); + if (!entryItr.hasNext()) { + return ImmutableMap.of(); + } + Entry entry1 = entryItr.next(); + K key1 = entry1.getKey(); + V value1 = entry1.getValue(); + checkEntryNotNull(key1, value1); + // Do something that works for j2cl, where we can't call getDeclaredClass(): + EnumMap enumMap = new EnumMap<>(singletonMap(key1, value1)); + while (entryItr.hasNext()) { + Entry entry = entryItr.next(); + K key = entry.getKey(); + V value = entry.getValue(); + checkEntryNotNull(key, value); + enumMap.put(key, value); + } + return ImmutableEnumMap.asImmutable(enumMap); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. The + * resulting implementation is specialized for enum key types. The returned map and its views will + * iterate over keys in their enum definition order, not encounter order. + * + *

If the mapped keys contain duplicates, an {@code IllegalArgumentException} is thrown when + * the collection operation is performed. (This differs from the {@code Collector} returned by + * {@link java.util.stream.Collectors#toMap(java.util.function.Function, + * java.util.function.Function) Collectors.toMap(Function, Function)}, which throws an {@code + * IllegalStateException}.) + * + * @since 21.0 + */ + @J2ktIncompatible + public static , V> + Collector> toImmutableEnumMap( + java.util.function.Function keyFunction, + java.util.function.Function valueFunction) { + return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction); + } + + /** + * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys + * and values are the result of applying the provided mapping functions to the input elements. The + * resulting implementation is specialized for enum key types. The returned map and its views will + * iterate over keys in their enum definition order, not encounter order. + * + *

If the mapped keys contain duplicates, the values are merged using the specified merging + * function. + * + * @since 21.0 + */ + @J2ktIncompatible + public static , V> + Collector> toImmutableEnumMap( + java.util.function.Function keyFunction, + java.util.function.Function valueFunction, + BinaryOperator mergeFunction) { + return CollectCollectors.toImmutableEnumMap(keyFunction, valueFunction, mergeFunction); + } + + /** + * Creates a mutable, empty {@code HashMap} instance. + * + *

Note: if mutability is not required, use {@link ImmutableMap#of()} instead. + * + *

Note: if {@code K} is an {@code enum} type, use {@link #newEnumMap} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code HashMap} constructor directly, taking advantage of "diamond" syntax. + * + * @return a new, empty {@code HashMap} + */ + public static HashMap newHashMap() { + return new HashMap<>(); + } + + /** + * Creates a mutable {@code HashMap} instance with the same mappings as the specified map. + * + *

Note: if mutability is not required, use {@link ImmutableMap#copyOf(Map)} instead. + * + *

Note: if {@code K} is an {@link Enum} type, use {@link #newEnumMap} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code HashMap} constructor directly, taking advantage of "diamond" syntax. + * + * @param map the mappings to be placed in the new map + * @return a new {@code HashMap} initialized with the mappings from {@code map} + */ + public static HashMap newHashMap( + Map map) { + return new HashMap<>(map); + } + + /** + * Creates a {@code HashMap} instance, with a high enough "initial capacity" that it should + * hold {@code expectedSize} elements without growth. This behavior cannot be broadly guaranteed, + * but it is observed to be true for OpenJDK 1.7. It also can't be guaranteed that the method + * isn't inadvertently oversizing the returned map. + * + * @param expectedSize the number of entries you expect to add to the returned map + * @return a new, empty {@code HashMap} with enough capacity to hold {@code expectedSize} entries + * without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashMap newHashMapWithExpectedSize( + int expectedSize) { + return new HashMap<>(capacity(expectedSize)); + } + + /** + * Returns a capacity that is sufficient to keep the map from being resized as long as it grows no + * larger than expectedSize and the load factor is ≥ its default (0.75). + */ + static int capacity(int expectedSize) { + if (expectedSize < 3) { + checkNonnegative(expectedSize, "expectedSize"); + return expectedSize + 1; + } + if (expectedSize < Ints.MAX_POWER_OF_TWO) { + // This seems to be consistent across JDKs. The capacity argument to HashMap and LinkedHashMap + // ends up being used to compute a "threshold" size, beyond which the internal table + // will be resized. That threshold is ceilingPowerOfTwo(capacity*loadFactor), where + // loadFactor is 0.75 by default. So with the calculation here we ensure that the + // threshold is equal to ceilingPowerOfTwo(expectedSize). There is a separate code + // path when the first operation on the new map is putAll(otherMap). There, prior to + // https://github.com/openjdk/jdk/commit/3e393047e12147a81e2899784b943923fc34da8e, a bug + // meant that sometimes a too-large threshold is calculated. However, this new threshold is + // independent of the initial capacity, except that it won't be lower than the threshold + // computed from that capacity. Because the internal table is only allocated on the first + // write, we won't see copying because of the new threshold. So it is always OK to use the + // calculation here. + return (int) Math.ceil(expectedSize / 0.75); + } + return Integer.MAX_VALUE; // any large value + } + + /** + * Creates a mutable, empty, insertion-ordered {@code LinkedHashMap} instance. + * + *

Note: if mutability is not required, use {@link ImmutableMap#of()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedHashMap} constructor directly, taking advantage of "diamond" syntax. + * + * @return a new, empty {@code LinkedHashMap} + */ + public static LinkedHashMap newLinkedHashMap() { + return new LinkedHashMap<>(); + } + + /** + * Creates a mutable, insertion-ordered {@code LinkedHashMap} instance with the same + * mappings as the specified map. + * + *

Note: if mutability is not required, use {@link ImmutableMap#copyOf(Map)} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedHashMap} constructor directly, taking advantage of "diamond" syntax. + * + * @param map the mappings to be placed in the new map + * @return a new, {@code LinkedHashMap} initialized with the mappings from {@code map} + */ + public static LinkedHashMap newLinkedHashMap( + Map map) { + return new LinkedHashMap<>(map); + } + + /** + * Creates a {@code LinkedHashMap} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without growth. This behavior cannot be + * broadly guaranteed, but it is observed to be true for OpenJDK 1.7. It also can't be guaranteed + * that the method isn't inadvertently oversizing the returned map. + * + * @param expectedSize the number of entries you expect to add to the returned map + * @return a new, empty {@code LinkedHashMap} with enough capacity to hold {@code expectedSize} + * entries without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + * @since 19.0 + */ + public static + LinkedHashMap newLinkedHashMapWithExpectedSize(int expectedSize) { + return new LinkedHashMap<>(capacity(expectedSize)); + } + + /** + * Creates a new empty {@link ConcurrentHashMap} instance. + * + * @since 3.0 + */ + public static ConcurrentMap newConcurrentMap() { + return new ConcurrentHashMap<>(); + } + + /** + * Creates a mutable, empty {@code TreeMap} instance using the natural ordering of its + * elements. + * + *

Note: if mutability is not required, use {@link ImmutableSortedMap#of()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeMap} constructor directly, taking advantage of "diamond" syntax. + * + * @return a new, empty {@code TreeMap} + */ + public static TreeMap newTreeMap() { + return new TreeMap<>(); + } + + /** + * Creates a mutable {@code TreeMap} instance with the same mappings as the specified map + * and using the same ordering as the specified map. + * + *

Note: if mutability is not required, use {@link + * ImmutableSortedMap#copyOfSorted(SortedMap)} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeMap} constructor directly, taking advantage of "diamond" syntax. + * + * @param map the sorted map whose mappings are to be placed in the new map and whose comparator + * is to be used to sort the new map + * @return a new {@code TreeMap} initialized with the mappings from {@code map} and using the + * comparator of {@code map} + */ + public static TreeMap newTreeMap( + SortedMap map) { + return new TreeMap<>(map); + } + + /** + * Creates a mutable, empty {@code TreeMap} instance using the given comparator. + * + *

Note: if mutability is not required, use {@code + * ImmutableSortedMap.orderedBy(comparator).build()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeMap} constructor directly, taking advantage of "diamond" syntax. + * + * @param comparator the comparator to sort the keys with + * @return a new, empty {@code TreeMap} + */ + public static TreeMap newTreeMap( + Comparator comparator) { + // Ideally, the extra type parameter "C" shouldn't be necessary. It is a + // work-around of a compiler type inference quirk that prevents the + // following code from being compiled: + // Comparator> comparator = null; + // Map, String> map = newTreeMap(comparator); + return new TreeMap<>(comparator); + } + + /** + * Creates an {@code EnumMap} instance. + * + * @param type the key type for this map + * @return a new, empty {@code EnumMap} + */ + public static , V extends Object> EnumMap newEnumMap(Class type) { + return new EnumMap<>(checkNotNull(type)); + } + + /** + * Creates an {@code EnumMap} with the same mappings as the specified map. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code EnumMap} constructor directly, taking advantage of "diamond" syntax. + * + * @param map the map from which to initialize this {@code EnumMap} + * @return a new {@code EnumMap} initialized with the mappings from {@code map} + * @throws IllegalArgumentException if {@code m} is not an {@code EnumMap} instance and contains + * no mappings + */ + public static , V extends Object> EnumMap newEnumMap( + Map map) { + return new EnumMap<>(map); + } + + /** + * Creates an {@code IdentityHashMap} instance. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code IdentityHashMap} constructor directly, taking advantage of "diamond" syntax. + * + * @return a new, empty {@code IdentityHashMap} + */ + public static IdentityHashMap newIdentityHashMap() { + return new IdentityHashMap<>(); + } + + /** + * Computes the difference between two maps. This difference is an immutable snapshot of the state + * of the maps at the time this method is called. It will never change, even if the maps change at + * a later time. + * + *

Since this method uses {@code HashMap} instances internally, the keys of the supplied maps + * must be well-behaved with respect to {@link Object#equals} and {@link Object#hashCode}. + * + *

Note:If you only need to know whether two maps have the same mappings, call {@code + * left.equals(right)} instead of this method. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @return the difference between the two maps + */ + public static MapDifference difference( + Map left, Map right) { + if (left instanceof SortedMap) { + @SuppressWarnings("unchecked") + SortedMap sortedLeft = (SortedMap) left; + return difference(sortedLeft, right); + } + return difference(left, right, Equivalence.equals()); + } + + /** + * Computes the difference between two maps. This difference is an immutable snapshot of the state + * of the maps at the time this method is called. It will never change, even if the maps change at + * a later time. + * + *

Since this method uses {@code HashMap} instances internally, the keys of the supplied maps + * must be well-behaved with respect to {@link Object#equals} and {@link Object#hashCode}. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @param valueEquivalence the equivalence relationship to use to compare values + * @return the difference between the two maps + * @since 10.0 + */ + public static MapDifference difference( + Map left, + Map right, + Equivalence valueEquivalence) { + Preconditions.checkNotNull(valueEquivalence); + + Map onlyOnLeft = newLinkedHashMap(); + Map onlyOnRight = new LinkedHashMap<>(right); // will whittle it down + Map onBoth = newLinkedHashMap(); + Map> differences = newLinkedHashMap(); + doDifference(left, right, valueEquivalence, onlyOnLeft, onlyOnRight, onBoth, differences); + return new MapDifferenceImpl<>(onlyOnLeft, onlyOnRight, onBoth, differences); + } + + /** + * Computes the difference between two sorted maps, using the comparator of the left map, or + * {@code Ordering.natural()} if the left map uses the natural ordering of its elements. This + * difference is an immutable snapshot of the state of the maps at the time this method is called. + * It will never change, even if the maps change at a later time. + * + *

Since this method uses {@code TreeMap} instances internally, the keys of the right map must + * all compare as distinct according to the comparator of the left map. + * + *

Note:If you only need to know whether two sorted maps have the same mappings, call + * {@code left.equals(right)} instead of this method. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @return the difference between the two maps + * @since 11.0 + */ + public static SortedMapDifference difference( + SortedMap left, Map right) { + checkNotNull(left); + checkNotNull(right); + Comparator comparator = orNaturalOrder(left.comparator()); + SortedMap onlyOnLeft = Maps.newTreeMap(comparator); + SortedMap onlyOnRight = Maps.newTreeMap(comparator); + onlyOnRight.putAll(right); // will whittle it down + SortedMap onBoth = Maps.newTreeMap(comparator); + SortedMap> differences = Maps.newTreeMap(comparator); + + doDifference(left, right, Equivalence.equals(), onlyOnLeft, onlyOnRight, onBoth, differences); + return new SortedMapDifferenceImpl<>(onlyOnLeft, onlyOnRight, onBoth, differences); + } + + private static void doDifference( + Map left, + Map right, + Equivalence valueEquivalence, + Map onlyOnLeft, + Map onlyOnRight, + Map onBoth, + Map> differences) { + for (Entry entry : left.entrySet()) { + K leftKey = entry.getKey(); + V leftValue = entry.getValue(); + if (right.containsKey(leftKey)) { + /* + * The cast is safe because onlyOnRight contains all the keys of right. + * + * TODO(cpovirk): Consider checking onlyOnRight.containsKey instead of right.containsKey. + * That could change behavior if the input maps use different equivalence relations (and so + * a key that appears once in `right` might appear multiple times in `left`). We don't + * guarantee behavior in that case, anyway, and the current behavior is likely undesirable. + * So that's either a reason to feel free to change it or a reason to not bother thinking + * further about this. + */ + V rightValue = uncheckedCastNullableTToT(onlyOnRight.remove(leftKey)); + if (valueEquivalence.equivalent(leftValue, rightValue)) { + onBoth.put(leftKey, leftValue); + } else { + differences.put(leftKey, ValueDifferenceImpl.create(leftValue, rightValue)); + } + } else { + onlyOnLeft.put(leftKey, leftValue); + } + } + } + + private static Map unmodifiableMap( + Map map) { + if (map instanceof SortedMap) { + return Collections.unmodifiableSortedMap((SortedMap) map); + } else { + return Collections.unmodifiableMap(map); + } + } + + static class MapDifferenceImpl + implements MapDifference { + final Map onlyOnLeft; + final Map onlyOnRight; + final Map onBoth; + final Map> differences; + + MapDifferenceImpl( + Map onlyOnLeft, + Map onlyOnRight, + Map onBoth, + Map> differences) { + this.onlyOnLeft = unmodifiableMap(onlyOnLeft); + this.onlyOnRight = unmodifiableMap(onlyOnRight); + this.onBoth = unmodifiableMap(onBoth); + this.differences = unmodifiableMap(differences); + } + + @Override + public boolean areEqual() { + return onlyOnLeft.isEmpty() && onlyOnRight.isEmpty() && differences.isEmpty(); + } + + @Override + public Map entriesOnlyOnLeft() { + return onlyOnLeft; + } + + @Override + public Map entriesOnlyOnRight() { + return onlyOnRight; + } + + @Override + public Map entriesInCommon() { + return onBoth; + } + + @Override + public Map> entriesDiffering() { + return differences; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof MapDifference) { + MapDifference other = (MapDifference) object; + return entriesOnlyOnLeft().equals(other.entriesOnlyOnLeft()) + && entriesOnlyOnRight().equals(other.entriesOnlyOnRight()) + && entriesInCommon().equals(other.entriesInCommon()) + && entriesDiffering().equals(other.entriesDiffering()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode( + entriesOnlyOnLeft(), entriesOnlyOnRight(), entriesInCommon(), entriesDiffering()); + } + + @Override + public String toString() { + if (areEqual()) { + return "equal"; + } + + StringBuilder result = new StringBuilder("not equal"); + if (!onlyOnLeft.isEmpty()) { + result.append(": only on left=").append(onlyOnLeft); + } + if (!onlyOnRight.isEmpty()) { + result.append(": only on right=").append(onlyOnRight); + } + if (!differences.isEmpty()) { + result.append(": value differences=").append(differences); + } + return result.toString(); + } + } + + static class ValueDifferenceImpl implements MapDifference.ValueDifference { + @ParametricNullness private final V left; + @ParametricNullness private final V right; + + static ValueDifference create( + @ParametricNullness V left, @ParametricNullness V right) { + return new ValueDifferenceImpl(left, right); + } + + private ValueDifferenceImpl(@ParametricNullness V left, @ParametricNullness V right) { + this.left = left; + this.right = right; + } + + @Override + @ParametricNullness + public V leftValue() { + return left; + } + + @Override + @ParametricNullness + public V rightValue() { + return right; + } + + @Override + public boolean equals(Object object) { + if (object instanceof MapDifference.ValueDifference) { + MapDifference.ValueDifference that = (MapDifference.ValueDifference) object; + return Objects.equal(this.left, that.leftValue()) + && Objects.equal(this.right, that.rightValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(left, right); + } + + @Override + public String toString() { + return "(" + left + ", " + right + ")"; + } + } + + static class SortedMapDifferenceImpl + extends MapDifferenceImpl implements SortedMapDifference { + SortedMapDifferenceImpl( + SortedMap onlyOnLeft, + SortedMap onlyOnRight, + SortedMap onBoth, + SortedMap> differences) { + super(onlyOnLeft, onlyOnRight, onBoth, differences); + } + + @Override + public SortedMap> entriesDiffering() { + return (SortedMap>) super.entriesDiffering(); + } + + @Override + public SortedMap entriesInCommon() { + return (SortedMap) super.entriesInCommon(); + } + + @Override + public SortedMap entriesOnlyOnLeft() { + return (SortedMap) super.entriesOnlyOnLeft(); + } + + @Override + public SortedMap entriesOnlyOnRight() { + return (SortedMap) super.entriesOnlyOnRight(); + } + } + + /** + * Returns the specified comparator if not null; otherwise returns {@code Ordering.natural()}. + * This method is an abomination of generics; the only purpose of this method is to contain the + * ugly type-casting in one place. + */ + @SuppressWarnings("unchecked") + static Comparator orNaturalOrder(Comparator comparator) { + if (comparator != null) { // can't use ? : because of javac bug 5080917 + return comparator; + } + return (Comparator) Ordering.natural(); + } + + /** + * Returns a live {@link Map} view whose keys are the contents of {@code set} and whose values are + * computed on demand using {@code function}. To get an immutable copy instead, use {@link + * #toMap(Iterable, Function)}. + * + *

Specifically, for each {@code k} in the backing set, the returned map has an entry mapping + * {@code k} to {@code function.apply(k)}. The {@code keySet}, {@code values}, and {@code + * entrySet} views of the returned map iterate in the same order as the backing set. + * + *

Modifications to the backing set are read through to the returned map. The returned map + * supports removal operations if the backing set does. Removal operations write through to the + * backing set. The returned map does not support put operations. + * + *

Warning: If the function rejects {@code null}, caution is required to make sure the + * set does not contain {@code null}, because the view cannot stop {@code null} from being added + * to the set. + * + *

Warning: This method assumes that for any instance {@code k} of key type {@code K}, + * {@code k.equals(k2)} implies that {@code k2} is also of type {@code K}. Using a key type for + * which this may not hold, such as {@code ArrayList}, may risk a {@code ClassCastException} when + * calling methods on the resulting map view. + * + * @since 14.0 + */ + public static Map asMap( + Set set, Function function) { + return new AsMapView<>(set, function); + } + + /** + * Returns a view of the sorted set as a map, mapping keys from the set according to the specified + * function. + * + *

Specifically, for each {@code k} in the backing set, the returned map has an entry mapping + * {@code k} to {@code function.apply(k)}. The {@code keySet}, {@code values}, and {@code + * entrySet} views of the returned map iterate in the same order as the backing set. + * + *

Modifications to the backing set are read through to the returned map. The returned map + * supports removal operations if the backing set does. Removal operations write through to the + * backing set. The returned map does not support put operations. + * + *

Warning: If the function rejects {@code null}, caution is required to make sure the + * set does not contain {@code null}, because the view cannot stop {@code null} from being added + * to the set. + * + *

Warning: This method assumes that for any instance {@code k} of key type {@code K}, + * {@code k.equals(k2)} implies that {@code k2} is also of type {@code K}. Using a key type for + * which this may not hold, such as {@code ArrayList}, may risk a {@code ClassCastException} when + * calling methods on the resulting map view. + * + * @since 14.0 + */ + public static SortedMap asMap( + SortedSet set, Function function) { + return new SortedAsMapView<>(set, function); + } + + /** + * Returns a view of the navigable set as a map, mapping keys from the set according to the + * specified function. + * + *

Specifically, for each {@code k} in the backing set, the returned map has an entry mapping + * {@code k} to {@code function.apply(k)}. The {@code keySet}, {@code values}, and {@code + * entrySet} views of the returned map iterate in the same order as the backing set. + * + *

Modifications to the backing set are read through to the returned map. The returned map + * supports removal operations if the backing set does. Removal operations write through to the + * backing set. The returned map does not support put operations. + * + *

Warning: If the function rejects {@code null}, caution is required to make sure the + * set does not contain {@code null}, because the view cannot stop {@code null} from being added + * to the set. + * + *

Warning: This method assumes that for any instance {@code k} of key type {@code K}, + * {@code k.equals(k2)} implies that {@code k2} is also of type {@code K}. Using a key type for + * which this may not hold, such as {@code ArrayList}, may risk a {@code ClassCastException} when + * calling methods on the resulting map view. + * + * @since 14.0 + */ + @GwtIncompatible // NavigableMap + public static NavigableMap asMap( + NavigableSet set, Function function) { + return new NavigableAsMapView<>(set, function); + } + + private static class AsMapView + extends ViewCachingAbstractMap { + + private final Set set; + final Function function; + + Set backingSet() { + return set; + } + + AsMapView(Set set, Function function) { + this.set = checkNotNull(set); + this.function = checkNotNull(function); + } + + @Override + public Set createKeySet() { + return removeOnlySet(backingSet()); + } + + @Override + Collection createValues() { + return Collections2.transform(set, function); + } + + @Override + public int size() { + return backingSet().size(); + } + + @Override + public boolean containsKey(Object key) { + return backingSet().contains(key); + } + + @Override + public V get(Object key) { + return getOrDefault(key, null); + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + if (Collections2.safeContains(backingSet(), key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return defaultValue; + } + } + + @Override + public V remove(Object key) { + if (backingSet().remove(key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return null; + } + } + + @Override + public void clear() { + backingSet().clear(); + } + + @Override + protected Set> createEntrySet() { + class EntrySetImpl extends EntrySet { + @Override + Map map() { + return AsMapView.this; + } + + @Override + public Iterator> iterator() { + return asMapEntryIterator(backingSet(), function); + } + } + return new EntrySetImpl(); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + // avoids allocation of entries + backingSet().forEach(k -> action.accept(k, function.apply(k))); + } + } + + static Iterator> asMapEntryIterator( + Set set, final Function function) { + return new TransformedIterator>(set.iterator()) { + @Override + Entry transform(@ParametricNullness final K key) { + return immutableEntry(key, function.apply(key)); + } + }; + } + + private static class SortedAsMapView extends AsMapView + implements SortedMap { + + SortedAsMapView(SortedSet set, Function function) { + super(set, function); + } + + @Override + SortedSet backingSet() { + return (SortedSet) super.backingSet(); + } + + @Override + public Comparator comparator() { + return backingSet().comparator(); + } + + @Override + public Set keySet() { + return removeOnlySortedSet(backingSet()); + } + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return asMap(backingSet().subSet(fromKey, toKey), function); + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return asMap(backingSet().headSet(toKey), function); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return asMap(backingSet().tailSet(fromKey), function); + } + + @Override + @ParametricNullness + public K firstKey() { + return backingSet().first(); + } + + @Override + @ParametricNullness + public K lastKey() { + return backingSet().last(); + } + } + + @GwtIncompatible // NavigableMap + private static final class NavigableAsMapView + extends AbstractNavigableMap { + /* + * Using AbstractNavigableMap is simpler than extending SortedAsMapView and rewriting all the + * NavigableMap methods. + */ + + private final NavigableSet set; + private final Function function; + + NavigableAsMapView(NavigableSet ks, Function vFunction) { + this.set = checkNotNull(ks); + this.function = checkNotNull(vFunction); + } + + @Override + public NavigableMap subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return asMap(set.subSet(fromKey, fromInclusive, toKey, toInclusive), function); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusive) { + return asMap(set.headSet(toKey, inclusive), function); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusive) { + return asMap(set.tailSet(fromKey, inclusive), function); + } + + @Override + public Comparator comparator() { + return set.comparator(); + } + + @Override + public V get(Object key) { + return getOrDefault(key, null); + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + if (Collections2.safeContains(set, key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return defaultValue; + } + } + + @Override + public void clear() { + set.clear(); + } + + @Override + Iterator> entryIterator() { + return asMapEntryIterator(set, function); + } + + @Override + Spliterator> entrySpliterator() { + return CollectSpliterators.map(set.spliterator(), e -> immutableEntry(e, function.apply(e))); + } + + @Override + public void forEach(BiConsumer action) { + set.forEach(k -> action.accept(k, function.apply(k))); + } + + @Override + Iterator> descendingEntryIterator() { + return descendingMap().entrySet().iterator(); + } + + @Override + public NavigableSet navigableKeySet() { + return removeOnlyNavigableSet(set); + } + + @Override + public int size() { + return set.size(); + } + + @Override + public NavigableMap descendingMap() { + return asMap(set.descendingSet(), function); + } + } + + private static Set removeOnlySet(final Set set) { + return new ForwardingSet() { + @Override + protected Set delegate() { + return set; + } + + @Override + public boolean add(@ParametricNullness E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + }; + } + + private static SortedSet removeOnlySortedSet(final SortedSet set) { + return new ForwardingSortedSet() { + @Override + protected SortedSet delegate() { + return set; + } + + @Override + public boolean add(@ParametricNullness E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + + @Override + public SortedSet headSet(@ParametricNullness E toElement) { + return removeOnlySortedSet(super.headSet(toElement)); + } + + @Override + public SortedSet subSet( + @ParametricNullness E fromElement, @ParametricNullness E toElement) { + return removeOnlySortedSet(super.subSet(fromElement, toElement)); + } + + @Override + public SortedSet tailSet(@ParametricNullness E fromElement) { + return removeOnlySortedSet(super.tailSet(fromElement)); + } + }; + } + + @GwtIncompatible // NavigableSet + private static NavigableSet removeOnlyNavigableSet( + final NavigableSet set) { + return new ForwardingNavigableSet() { + @Override + protected NavigableSet delegate() { + return set; + } + + @Override + public boolean add(@ParametricNullness E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + + @Override + public SortedSet headSet(@ParametricNullness E toElement) { + return removeOnlySortedSet(super.headSet(toElement)); + } + + @Override + public NavigableSet headSet(@ParametricNullness E toElement, boolean inclusive) { + return removeOnlyNavigableSet(super.headSet(toElement, inclusive)); + } + + @Override + public SortedSet subSet( + @ParametricNullness E fromElement, @ParametricNullness E toElement) { + return removeOnlySortedSet(super.subSet(fromElement, toElement)); + } + + @Override + public NavigableSet subSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return removeOnlyNavigableSet( + super.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public SortedSet tailSet(@ParametricNullness E fromElement) { + return removeOnlySortedSet(super.tailSet(fromElement)); + } + + @Override + public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclusive) { + return removeOnlyNavigableSet(super.tailSet(fromElement, inclusive)); + } + + @Override + public NavigableSet descendingSet() { + return removeOnlyNavigableSet(super.descendingSet()); + } + }; + } + + /** + * Returns an immutable map whose keys are the distinct elements of {@code keys} and whose value + * for each key was computed by {@code valueFunction}. The map's iteration order is the order of + * the first appearance of each key in {@code keys}. + * + *

When there are multiple instances of a key in {@code keys}, it is unspecified whether {@code + * valueFunction} will be applied to more than one instance of that key and, if it is, which + * result will be mapped to that key in the returned map. + * + *

If {@code keys} is a {@link Set}, a live view can be obtained instead of a copy using {@link + * Maps#asMap(Set, Function)}. + * + * @throws NullPointerException if any element of {@code keys} is {@code null}, or if {@code + * valueFunction} produces {@code null} for any key + * @since 14.0 + */ + public static ImmutableMap toMap( + Iterable keys, Function valueFunction) { + return toMap(keys.iterator(), valueFunction); + } + + /** + * Returns an immutable map whose keys are the distinct elements of {@code keys} and whose value + * for each key was computed by {@code valueFunction}. The map's iteration order is the order of + * the first appearance of each key in {@code keys}. + * + *

When there are multiple instances of a key in {@code keys}, it is unspecified whether {@code + * valueFunction} will be applied to more than one instance of that key and, if it is, which + * result will be mapped to that key in the returned map. + * + * @throws NullPointerException if any element of {@code keys} is {@code null}, or if {@code + * valueFunction} produces {@code null} for any key + * @since 14.0 + */ + public static ImmutableMap toMap( + Iterator keys, Function valueFunction) { + checkNotNull(valueFunction); + ImmutableMap.Builder builder = ImmutableMap.builder(); + while (keys.hasNext()) { + K key = keys.next(); + builder.put(key, valueFunction.apply(key)); + } + // Using buildKeepingLast() so as not to fail on duplicate keys + return builder.buildKeepingLast(); + } + + /** + * Returns a map with the given {@code values}, indexed by keys derived from those values. In + * other words, each input value produces an entry in the map whose key is the result of applying + * {@code keyFunction} to that value. These entries appear in the same order as the input values. + * Example usage: + * + *

{@code
+   * Color red = new Color("red", 255, 0, 0);
+   * ...
+   * ImmutableSet allColors = ImmutableSet.of(red, green, blue);
+   *
+   * ImmutableMap colorForName =
+   *     uniqueIndex(allColors, c -> c.toString());
+   * assertThat(colorForName).containsEntry("red", red);
+   * }
+ * + *

If your index may associate multiple values with each key, use {@link + * Multimaps#index(Iterable, Function) Multimaps.index}. + * + *

Note: on Java 8 and later, it is usually better to use streams. For example: + * + *

{@code
+   * import static org.apache.iotdb.google.common.collect.ImmutableMap.toImmutableMap;
+   * ...
+   * ImmutableMap colorForName =
+   *     allColors.stream().collect(toImmutableMap(c -> c.toString(), c -> c));
+   * }
+ * + *

Streams provide a more standard and flexible API and the lambdas make it clear what the keys + * and values in the map are. + * + * @param values the values to use when constructing the {@code Map} + * @param keyFunction the function used to produce the key for each value + * @return a map mapping the result of evaluating the function {@code keyFunction} on each value + * in the input collection to that value + * @throws IllegalArgumentException if {@code keyFunction} produces the same key for more than one + * value in the input collection + * @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code + * keyFunction} produces {@code null} for any value + */ + public static ImmutableMap uniqueIndex( + Iterable values, Function keyFunction) { + if (values instanceof Collection) { + return uniqueIndex( + values.iterator(), + keyFunction, + ImmutableMap.builderWithExpectedSize(((Collection) values).size())); + } + return uniqueIndex(values.iterator(), keyFunction); + } + + /** + * Returns a map with the given {@code values}, indexed by keys derived from those values. In + * other words, each input value produces an entry in the map whose key is the result of applying + * {@code keyFunction} to that value. These entries appear in the same order as the input values. + * Example usage: + * + *

{@code
+   * Color red = new Color("red", 255, 0, 0);
+   * ...
+   * Iterator allColors = ImmutableSet.of(red, green, blue).iterator();
+   *
+   * Map colorForName =
+   *     uniqueIndex(allColors, toStringFunction());
+   * assertThat(colorForName).containsEntry("red", red);
+   * }
+ * + *

If your index may associate multiple values with each key, use {@link + * Multimaps#index(Iterator, Function) Multimaps.index}. + * + * @param values the values to use when constructing the {@code Map} + * @param keyFunction the function used to produce the key for each value + * @return a map mapping the result of evaluating the function {@code keyFunction} on each value + * in the input collection to that value + * @throws IllegalArgumentException if {@code keyFunction} produces the same key for more than one + * value in the input collection + * @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code + * keyFunction} produces {@code null} for any value + * @since 10.0 + */ + public static ImmutableMap uniqueIndex( + Iterator values, Function keyFunction) { + return uniqueIndex(values, keyFunction, ImmutableMap.builder()); + } + + private static ImmutableMap uniqueIndex( + Iterator values, Function keyFunction, ImmutableMap.Builder builder) { + checkNotNull(keyFunction); + while (values.hasNext()) { + V value = values.next(); + builder.put(keyFunction.apply(value), value); + } + try { + return builder.buildOrThrow(); + } catch (IllegalArgumentException duplicateKeys) { + throw new IllegalArgumentException( + duplicateKeys.getMessage() + + ". To index multiple values under a key, use Multimaps.index."); + } + } + + /** + * Creates an {@code ImmutableMap} from a {@code Properties} instance. Properties + * normally derive from {@code Map}, but they typically contain strings, which is + * awkward. This method lets you get a plain-old-{@code Map} out of a {@code Properties}. + * + * @param properties a {@code Properties} object to be converted + * @return an immutable map containing all the entries in {@code properties} + * @throws ClassCastException if any key in {@code properties} is not a {@code String} + * @throws NullPointerException if any key or value in {@code properties} is null + */ + @J2ktIncompatible + @GwtIncompatible // java.util.Properties + public static ImmutableMap fromProperties(Properties properties) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) { + /* + * requireNonNull is safe because propertyNames contains only non-null elements. + * + * Accordingly, we have it annotated as returning `Enumeration` in our + * prototype checker's JDK. However, the checker still sees the return type as plain + * `Enumeration`, probably because of one of the following two bugs (and maybe those two + * bugs are themselves just symptoms of the same underlying problem): + * + * https://github.com/typetools/checker-framework/issues/3030 + * + * https://github.com/typetools/checker-framework/issues/3236 + */ + String key = (String) requireNonNull(e.nextElement()); + /* + * requireNonNull is safe because the key came from propertyNames... + * + * ...except that it's possible for users to insert a string key with a non-string value, and + * in that case, getProperty *will* return null. + * + * TODO(b/192002623): Handle that case: Either: + * + * - Skip non-string keys and values entirely, as proposed in the linked bug. + * + * - Throw ClassCastException instead of NullPointerException, as documented in the current + * Javadoc. (Note that we can't necessarily "just" change our call to `getProperty` to `get` + * because `get` does not consult the default properties.) + */ + builder.put(key, requireNonNull(properties.getProperty(key))); + } + + return builder.buildOrThrow(); + } + + /** + * Returns an immutable map entry with the specified key and value. The {@link Entry#setValue} + * operation throws an {@link UnsupportedOperationException}. + * + *

The returned entry is serializable. + * + *

Java 9 users: consider using {@code java.util.Map.entry(key, value)} if the key and + * value are non-null and the entry does not need to be serializable. + * + * @param key the key to be associated with the returned entry + * @param value the value to be associated with the returned entry + */ + @GwtCompatible(serializable = true) + public static Entry immutableEntry( + @ParametricNullness K key, @ParametricNullness V value) { + return new ImmutableEntry<>(key, value); + } + + /** + * Returns an unmodifiable view of the specified set of entries. The {@link Entry#setValue} + * operation throws an {@link UnsupportedOperationException}, as do any operations that would + * modify the returned set. + * + * @param entrySet the entries for which to return an unmodifiable view + * @return an unmodifiable view of the entries + */ + static Set> unmodifiableEntrySet( + Set> entrySet) { + return new UnmodifiableEntrySet<>(Collections.unmodifiableSet(entrySet)); + } + + /** + * Returns an unmodifiable view of the specified map entry. The {@link Entry#setValue} operation + * throws an {@link UnsupportedOperationException}. This also has the side effect of redefining + * {@code equals} to comply with the Entry contract, to avoid a possible nefarious implementation + * of equals. + * + * @param entry the entry for which to return an unmodifiable view + * @return an unmodifiable view of the entry + */ + static Entry unmodifiableEntry( + final Entry entry) { + checkNotNull(entry); + return new AbstractMapEntry() { + @Override + @ParametricNullness + public K getKey() { + return entry.getKey(); + } + + @Override + @ParametricNullness + public V getValue() { + return entry.getValue(); + } + }; + } + + static + UnmodifiableIterator> unmodifiableEntryIterator( + final Iterator> entryIterator) { + return new UnmodifiableIterator>() { + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public Entry next() { + return unmodifiableEntry(entryIterator.next()); + } + }; + } + + /** The implementation of {@link Multimaps#unmodifiableEntries}. */ + static class UnmodifiableEntries + extends ForwardingCollection> { + private final Collection> entries; + + UnmodifiableEntries(Collection> entries) { + this.entries = entries; + } + + @Override + protected Collection> delegate() { + return entries; + } + + @Override + public Iterator> iterator() { + return unmodifiableEntryIterator(entries.iterator()); + } + + // See java.util.Collections.UnmodifiableEntrySet for details on attacks. + + @Override + public Object[] toArray() { + /* + * standardToArray returns `@Nullable Object[]` rather than `Object[]` but because it can + * be used with collections that may contain null. This collection never contains nulls, so we + * could return `Object[]`. But this class is private and J2KT cannot change return types in + * overrides, so we declare `@Nullable Object[]` as the return type. + */ + return standardToArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return standardToArray(array); + } + } + + /** The implementation of {@link Maps#unmodifiableEntrySet(Set)}. */ + static class UnmodifiableEntrySet + extends UnmodifiableEntries implements Set> { + UnmodifiableEntrySet(Set> entries) { + super(entries); + } + + // See java.util.Collections.UnmodifiableEntrySet for details on attacks. + + @Override + public boolean equals(Object object) { + return Sets.equalsImpl(this, object); + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + } + + /** + * Returns a {@link Converter} that converts values using {@link BiMap#get bimap.get()}, and whose + * inverse view converts values using {@link BiMap#inverse bimap.inverse()}{@code .get()}. + * + *

To use a plain {@link Map} as a {@link Function}, see {@link + * org.apache.iotdb.google.common.base.Functions#forMap(Map)} or {@link + * org.apache.iotdb.google.common.base.Functions#forMap(Map, Object)}. + * + * @since 16.0 + */ + public static Converter asConverter(final BiMap bimap) { + return new BiMapConverter<>(bimap); + } + + private static final class BiMapConverter extends Converter implements Serializable { + private final BiMap bimap; + + BiMapConverter(BiMap bimap) { + this.bimap = checkNotNull(bimap); + } + + @Override + protected B doForward(A a) { + return convert(bimap, a); + } + + @Override + protected A doBackward(B b) { + return convert(bimap.inverse(), b); + } + + private static Y convert(BiMap bimap, X input) { + Y output = bimap.get(input); + checkArgument(output != null, "No non-null mapping present for input: %s", input); + return output; + } + + @Override + public boolean equals(Object object) { + if (object instanceof BiMapConverter) { + BiMapConverter that = (BiMapConverter) object; + return this.bimap.equals(that.bimap); + } + return false; + } + + @Override + public int hashCode() { + return bimap.hashCode(); + } + + // There's really no good way to implement toString() without printing the entire BiMap, right? + @Override + public String toString() { + return "Maps.asConverter(" + bimap + ")"; + } + + private static final long serialVersionUID = 0L; + } + + /** + * Returns a synchronized (thread-safe) bimap backed by the specified bimap. In order to guarantee + * serial access, it is critical that all access to the backing bimap is accomplished + * through the returned bimap. + * + *

It is imperative that the user manually synchronize on the returned map when accessing any + * of its collection views: + * + *

{@code
+   * BiMap map = Maps.synchronizedBiMap(
+   *     HashBiMap.create());
+   * ...
+   * Set set = map.keySet();  // Needn't be in synchronized block
+   * ...
+   * synchronized (map) {  // Synchronizing on map, not set!
+   *   Iterator it = set.iterator(); // Must be in synchronized block
+   *   while (it.hasNext()) {
+   *     foo(it.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned bimap will be serializable if the specified bimap is serializable. + * + * @param bimap the bimap to be wrapped in a synchronized view + * @return a synchronized view of the specified bimap + */ + public static BiMap synchronizedBiMap( + BiMap bimap) { + return Synchronized.biMap(bimap, null); + } + + /** + * Returns an unmodifiable view of the specified bimap. This method allows modules to provide + * users with "read-only" access to internal bimaps. Query operations on the returned bimap "read + * through" to the specified bimap, and attempts to modify the returned map, whether direct or via + * its collection views, result in an {@code UnsupportedOperationException}. + * + *

The returned bimap will be serializable if the specified bimap is serializable. + * + * @param bimap the bimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified bimap + */ + public static BiMap unmodifiableBiMap( + BiMap bimap) { + return new UnmodifiableBiMap<>(bimap, null); + } + + /** + * @see Maps#unmodifiableBiMap(BiMap) + */ + private static class UnmodifiableBiMap + extends ForwardingMap implements BiMap, Serializable { + final Map unmodifiableMap; + final BiMap delegate; + BiMap inverse; + transient Set values; + + UnmodifiableBiMap(BiMap delegate, BiMap inverse) { + unmodifiableMap = Collections.unmodifiableMap(delegate); + this.delegate = delegate; + this.inverse = inverse; + } + + @Override + protected Map delegate() { + return unmodifiableMap; + } + + @Override + public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceAll(BiFunction function) { + throw new UnsupportedOperationException(); + } + + @Override + public V putIfAbsent(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public V replace(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public V computeIfAbsent( + K key, java.util.function.Function mappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public V computeIfPresent( + K key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public V merge(K key, V value, BiFunction function) { + throw new UnsupportedOperationException(); + } + + @Override + public BiMap inverse() { + BiMap result = inverse; + return (result == null) + ? inverse = new UnmodifiableBiMap<>(delegate.inverse(), this) + : result; + } + + @Override + public Set values() { + Set result = values; + return (result == null) ? values = Collections.unmodifiableSet(delegate.values()) : result; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a view of a map where each value is transformed by a function. All other properties of + * the map, such as iteration order, are left intact. For example, the code: + * + *

{@code
+   * Map map = ImmutableMap.of("a", 4, "b", 9);
+   * Function sqrt =
+   *     new Function() {
+   *       public Double apply(Integer in) {
+   *         return Math.sqrt((int) in);
+   *       }
+   *     };
+   * Map transformed = Maps.transformValues(map, sqrt);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {a=2.0, b=3.0}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys, and even null values provided + * that the function is capable of accepting null input. The transformed map might contain null + * values, if the function sometimes gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the underlying map is. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned map + * to be a view, but it means that the function will be applied many times for bulk operations + * like {@link Map#containsValue} and {@code Map.toString()}. For this to perform well, {@code + * function} should be fast. To avoid lazy evaluation when the returned map doesn't need to be a + * view, copy the returned map into a new map of your choosing. + */ + public static Map transformValues( + Map fromMap, Function function) { + return transformEntries(fromMap, asEntryTransformer(function)); + } + + /** + * Returns a view of a sorted map where each value is transformed by a function. All other + * properties of the map, such as iteration order, are left intact. For example, the code: + * + *

{@code
+   * SortedMap map = ImmutableSortedMap.of("a", 4, "b", 9);
+   * Function sqrt =
+   *     new Function() {
+   *       public Double apply(Integer in) {
+   *         return Math.sqrt((int) in);
+   *       }
+   *     };
+   * SortedMap transformed =
+   *      Maps.transformValues(map, sqrt);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {a=2.0, b=3.0}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys, and even null values provided + * that the function is capable of accepting null input. The transformed map might contain null + * values, if the function sometimes gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the underlying map is. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned map + * to be a view, but it means that the function will be applied many times for bulk operations + * like {@link Map#containsValue} and {@code Map.toString()}. For this to perform well, {@code + * function} should be fast. To avoid lazy evaluation when the returned map doesn't need to be a + * view, copy the returned map into a new map of your choosing. + * + * @since 11.0 + */ + public static + SortedMap transformValues( + SortedMap fromMap, Function function) { + return transformEntries(fromMap, asEntryTransformer(function)); + } + + /** + * Returns a view of a navigable map where each value is transformed by a function. All other + * properties of the map, such as iteration order, are left intact. For example, the code: + * + *

{@code
+   * NavigableMap map = Maps.newTreeMap();
+   * map.put("a", 4);
+   * map.put("b", 9);
+   * Function sqrt =
+   *     new Function() {
+   *       public Double apply(Integer in) {
+   *         return Math.sqrt((int) in);
+   *       }
+   *     };
+   * NavigableMap transformed =
+   *      Maps.transformNavigableValues(map, sqrt);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {a=2.0, b=3.0}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys, and even null values provided + * that the function is capable of accepting null input. The transformed map might contain null + * values, if the function sometimes gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the underlying map is. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned map + * to be a view, but it means that the function will be applied many times for bulk operations + * like {@link Map#containsValue} and {@code Map.toString()}. For this to perform well, {@code + * function} should be fast. To avoid lazy evaluation when the returned map doesn't need to be a + * view, copy the returned map into a new map of your choosing. + * + * @since 13.0 + */ + @GwtIncompatible // NavigableMap + public static + NavigableMap transformValues( + NavigableMap fromMap, Function function) { + return transformEntries(fromMap, asEntryTransformer(function)); + } + + /** + * Returns a view of a map whose values are derived from the original map's entries. In contrast + * to {@link #transformValues}, this method's entry-transformation logic may depend on the key as + * well as the value. + * + *

All other properties of the transformed map, such as iteration order, are left intact. For + * example, the code: + * + *

{@code
+   * Map options =
+   *     ImmutableMap.of("verbose", true, "sort", false);
+   * EntryTransformer flagPrefixer =
+   *     new EntryTransformer() {
+   *       public String transformEntry(String key, Boolean value) {
+   *         return value ? key : "no" + key;
+   *       }
+   *     };
+   * Map transformed =
+   *     Maps.transformEntries(options, flagPrefixer);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {verbose=verbose, sort=nosort}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys and null values provided that + * the transformer is capable of accepting null inputs. The transformed map might contain null + * values if the transformer sometimes gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the underlying map is. + * + *

The transformer is applied lazily, invoked when needed. This is necessary for the returned + * map to be a view, but it means that the transformer will be applied many times for bulk + * operations like {@link Map#containsValue} and {@link Object#toString}. For this to perform + * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned map + * doesn't need to be a view, copy the returned map into a new map of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of {@code + * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of + * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the + * transformed map. + * + * @since 7.0 + */ + public static + Map transformEntries( + Map fromMap, EntryTransformer transformer) { + return new TransformedEntriesMap<>(fromMap, transformer); + } + + /** + * Returns a view of a sorted map whose values are derived from the original sorted map's entries. + * In contrast to {@link #transformValues}, this method's entry-transformation logic may depend on + * the key as well as the value. + * + *

All other properties of the transformed map, such as iteration order, are left intact. For + * example, the code: + * + *

{@code
+   * Map options =
+   *     ImmutableSortedMap.of("verbose", true, "sort", false);
+   * EntryTransformer flagPrefixer =
+   *     new EntryTransformer() {
+   *       public String transformEntry(String key, Boolean value) {
+   *         return value ? key : "yes" + key;
+   *       }
+   *     };
+   * SortedMap transformed =
+   *     Maps.transformEntries(options, flagPrefixer);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {sort=yessort, verbose=verbose}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys and null values provided that + * the transformer is capable of accepting null inputs. The transformed map might contain null + * values if the transformer sometimes gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the underlying map is. + * + *

The transformer is applied lazily, invoked when needed. This is necessary for the returned + * map to be a view, but it means that the transformer will be applied many times for bulk + * operations like {@link Map#containsValue} and {@link Object#toString}. For this to perform + * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned map + * doesn't need to be a view, copy the returned map into a new map of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of {@code + * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of + * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the + * transformed map. + * + * @since 11.0 + */ + public static + SortedMap transformEntries( + SortedMap fromMap, EntryTransformer transformer) { + return new TransformedEntriesSortedMap<>(fromMap, transformer); + } + + /** + * Returns a view of a navigable map whose values are derived from the original navigable map's + * entries. In contrast to {@link #transformValues}, this method's entry-transformation logic may + * depend on the key as well as the value. + * + *

All other properties of the transformed map, such as iteration order, are left intact. For + * example, the code: + * + *

{@code
+   * NavigableMap options = Maps.newTreeMap();
+   * options.put("verbose", false);
+   * options.put("sort", true);
+   * EntryTransformer flagPrefixer =
+   *     new EntryTransformer() {
+   *       public String transformEntry(String key, Boolean value) {
+   *         return value ? key : ("yes" + key);
+   *       }
+   *     };
+   * NavigableMap transformed =
+   *     LabsMaps.transformNavigableEntries(options, flagPrefixer);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {sort=yessort, verbose=verbose}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys and null values provided that + * the transformer is capable of accepting null inputs. The transformed map might contain null + * values if the transformer sometimes gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the underlying map is. + * + *

The transformer is applied lazily, invoked when needed. This is necessary for the returned + * map to be a view, but it means that the transformer will be applied many times for bulk + * operations like {@link Map#containsValue} and {@link Object#toString}. For this to perform + * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned map + * doesn't need to be a view, copy the returned map into a new map of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of {@code + * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of + * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the + * transformed map. + * + * @since 13.0 + */ + @GwtIncompatible // NavigableMap + public static + NavigableMap transformEntries( + NavigableMap fromMap, EntryTransformer transformer) { + return new TransformedEntriesNavigableMap<>(fromMap, transformer); + } + + /** + * A transformation of the value of a key-value pair, using both key and value as inputs. To apply + * the transformation to a map, use {@link Maps#transformEntries(Map, EntryTransformer)}. + * + * @param the key type of the input and output entries + * @param the value type of the input entry + * @param the value type of the output entry + * @since 7.0 + */ + @FunctionalInterface + public interface EntryTransformer { + /** + * Determines an output value based on a key-value pair. This method is generally + * expected, but not absolutely required, to have the following properties: + * + *

    + *
  • Its execution does not cause any observable side effects. + *
  • The computation is consistent with equals; that is, {@link Objects#equal + * Objects.equal}{@code (k1, k2) &&} {@link Objects#equal}{@code (v1, v2)} implies that + * {@code Objects.equal(transformer.transform(k1, v1), transformer.transform(k2, v2))}. + *
+ * + * @throws NullPointerException if the key or value is null and this transformer does not accept + * null arguments + */ + @ParametricNullness + V2 transformEntry(@ParametricNullness K key, @ParametricNullness V1 value); + } + + /** Views a function as an entry transformer that ignores the entry key. */ + static + EntryTransformer asEntryTransformer(final Function function) { + checkNotNull(function); + return new EntryTransformer() { + @Override + @ParametricNullness + public V2 transformEntry(@ParametricNullness K key, @ParametricNullness V1 value) { + return function.apply(value); + } + }; + } + + static + Function asValueToValueFunction( + final EntryTransformer transformer, @ParametricNullness final K key) { + checkNotNull(transformer); + return new Function() { + @Override + @ParametricNullness + public V2 apply(@ParametricNullness V1 v1) { + return transformer.transformEntry(key, v1); + } + }; + } + + /** Views an entry transformer as a function from {@code Entry} to values. */ + static + Function, V2> asEntryToValueFunction( + final EntryTransformer transformer) { + checkNotNull(transformer); + return new Function, V2>() { + @Override + @ParametricNullness + public V2 apply(Entry entry) { + return transformer.transformEntry(entry.getKey(), entry.getValue()); + } + }; + } + + /** Returns a view of an entry transformed by the specified transformer. */ + static Entry transformEntry( + final EntryTransformer transformer, final Entry entry) { + checkNotNull(transformer); + checkNotNull(entry); + return new AbstractMapEntry() { + @Override + @ParametricNullness + public K getKey() { + return entry.getKey(); + } + + @Override + @ParametricNullness + public V2 getValue() { + return transformer.transformEntry(entry.getKey(), entry.getValue()); + } + }; + } + + /** Views an entry transformer as a function from entries to entries. */ + static + Function, Entry> asEntryToEntryFunction( + final EntryTransformer transformer) { + checkNotNull(transformer); + return new Function, Entry>() { + @Override + public Entry apply(final Entry entry) { + return transformEntry(transformer, entry); + } + }; + } + + static class TransformedEntriesMap + extends IteratorBasedAbstractMap { + final Map fromMap; + final EntryTransformer transformer; + + TransformedEntriesMap( + Map fromMap, EntryTransformer transformer) { + this.fromMap = checkNotNull(fromMap); + this.transformer = checkNotNull(transformer); + } + + @Override + public int size() { + return fromMap.size(); + } + + @Override + public boolean containsKey(Object key) { + return fromMap.containsKey(key); + } + + @Override + public V2 get(Object key) { + return getOrDefault(key, null); + } + + // safe as long as the user followed the Warning in the javadoc + @SuppressWarnings("unchecked") + @Override + public V2 getOrDefault(Object key, V2 defaultValue) { + V1 value = fromMap.get(key); + if (value != null || fromMap.containsKey(key)) { + // The cast is safe because of the containsKey check. + return transformer.transformEntry((K) key, uncheckedCastNullableTToT(value)); + } + return defaultValue; + } + + // safe as long as the user followed the Warning in the javadoc + @SuppressWarnings("unchecked") + @Override + public V2 remove(Object key) { + return fromMap.containsKey(key) + // The cast is safe because of the containsKey check. + ? transformer.transformEntry((K) key, uncheckedCastNullableTToT(fromMap.remove(key))) + : null; + } + + @Override + public void clear() { + fromMap.clear(); + } + + @Override + public Set keySet() { + return fromMap.keySet(); + } + + @Override + Iterator> entryIterator() { + return Iterators.transform( + fromMap.entrySet().iterator(), Maps.asEntryToEntryFunction(transformer)); + } + + @Override + Spliterator> entrySpliterator() { + return CollectSpliterators.map( + fromMap.entrySet().spliterator(), Maps.asEntryToEntryFunction(transformer)); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + // avoids creating new Entry objects + fromMap.forEach((k, v1) -> action.accept(k, transformer.transformEntry(k, v1))); + } + + @Override + public Collection values() { + return new Values<>(this); + } + } + + static class TransformedEntriesSortedMap + extends TransformedEntriesMap implements SortedMap { + + protected SortedMap fromMap() { + return (SortedMap) fromMap; + } + + TransformedEntriesSortedMap( + SortedMap fromMap, EntryTransformer transformer) { + super(fromMap, transformer); + } + + @Override + public Comparator comparator() { + return fromMap().comparator(); + } + + @Override + @ParametricNullness + public K firstKey() { + return fromMap().firstKey(); + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return transformEntries(fromMap().headMap(toKey), transformer); + } + + @Override + @ParametricNullness + public K lastKey() { + return fromMap().lastKey(); + } + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return transformEntries(fromMap().subMap(fromKey, toKey), transformer); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return transformEntries(fromMap().tailMap(fromKey), transformer); + } + } + + @GwtIncompatible // NavigableMap + private static class TransformedEntriesNavigableMap< + K extends Object, V1 extends Object, V2 extends Object> + extends TransformedEntriesSortedMap implements NavigableMap { + + TransformedEntriesNavigableMap( + NavigableMap fromMap, EntryTransformer transformer) { + super(fromMap, transformer); + } + + @Override + public Entry ceilingEntry(@ParametricNullness K key) { + return transformEntry(fromMap().ceilingEntry(key)); + } + + @Override + public K ceilingKey(@ParametricNullness K key) { + return fromMap().ceilingKey(key); + } + + @Override + public NavigableSet descendingKeySet() { + return fromMap().descendingKeySet(); + } + + @Override + public NavigableMap descendingMap() { + return transformEntries(fromMap().descendingMap(), transformer); + } + + @Override + public Entry firstEntry() { + return transformEntry(fromMap().firstEntry()); + } + + @Override + public Entry floorEntry(@ParametricNullness K key) { + return transformEntry(fromMap().floorEntry(key)); + } + + @Override + public K floorKey(@ParametricNullness K key) { + return fromMap().floorKey(key); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey) { + return headMap(toKey, false); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusive) { + return transformEntries(fromMap().headMap(toKey, inclusive), transformer); + } + + @Override + public Entry higherEntry(@ParametricNullness K key) { + return transformEntry(fromMap().higherEntry(key)); + } + + @Override + public K higherKey(@ParametricNullness K key) { + return fromMap().higherKey(key); + } + + @Override + public Entry lastEntry() { + return transformEntry(fromMap().lastEntry()); + } + + @Override + public Entry lowerEntry(@ParametricNullness K key) { + return transformEntry(fromMap().lowerEntry(key)); + } + + @Override + public K lowerKey(@ParametricNullness K key) { + return fromMap().lowerKey(key); + } + + @Override + public NavigableSet navigableKeySet() { + return fromMap().navigableKeySet(); + } + + @Override + public Entry pollFirstEntry() { + return transformEntry(fromMap().pollFirstEntry()); + } + + @Override + public Entry pollLastEntry() { + return transformEntry(fromMap().pollLastEntry()); + } + + @Override + public NavigableMap subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return transformEntries( + fromMap().subMap(fromKey, fromInclusive, toKey, toInclusive), transformer); + } + + @Override + public NavigableMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusive) { + return transformEntries(fromMap().tailMap(fromKey, inclusive), transformer); + } + + private Entry transformEntry(Entry entry) { + return (entry == null) ? null : Maps.transformEntry(transformer, entry); + } + + @Override + protected NavigableMap fromMap() { + return (NavigableMap) super.fromMap(); + } + } + + static Predicate> keyPredicateOnEntries( + Predicate keyPredicate) { + return compose(keyPredicate, Maps.keyFunction()); + } + + static Predicate> valuePredicateOnEntries( + Predicate valuePredicate) { + return compose(valuePredicate, Maps.valueFunction()); + } + + /** + * Returns a map containing the mappings in {@code unfiltered} whose keys satisfy a predicate. The + * returned map is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a key that doesn't satisfy the predicate, the map's {@code put()} and + * {@code putAll()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings whose keys satisfy the filter will be removed from the underlying + * map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code keyPredicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + */ + public static Map filterKeys( + Map unfiltered, final Predicate keyPredicate) { + checkNotNull(keyPredicate); + Predicate> entryPredicate = keyPredicateOnEntries(keyPredicate); + return (unfiltered instanceof AbstractFilteredMap) + ? filterFiltered((AbstractFilteredMap) unfiltered, entryPredicate) + : new FilteredKeyMap(checkNotNull(unfiltered), keyPredicate, entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose keys satisfy a + * predicate. The returned map is a live view of {@code unfiltered}; changes to one affect the + * other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a key that doesn't satisfy the predicate, the map's {@code put()} and + * {@code putAll()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings whose keys satisfy the filter will be removed from the underlying + * map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code keyPredicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 11.0 + */ + public static SortedMap filterKeys( + SortedMap unfiltered, final Predicate keyPredicate) { + // TODO(lowasser): Return a subclass of Maps.FilteredKeyMap for slightly better + // performance. + return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); + } + + /** + * Returns a navigable map containing the mappings in {@code unfiltered} whose keys satisfy a + * predicate. The returned map is a live view of {@code unfiltered}; changes to one affect the + * other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a key that doesn't satisfy the predicate, the map's {@code put()} and + * {@code putAll()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings whose keys satisfy the filter will be removed from the underlying + * map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code keyPredicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 14.0 + */ + @GwtIncompatible // NavigableMap + public static NavigableMap filterKeys( + NavigableMap unfiltered, final Predicate keyPredicate) { + // TODO(lowasser): Return a subclass of Maps.FilteredKeyMap for slightly better + // performance. + return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); + } + + /** + * Returns a bimap containing the mappings in {@code unfiltered} whose keys satisfy a predicate. + * The returned bimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the bimap + * and its views. When given a key that doesn't satisfy the predicate, the bimap's {@code put()}, + * {@code forcePut()} and {@code putAll()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * bimap or its views, only mappings that satisfy the filter will be removed from the underlying + * bimap. + * + *

The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered bimap's methods, such as {@code size()}, iterate across every key in + * the underlying bimap and determine which satisfy the filter. When a live view is not + * needed, it may be faster to copy the filtered bimap and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals , as documented + * at {@link Predicate#apply}. + * + * @since 14.0 + */ + public static BiMap filterKeys( + BiMap unfiltered, final Predicate keyPredicate) { + checkNotNull(keyPredicate); + return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); + } + + /** + * Returns a map containing the mappings in {@code unfiltered} whose values satisfy a predicate. + * The returned map is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a value that doesn't satisfy the predicate, the map's {@code put()}, + * {@code putAll()}, and {@link Entry#setValue} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings whose values satisfy the filter will be removed from the underlying + * map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code valuePredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + */ + public static Map filterValues( + Map unfiltered, final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose values satisfy a + * predicate. The returned map is a live view of {@code unfiltered}; changes to one affect the + * other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a value that doesn't satisfy the predicate, the map's {@code put()}, + * {@code putAll()}, and {@link Entry#setValue} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings whose values satisfy the filter will be removed from the underlying + * map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code valuePredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 11.0 + */ + public static SortedMap filterValues( + SortedMap unfiltered, final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a navigable map containing the mappings in {@code unfiltered} whose values satisfy a + * predicate. The returned map is a live view of {@code unfiltered}; changes to one affect the + * other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a value that doesn't satisfy the predicate, the map's {@code put()}, + * {@code putAll()}, and {@link Entry#setValue} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings whose values satisfy the filter will be removed from the underlying + * map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code valuePredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 14.0 + */ + @GwtIncompatible // NavigableMap + public static NavigableMap filterValues( + NavigableMap unfiltered, final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a bimap containing the mappings in {@code unfiltered} whose values satisfy a predicate. + * The returned bimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the bimap + * and its views. When given a value that doesn't satisfy the predicate, the bimap's {@code + * put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link + * IllegalArgumentException}. Similarly, the map's entries have a {@link Entry#setValue} method + * that throws an {@link IllegalArgumentException} when the provided value doesn't satisfy the + * predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * bimap or its views, only mappings that satisfy the filter will be removed from the underlying + * bimap. + * + *

The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered bimap's methods, such as {@code size()}, iterate across every value in + * the underlying bimap and determine which satisfy the filter. When a live view is not + * needed, it may be faster to copy the filtered bimap and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals , as documented + * at {@link Predicate#apply}. + * + * @since 14.0 + */ + public static BiMap filterValues( + BiMap unfiltered, final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a map containing the mappings in {@code unfiltered} that satisfy a predicate. The + * returned map is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a key/value pair that doesn't satisfy the predicate, the map's {@code + * put()} and {@code putAll()} methods throw an {@link IllegalArgumentException}. Similarly, the + * map's entries have a {@link Entry#setValue} method that throws an {@link + * IllegalArgumentException} when the existing key and the provided value don't satisfy the + * predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings that satisfy the filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. + */ + public static Map filterEntries( + Map unfiltered, Predicate> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof AbstractFilteredMap) + ? filterFiltered((AbstractFilteredMap) unfiltered, entryPredicate) + : new FilteredEntryMap(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} that satisfy a predicate. + * The returned map is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a key/value pair that doesn't satisfy the predicate, the map's {@code + * put()} and {@code putAll()} methods throw an {@link IllegalArgumentException}. Similarly, the + * map's entries have a {@link Entry#setValue} method that throws an {@link + * IllegalArgumentException} when the existing key and the provided value don't satisfy the + * predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings that satisfy the filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. + * + * @since 11.0 + */ + public static SortedMap filterEntries( + SortedMap unfiltered, Predicate> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredEntrySortedMap) + ? filterFiltered((FilteredEntrySortedMap) unfiltered, entryPredicate) + : new FilteredEntrySortedMap(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} that satisfy a predicate. + * The returned map is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the map + * and its views. When given a key/value pair that doesn't satisfy the predicate, the map's {@code + * put()} and {@code putAll()} methods throw an {@link IllegalArgumentException}. Similarly, the + * map's entries have a {@link Entry#setValue} method that throws an {@link + * IllegalArgumentException} when the existing key and the provided value don't satisfy the + * predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered map + * or its views, only mappings that satisfy the filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying map and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered map and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. + * + * @since 14.0 + */ + @GwtIncompatible // NavigableMap + public static NavigableMap filterEntries( + NavigableMap unfiltered, Predicate> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredEntryNavigableMap) + ? filterFiltered((FilteredEntryNavigableMap) unfiltered, entryPredicate) + : new FilteredEntryNavigableMap(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Returns a bimap containing the mappings in {@code unfiltered} that satisfy a predicate. The + * returned bimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the bimap + * and its views. When given a key/value pair that doesn't satisfy the predicate, the bimap's + * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link + * IllegalArgumentException}. Similarly, the map's entries have an {@link Entry#setValue} method + * that throws an {@link IllegalArgumentException} when the existing key and the provided value + * don't satisfy the predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * bimap or its views, only mappings that satisfy the filter will be removed from the underlying + * bimap. + * + *

The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered bimap's methods, such as {@code size()}, iterate across every key/value + * mapping in the underlying bimap and determine which satisfy the filter. When a live view is + * not needed, it may be faster to copy the filtered bimap and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals , as documented + * at {@link Predicate#apply}. + * + * @since 14.0 + */ + public static BiMap filterEntries( + BiMap unfiltered, Predicate> entryPredicate) { + checkNotNull(unfiltered); + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredEntryBiMap) + ? filterFiltered((FilteredEntryBiMap) unfiltered, entryPredicate) + : new FilteredEntryBiMap(unfiltered, entryPredicate); + } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when filtering a filtered + * map. + */ + private static Map filterFiltered( + AbstractFilteredMap map, Predicate> entryPredicate) { + return new FilteredEntryMap<>( + map.unfiltered, Predicates.>and(map.predicate, entryPredicate)); + } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when filtering a filtered + * sorted map. + */ + private static SortedMap filterFiltered( + FilteredEntrySortedMap map, Predicate> entryPredicate) { + Predicate> predicate = Predicates.>and(map.predicate, entryPredicate); + return new FilteredEntrySortedMap<>(map.sortedMap(), predicate); + } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when filtering a filtered + * navigable map. + */ + @GwtIncompatible // NavigableMap + private static NavigableMap filterFiltered( + FilteredEntryNavigableMap map, Predicate> entryPredicate) { + Predicate> predicate = + Predicates.>and(map.entryPredicate, entryPredicate); + return new FilteredEntryNavigableMap<>(map.unfiltered, predicate); + } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when filtering a filtered + * map. + */ + private static BiMap filterFiltered( + FilteredEntryBiMap map, Predicate> entryPredicate) { + Predicate> predicate = Predicates.>and(map.predicate, entryPredicate); + return new FilteredEntryBiMap<>(map.unfiltered(), predicate); + } + + private abstract static class AbstractFilteredMap + extends ViewCachingAbstractMap { + final Map unfiltered; + final Predicate> predicate; + + AbstractFilteredMap(Map unfiltered, Predicate> predicate) { + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + boolean apply(Object key, @ParametricNullness V value) { + // This method is called only when the key is in the map (or about to be added to the map), + // implying that key is a K. + @SuppressWarnings({"unchecked", "nullness"}) + K k = (K) key; + return predicate.apply(Maps.immutableEntry(k, value)); + } + + @Override + public V put(@ParametricNullness K key, @ParametricNullness V value) { + checkArgument(apply(key, value)); + return unfiltered.put(key, value); + } + + @Override + public void putAll(Map map) { + for (Entry entry : map.entrySet()) { + checkArgument(apply(entry.getKey(), entry.getValue())); + } + unfiltered.putAll(map); + } + + @Override + public boolean containsKey(Object key) { + return unfiltered.containsKey(key) && apply(key, unfiltered.get(key)); + } + + @Override + public V get(Object key) { + V value = unfiltered.get(key); + return ((value != null) && apply(key, value)) ? value : null; + } + + @Override + public boolean isEmpty() { + return entrySet().isEmpty(); + } + + @Override + public V remove(Object key) { + return containsKey(key) ? unfiltered.remove(key) : null; + } + + @Override + Collection createValues() { + return new FilteredMapValues<>(this, unfiltered, predicate); + } + } + + private static final class FilteredMapValues + extends Maps.Values { + final Map unfiltered; + final Predicate> predicate; + + FilteredMapValues( + Map filteredMap, Map unfiltered, Predicate> predicate) { + super(filteredMap); + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + @Override + public boolean remove(Object o) { + Iterator> entryItr = unfiltered.entrySet().iterator(); + while (entryItr.hasNext()) { + Entry entry = entryItr.next(); + if (predicate.apply(entry) && Objects.equal(entry.getValue(), o)) { + entryItr.remove(); + return true; + } + } + return false; + } + + @Override + public boolean removeAll(Collection collection) { + Iterator> entryItr = unfiltered.entrySet().iterator(); + boolean result = false; + while (entryItr.hasNext()) { + Entry entry = entryItr.next(); + if (predicate.apply(entry) && collection.contains(entry.getValue())) { + entryItr.remove(); + result = true; + } + } + return result; + } + + @Override + public boolean retainAll(Collection collection) { + Iterator> entryItr = unfiltered.entrySet().iterator(); + boolean result = false; + while (entryItr.hasNext()) { + Entry entry = entryItr.next(); + if (predicate.apply(entry) && !collection.contains(entry.getValue())) { + entryItr.remove(); + result = true; + } + } + return result; + } + + @Override + public Object[] toArray() { + // creating an ArrayList so filtering happens once + return Lists.newArrayList(iterator()).toArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return Lists.newArrayList(iterator()).toArray(array); + } + } + + private static class FilteredKeyMap + extends AbstractFilteredMap { + final Predicate keyPredicate; + + FilteredKeyMap( + Map unfiltered, + Predicate keyPredicate, + Predicate> entryPredicate) { + super(unfiltered, entryPredicate); + this.keyPredicate = keyPredicate; + } + + @Override + protected Set> createEntrySet() { + return Sets.filter(unfiltered.entrySet(), predicate); + } + + @Override + Set createKeySet() { + return Sets.filter(unfiltered.keySet(), keyPredicate); + } + + // The cast is called only when the key is in the unfiltered map, implying + // that key is a K. + @Override + @SuppressWarnings("unchecked") + public boolean containsKey(Object key) { + return unfiltered.containsKey(key) && keyPredicate.apply((K) key); + } + } + + static class FilteredEntryMap + extends AbstractFilteredMap { + /** + * Entries in this set satisfy the predicate, but they don't validate the input to {@code + * Entry.setValue()}. + */ + final Set> filteredEntrySet; + + FilteredEntryMap(Map unfiltered, Predicate> entryPredicate) { + super(unfiltered, entryPredicate); + filteredEntrySet = Sets.filter(unfiltered.entrySet(), predicate); + } + + @Override + protected Set> createEntrySet() { + return new EntrySet(); + } + + private class EntrySet extends ForwardingSet> { + @Override + protected Set> delegate() { + return filteredEntrySet; + } + + @Override + public Iterator> iterator() { + return new TransformedIterator, Entry>(filteredEntrySet.iterator()) { + @Override + Entry transform(final Entry entry) { + return new ForwardingMapEntry() { + @Override + protected Entry delegate() { + return entry; + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V newValue) { + checkArgument(apply(getKey(), newValue)); + return super.setValue(newValue); + } + }; + } + }; + } + } + + @Override + Set createKeySet() { + return new KeySet(); + } + + static boolean removeAllKeys( + Map map, Predicate> entryPredicate, Collection keyCollection) { + Iterator> entryItr = map.entrySet().iterator(); + boolean result = false; + while (entryItr.hasNext()) { + Entry entry = entryItr.next(); + if (entryPredicate.apply(entry) && keyCollection.contains(entry.getKey())) { + entryItr.remove(); + result = true; + } + } + return result; + } + + static boolean retainAllKeys( + Map map, Predicate> entryPredicate, Collection keyCollection) { + Iterator> entryItr = map.entrySet().iterator(); + boolean result = false; + while (entryItr.hasNext()) { + Entry entry = entryItr.next(); + if (entryPredicate.apply(entry) && !keyCollection.contains(entry.getKey())) { + entryItr.remove(); + result = true; + } + } + return result; + } + + class KeySet extends Maps.KeySet { + KeySet() { + super(FilteredEntryMap.this); + } + + @Override + public boolean remove(Object o) { + if (containsKey(o)) { + unfiltered.remove(o); + return true; + } + return false; + } + + @Override + public boolean removeAll(Collection collection) { + return removeAllKeys(unfiltered, predicate, collection); + } + + @Override + public boolean retainAll(Collection collection) { + return retainAllKeys(unfiltered, predicate, collection); + } + + @Override + public Object[] toArray() { + // creating an ArrayList so filtering happens once + return Lists.newArrayList(iterator()).toArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return Lists.newArrayList(iterator()).toArray(array); + } + } + } + + private static class FilteredEntrySortedMap + extends FilteredEntryMap implements SortedMap { + + FilteredEntrySortedMap( + SortedMap unfiltered, Predicate> entryPredicate) { + super(unfiltered, entryPredicate); + } + + SortedMap sortedMap() { + return (SortedMap) unfiltered; + } + + @Override + public SortedSet keySet() { + return (SortedSet) super.keySet(); + } + + @Override + SortedSet createKeySet() { + return new SortedKeySet(); + } + + class SortedKeySet extends KeySet implements SortedSet { + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + public SortedSet subSet( + @ParametricNullness K fromElement, @ParametricNullness K toElement) { + return (SortedSet) subMap(fromElement, toElement).keySet(); + } + + @Override + public SortedSet headSet(@ParametricNullness K toElement) { + return (SortedSet) headMap(toElement).keySet(); + } + + @Override + public SortedSet tailSet(@ParametricNullness K fromElement) { + return (SortedSet) tailMap(fromElement).keySet(); + } + + @Override + @ParametricNullness + public K first() { + return firstKey(); + } + + @Override + @ParametricNullness + public K last() { + return lastKey(); + } + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + @ParametricNullness + public K firstKey() { + // correctly throws NoSuchElementException when filtered map is empty. + return keySet().iterator().next(); + } + + @Override + @ParametricNullness + public K lastKey() { + SortedMap headMap = sortedMap(); + while (true) { + // correctly throws NoSuchElementException when filtered map is empty. + K key = headMap.lastKey(); + // The cast is safe because the key is taken from the map. + if (apply(key, uncheckedCastNullableTToT(unfiltered.get(key)))) { + return key; + } + headMap = sortedMap().headMap(key); + } + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return new FilteredEntrySortedMap<>(sortedMap().headMap(toKey), predicate); + } + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return new FilteredEntrySortedMap<>(sortedMap().subMap(fromKey, toKey), predicate); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return new FilteredEntrySortedMap<>(sortedMap().tailMap(fromKey), predicate); + } + } + + @GwtIncompatible // NavigableMap + private static class FilteredEntryNavigableMap + extends AbstractNavigableMap { + /* + * It's less code to extend AbstractNavigableMap and forward the filtering logic to + * FilteredEntryMap than to extend FilteredEntrySortedMap and reimplement all the NavigableMap + * methods. + */ + + private final NavigableMap unfiltered; + private final Predicate> entryPredicate; + private final Map filteredDelegate; + + FilteredEntryNavigableMap( + NavigableMap unfiltered, Predicate> entryPredicate) { + this.unfiltered = checkNotNull(unfiltered); + this.entryPredicate = entryPredicate; + this.filteredDelegate = new FilteredEntryMap<>(unfiltered, entryPredicate); + } + + @Override + public Comparator comparator() { + return unfiltered.comparator(); + } + + @Override + public NavigableSet navigableKeySet() { + return new Maps.NavigableKeySet(this) { + @Override + public boolean removeAll(Collection collection) { + return FilteredEntryMap.removeAllKeys(unfiltered, entryPredicate, collection); + } + + @Override + public boolean retainAll(Collection collection) { + return FilteredEntryMap.retainAllKeys(unfiltered, entryPredicate, collection); + } + }; + } + + @Override + public Collection values() { + return new FilteredMapValues<>(this, unfiltered, entryPredicate); + } + + @Override + Iterator> entryIterator() { + return Iterators.filter(unfiltered.entrySet().iterator(), entryPredicate); + } + + @Override + Iterator> descendingEntryIterator() { + return Iterators.filter(unfiltered.descendingMap().entrySet().iterator(), entryPredicate); + } + + @Override + public int size() { + return filteredDelegate.size(); + } + + @Override + public boolean isEmpty() { + return !Iterables.any(unfiltered.entrySet(), entryPredicate); + } + + @Override + public V get(Object key) { + return filteredDelegate.get(key); + } + + @Override + public boolean containsKey(Object key) { + return filteredDelegate.containsKey(key); + } + + @Override + public V put(@ParametricNullness K key, @ParametricNullness V value) { + return filteredDelegate.put(key, value); + } + + @Override + public V remove(Object key) { + return filteredDelegate.remove(key); + } + + @Override + public void putAll(Map m) { + filteredDelegate.putAll(m); + } + + @Override + public void clear() { + filteredDelegate.clear(); + } + + @Override + public Set> entrySet() { + return filteredDelegate.entrySet(); + } + + @Override + public Entry pollFirstEntry() { + return Iterables.removeFirstMatching(unfiltered.entrySet(), entryPredicate); + } + + @Override + public Entry pollLastEntry() { + return Iterables.removeFirstMatching(unfiltered.descendingMap().entrySet(), entryPredicate); + } + + @Override + public NavigableMap descendingMap() { + return filterEntries(unfiltered.descendingMap(), entryPredicate); + } + + @Override + public NavigableMap subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return filterEntries( + unfiltered.subMap(fromKey, fromInclusive, toKey, toInclusive), entryPredicate); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusive) { + return filterEntries(unfiltered.headMap(toKey, inclusive), entryPredicate); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusive) { + return filterEntries(unfiltered.tailMap(fromKey, inclusive), entryPredicate); + } + } + + static final class FilteredEntryBiMap + extends FilteredEntryMap implements BiMap { + private final BiMap inverse; + + private static Predicate> inversePredicate( + final Predicate> forwardPredicate) { + return new Predicate>() { + @Override + public boolean apply(Entry input) { + return forwardPredicate.apply(Maps.immutableEntry(input.getValue(), input.getKey())); + } + }; + } + + FilteredEntryBiMap(BiMap delegate, Predicate> predicate) { + super(delegate, predicate); + this.inverse = + new FilteredEntryBiMap<>(delegate.inverse(), inversePredicate(predicate), this); + } + + private FilteredEntryBiMap( + BiMap delegate, Predicate> predicate, BiMap inverse) { + super(delegate, predicate); + this.inverse = inverse; + } + + BiMap unfiltered() { + return (BiMap) unfiltered; + } + + @Override + public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + checkArgument(apply(key, value)); + return unfiltered().forcePut(key, value); + } + + @Override + public void replaceAll(BiFunction function) { + unfiltered() + .replaceAll( + (key, value) -> + predicate.apply(Maps.immutableEntry(key, value)) + ? function.apply(key, value) + : value); + } + + @Override + public BiMap inverse() { + return inverse; + } + + @Override + public Set values() { + return inverse.keySet(); + } + } + + /** + * Returns an unmodifiable view of the specified navigable map. Query operations on the returned + * map read through to the specified map, and attempts to modify the returned map, whether direct + * or via its views, result in an {@code UnsupportedOperationException}. + * + *

The returned navigable map will be serializable if the specified navigable map is + * serializable. + * + *

This method's signature will not permit you to convert a {@code NavigableMap} to a {@code NavigableMap}. If it permitted this, the returned map's {@code + * comparator()} method might return a {@code Comparator}, which works only on a + * particular subtype of {@code K}, but promise that it's a {@code Comparator}, which + * must work on any type of {@code K}. + * + * @param map the navigable map for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified navigable map + * @since 12.0 + */ + @GwtIncompatible // NavigableMap + public static NavigableMap unmodifiableNavigableMap( + NavigableMap map) { + checkNotNull(map); + if (map instanceof UnmodifiableNavigableMap) { + @SuppressWarnings("unchecked") // covariant + NavigableMap result = (NavigableMap) map; + return result; + } else { + return new UnmodifiableNavigableMap<>(map); + } + } + + private static Entry unmodifiableOrNull( + Entry entry) { + return (entry == null) ? null : Maps.unmodifiableEntry(entry); + } + + @GwtIncompatible // NavigableMap + static class UnmodifiableNavigableMap + extends ForwardingSortedMap implements NavigableMap, Serializable { + private final NavigableMap delegate; + + UnmodifiableNavigableMap(NavigableMap delegate) { + this.delegate = delegate; + } + + UnmodifiableNavigableMap( + NavigableMap delegate, UnmodifiableNavigableMap descendingMap) { + this.delegate = delegate; + this.descendingMap = descendingMap; + } + + @Override + protected SortedMap delegate() { + return Collections.unmodifiableSortedMap(delegate); + } + + @Override + public Entry lowerEntry(@ParametricNullness K key) { + return unmodifiableOrNull(delegate.lowerEntry(key)); + } + + @Override + public K lowerKey(@ParametricNullness K key) { + return delegate.lowerKey(key); + } + + @Override + public Entry floorEntry(@ParametricNullness K key) { + return unmodifiableOrNull(delegate.floorEntry(key)); + } + + @Override + public K floorKey(@ParametricNullness K key) { + return delegate.floorKey(key); + } + + @Override + public Entry ceilingEntry(@ParametricNullness K key) { + return unmodifiableOrNull(delegate.ceilingEntry(key)); + } + + @Override + public K ceilingKey(@ParametricNullness K key) { + return delegate.ceilingKey(key); + } + + @Override + public Entry higherEntry(@ParametricNullness K key) { + return unmodifiableOrNull(delegate.higherEntry(key)); + } + + @Override + public K higherKey(@ParametricNullness K key) { + return delegate.higherKey(key); + } + + @Override + public Entry firstEntry() { + return unmodifiableOrNull(delegate.firstEntry()); + } + + @Override + public Entry lastEntry() { + return unmodifiableOrNull(delegate.lastEntry()); + } + + @Override + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceAll(BiFunction function) { + throw new UnsupportedOperationException(); + } + + @Override + public V putIfAbsent(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + throw new UnsupportedOperationException(); + } + + @Override + public V replace(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public V computeIfAbsent( + K key, java.util.function.Function mappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public V computeIfPresent( + K key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + throw new UnsupportedOperationException(); + } + + @Override + public V merge(K key, V value, BiFunction function) { + throw new UnsupportedOperationException(); + } + + private transient UnmodifiableNavigableMap descendingMap; + + @Override + public NavigableMap descendingMap() { + UnmodifiableNavigableMap result = descendingMap; + return (result == null) + ? descendingMap = new UnmodifiableNavigableMap<>(delegate.descendingMap(), this) + : result; + } + + @Override + public Set keySet() { + return navigableKeySet(); + } + + @Override + public NavigableSet navigableKeySet() { + return Sets.unmodifiableNavigableSet(delegate.navigableKeySet()); + } + + @Override + public NavigableSet descendingKeySet() { + return Sets.unmodifiableNavigableSet(delegate.descendingKeySet()); + } + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return Maps.unmodifiableNavigableMap( + delegate.subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return headMap(toKey, false); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusive) { + return Maps.unmodifiableNavigableMap(delegate.headMap(toKey, inclusive)); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusive) { + return Maps.unmodifiableNavigableMap(delegate.tailMap(fromKey, inclusive)); + } + } + + /** + * Returns a synchronized (thread-safe) navigable map backed by the specified navigable map. In + * order to guarantee serial access, it is critical that all access to the backing + * navigable map is accomplished through the returned navigable map (or its views). + * + *

It is imperative that the user manually synchronize on the returned navigable map when + * iterating over any of its collection views, or the collections views of any of its {@code + * descendingMap}, {@code subMap}, {@code headMap} or {@code tailMap} views. + * + *

{@code
+   * NavigableMap map = synchronizedNavigableMap(new TreeMap());
+   *
+   * // Needn't be in synchronized block
+   * NavigableSet set = map.navigableKeySet();
+   *
+   * synchronized (map) { // Synchronizing on map, not set!
+   *   Iterator it = set.iterator(); // Must be in synchronized block
+   *   while (it.hasNext()) {
+   *     foo(it.next());
+   *   }
+   * }
+   * }
+ * + *

or: + * + *

{@code
+   * NavigableMap map = synchronizedNavigableMap(new TreeMap());
+   * NavigableMap map2 = map.subMap(foo, false, bar, true);
+   *
+   * // Needn't be in synchronized block
+   * NavigableSet set2 = map2.descendingKeySet();
+   *
+   * synchronized (map) { // Synchronizing on map, not map2 or set2!
+   *   Iterator it = set2.iterator(); // Must be in synchronized block
+   *   while (it.hasNext()) {
+   *     foo(it.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned navigable map will be serializable if the specified navigable map is + * serializable. + * + * @param navigableMap the navigable map to be "wrapped" in a synchronized navigable map. + * @return a synchronized view of the specified navigable map. + * @since 13.0 + */ + @GwtIncompatible // NavigableMap + public static NavigableMap synchronizedNavigableMap( + NavigableMap navigableMap) { + return Synchronized.navigableMap(navigableMap); + } + + /** + * {@code AbstractMap} extension that makes it easy to cache customized keySet, values, and + * entrySet views. + */ + @GwtCompatible + abstract static class ViewCachingAbstractMap + extends AbstractMap { + /** + * Creates the entry set to be returned by {@link #entrySet()}. This method is invoked at most + * once on a given map, at the time when {@code entrySet} is first called. + */ + abstract Set> createEntrySet(); + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + private transient Set keySet; + + @Override + public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + Set createKeySet() { + return new KeySet<>(this); + } + + private transient Collection values; + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + Collection createValues() { + return new Values<>(this); + } + } + + abstract static class IteratorBasedAbstractMap + extends AbstractMap { + @Override + public abstract int size(); + + abstract Iterator> entryIterator(); + + Spliterator> entrySpliterator() { + return Spliterators.spliterator( + entryIterator(), size(), Spliterator.SIZED | Spliterator.DISTINCT); + } + + @Override + public Set> entrySet() { + return new EntrySet() { + @Override + Map map() { + return IteratorBasedAbstractMap.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + + @Override + public Spliterator> spliterator() { + return entrySpliterator(); + } + + @Override + public void forEach(Consumer> action) { + forEachEntry(action); + } + }; + } + + void forEachEntry(Consumer> action) { + entryIterator().forEachRemaining(action); + } + + @Override + public void clear() { + Iterators.clear(entryIterator()); + } + } + + /** + * Delegates to {@link Map#get}. Returns {@code null} on {@code ClassCastException} and {@code + * NullPointerException}. + */ + static V safeGet(Map map, Object key) { + checkNotNull(map); + try { + return map.get(key); + } catch (ClassCastException | NullPointerException e) { + return null; + } + } + + /** + * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code ClassCastException} and + * {@code NullPointerException}. + */ + static boolean safeContainsKey(Map map, Object key) { + checkNotNull(map); + try { + return map.containsKey(key); + } catch (ClassCastException | NullPointerException e) { + return false; + } + } + + /** + * Delegates to {@link Map#remove}. Returns {@code null} on {@code ClassCastException} and {@code + * NullPointerException}. + */ + static V safeRemove(Map map, Object key) { + checkNotNull(map); + try { + return map.remove(key); + } catch (ClassCastException | NullPointerException e) { + return null; + } + } + + /** An admittedly inefficient implementation of {@link Map#containsKey}. */ + static boolean containsKeyImpl(Map map, Object key) { + return Iterators.contains(keyIterator(map.entrySet().iterator()), key); + } + + /** An implementation of {@link Map#containsValue}. */ + static boolean containsValueImpl(Map map, Object value) { + return Iterators.contains(valueIterator(map.entrySet().iterator()), value); + } + + /** + * Implements {@code Collection.contains} safely for forwarding collections of map entries. If + * {@code o} is an instance of {@code Entry}, it is wrapped using {@link #unmodifiableEntry} to + * protect against a possible nefarious equals method. + * + *

Note that {@code c} is the backing (delegate) collection, rather than the forwarding + * collection. + * + * @param c the delegate (unwrapped) collection of map entries + * @param o the object that might be contained in {@code c} + * @return {@code true} if {@code c} contains {@code o} + */ + static boolean containsEntryImpl( + Collection> c, Object o) { + if (!(o instanceof Entry)) { + return false; + } + return c.contains(unmodifiableEntry((Entry) o)); + } + + /** + * Implements {@code Collection.remove} safely for forwarding collections of map entries. If + * {@code o} is an instance of {@code Entry}, it is wrapped using {@link #unmodifiableEntry} to + * protect against a possible nefarious equals method. + * + *

Note that {@code c} is backing (delegate) collection, rather than the forwarding collection. + * + * @param c the delegate (unwrapped) collection of map entries + * @param o the object to remove from {@code c} + * @return {@code true} if {@code c} was changed + */ + static boolean removeEntryImpl( + Collection> c, Object o) { + if (!(o instanceof Entry)) { + return false; + } + return c.remove(unmodifiableEntry((Entry) o)); + } + + /** An implementation of {@link Map#equals}. */ + static boolean equalsImpl(Map map, Object object) { + if (map == object) { + return true; + } else if (object instanceof Map) { + Map o = (Map) object; + return map.entrySet().equals(o.entrySet()); + } + return false; + } + + /** An implementation of {@link Map#toString}. */ + static String toStringImpl(Map map) { + StringBuilder sb = Collections2.newStringBuilderForCollection(map.size()).append('{'); + boolean first = true; + for (Entry entry : map.entrySet()) { + if (!first) { + sb.append(", "); + } + first = false; + sb.append(entry.getKey()).append('=').append(entry.getValue()); + } + return sb.append('}').toString(); + } + + /** An implementation of {@link Map#putAll}. */ + static void putAllImpl( + Map self, Map map) { + for (Entry entry : map.entrySet()) { + self.put(entry.getKey(), entry.getValue()); + } + } + + static class KeySet extends Sets.ImprovedAbstractSet { + final Map map; + + KeySet(Map map) { + this.map = checkNotNull(map); + } + + Map map() { + return map; + } + + @Override + public Iterator iterator() { + return keyIterator(map().entrySet().iterator()); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + // avoids entry allocation for those maps that allocate entries on iteration + map.forEach((k, v) -> action.accept(k)); + } + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean isEmpty() { + return map().isEmpty(); + } + + @Override + public boolean contains(Object o) { + return map().containsKey(o); + } + + @Override + public boolean remove(Object o) { + if (contains(o)) { + map().remove(o); + return true; + } + return false; + } + + @Override + public void clear() { + map().clear(); + } + } + + static K keyOrNull(Entry entry) { + return (entry == null) ? null : entry.getKey(); + } + + static V valueOrNull(Entry entry) { + return (entry == null) ? null : entry.getValue(); + } + + static class SortedKeySet extends KeySet + implements SortedSet { + SortedKeySet(SortedMap map) { + super(map); + } + + @Override + SortedMap map() { + return (SortedMap) super.map(); + } + + @Override + public Comparator comparator() { + return map().comparator(); + } + + @Override + public SortedSet subSet(@ParametricNullness K fromElement, @ParametricNullness K toElement) { + return new SortedKeySet<>(map().subMap(fromElement, toElement)); + } + + @Override + public SortedSet headSet(@ParametricNullness K toElement) { + return new SortedKeySet<>(map().headMap(toElement)); + } + + @Override + public SortedSet tailSet(@ParametricNullness K fromElement) { + return new SortedKeySet<>(map().tailMap(fromElement)); + } + + @Override + @ParametricNullness + public K first() { + return map().firstKey(); + } + + @Override + @ParametricNullness + public K last() { + return map().lastKey(); + } + } + + @GwtIncompatible // NavigableMap + static class NavigableKeySet extends SortedKeySet + implements NavigableSet { + NavigableKeySet(NavigableMap map) { + super(map); + } + + @Override + NavigableMap map() { + return (NavigableMap) map; + } + + @Override + public K lower(@ParametricNullness K e) { + return map().lowerKey(e); + } + + @Override + public K floor(@ParametricNullness K e) { + return map().floorKey(e); + } + + @Override + public K ceiling(@ParametricNullness K e) { + return map().ceilingKey(e); + } + + @Override + public K higher(@ParametricNullness K e) { + return map().higherKey(e); + } + + @Override + public K pollFirst() { + return keyOrNull(map().pollFirstEntry()); + } + + @Override + public K pollLast() { + return keyOrNull(map().pollLastEntry()); + } + + @Override + public NavigableSet descendingSet() { + return map().descendingKeySet(); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public NavigableSet subSet( + @ParametricNullness K fromElement, + boolean fromInclusive, + @ParametricNullness K toElement, + boolean toInclusive) { + return map().subMap(fromElement, fromInclusive, toElement, toInclusive).navigableKeySet(); + } + + @Override + public SortedSet subSet(@ParametricNullness K fromElement, @ParametricNullness K toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet headSet(@ParametricNullness K toElement, boolean inclusive) { + return map().headMap(toElement, inclusive).navigableKeySet(); + } + + @Override + public SortedSet headSet(@ParametricNullness K toElement) { + return headSet(toElement, false); + } + + @Override + public NavigableSet tailSet(@ParametricNullness K fromElement, boolean inclusive) { + return map().tailMap(fromElement, inclusive).navigableKeySet(); + } + + @Override + public SortedSet tailSet(@ParametricNullness K fromElement) { + return tailSet(fromElement, true); + } + } + + static class Values extends AbstractCollection { + final Map map; + + Values(Map map) { + this.map = checkNotNull(map); + } + + final Map map() { + return map; + } + + @Override + public Iterator iterator() { + return valueIterator(map().entrySet().iterator()); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + // avoids allocation of entries for those maps that generate fresh entries on iteration + map.forEach((k, v) -> action.accept(v)); + } + + @Override + public boolean remove(Object o) { + try { + return super.remove(o); + } catch (UnsupportedOperationException e) { + for (Entry entry : map().entrySet()) { + if (Objects.equal(o, entry.getValue())) { + map().remove(entry.getKey()); + return true; + } + } + return false; + } + } + + @Override + public boolean removeAll(Collection c) { + try { + return super.removeAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + Set toRemove = Sets.newHashSet(); + for (Entry entry : map().entrySet()) { + if (c.contains(entry.getValue())) { + toRemove.add(entry.getKey()); + } + } + return map().keySet().removeAll(toRemove); + } + } + + @Override + public boolean retainAll(Collection c) { + try { + return super.retainAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + Set toRetain = Sets.newHashSet(); + for (Entry entry : map().entrySet()) { + if (c.contains(entry.getValue())) { + toRetain.add(entry.getKey()); + } + } + return map().keySet().retainAll(toRetain); + } + } + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean isEmpty() { + return map().isEmpty(); + } + + @Override + public boolean contains(Object o) { + return map().containsValue(o); + } + + @Override + public void clear() { + map().clear(); + } + } + + abstract static class EntrySet + extends Sets.ImprovedAbstractSet> { + abstract Map map(); + + @Override + public int size() { + return map().size(); + } + + @Override + public void clear() { + map().clear(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + Object key = entry.getKey(); + V value = Maps.safeGet(map(), key); + return Objects.equal(value, entry.getValue()) && (value != null || map().containsKey(key)); + } + return false; + } + + @Override + public boolean isEmpty() { + return map().isEmpty(); + } + + @Override + public boolean remove(Object o) { + /* + * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our + * nullness checker. + */ + if (contains(o) && o instanceof Entry) { + Entry entry = (Entry) o; + return map().keySet().remove(entry.getKey()); + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + try { + return super.removeAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + // if the iterators don't support remove + return Sets.removeAllImpl(this, c.iterator()); + } + } + + @Override + public boolean retainAll(Collection c) { + try { + return super.retainAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + // if the iterators don't support remove + Set keys = Sets.newHashSetWithExpectedSize(c.size()); + for (Object o : c) { + /* + * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our + * nullness checker. + */ + if (contains(o) && o instanceof Entry) { + Entry entry = (Entry) o; + keys.add(entry.getKey()); + } + } + return map().keySet().retainAll(keys); + } + } + } + + @GwtIncompatible // NavigableMap + abstract static class DescendingMap + extends ForwardingMap implements NavigableMap { + + abstract NavigableMap forward(); + + @Override + protected final Map delegate() { + return forward(); + } + + private transient Comparator comparator; + + @SuppressWarnings("unchecked") + @Override + public Comparator comparator() { + Comparator result = comparator; + if (result == null) { + Comparator forwardCmp = forward().comparator(); + if (forwardCmp == null) { + forwardCmp = (Comparator) Ordering.natural(); + } + result = comparator = reverse(forwardCmp); + } + return result; + } + + // If we inline this, we get a javac error. + private static Ordering reverse(Comparator forward) { + return Ordering.from(forward).reverse(); + } + + @Override + @ParametricNullness + public K firstKey() { + return forward().lastKey(); + } + + @Override + @ParametricNullness + public K lastKey() { + return forward().firstKey(); + } + + @Override + public Entry lowerEntry(@ParametricNullness K key) { + return forward().higherEntry(key); + } + + @Override + public K lowerKey(@ParametricNullness K key) { + return forward().higherKey(key); + } + + @Override + public Entry floorEntry(@ParametricNullness K key) { + return forward().ceilingEntry(key); + } + + @Override + public K floorKey(@ParametricNullness K key) { + return forward().ceilingKey(key); + } + + @Override + public Entry ceilingEntry(@ParametricNullness K key) { + return forward().floorEntry(key); + } + + @Override + public K ceilingKey(@ParametricNullness K key) { + return forward().floorKey(key); + } + + @Override + public Entry higherEntry(@ParametricNullness K key) { + return forward().lowerEntry(key); + } + + @Override + public K higherKey(@ParametricNullness K key) { + return forward().lowerKey(key); + } + + @Override + public Entry firstEntry() { + return forward().lastEntry(); + } + + @Override + public Entry lastEntry() { + return forward().firstEntry(); + } + + @Override + public Entry pollFirstEntry() { + return forward().pollLastEntry(); + } + + @Override + public Entry pollLastEntry() { + return forward().pollFirstEntry(); + } + + @Override + public NavigableMap descendingMap() { + return forward(); + } + + private transient Set> entrySet; + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + abstract Iterator> entryIterator(); + + Set> createEntrySet() { + class EntrySetImpl extends EntrySet { + @Override + Map map() { + return DescendingMap.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + } + return new EntrySetImpl(); + } + + @Override + public Set keySet() { + return navigableKeySet(); + } + + private transient NavigableSet navigableKeySet; + + @Override + public NavigableSet navigableKeySet() { + NavigableSet result = navigableKeySet; + return (result == null) ? navigableKeySet = new NavigableKeySet<>(this) : result; + } + + @Override + public NavigableSet descendingKeySet() { + return forward().navigableKeySet(); + } + + @Override + public NavigableMap subMap( + @ParametricNullness K fromKey, + boolean fromInclusive, + @ParametricNullness K toKey, + boolean toInclusive) { + return forward().subMap(toKey, toInclusive, fromKey, fromInclusive).descendingMap(); + } + + @Override + public SortedMap subMap(@ParametricNullness K fromKey, @ParametricNullness K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap headMap(@ParametricNullness K toKey, boolean inclusive) { + return forward().tailMap(toKey, inclusive).descendingMap(); + } + + @Override + public SortedMap headMap(@ParametricNullness K toKey) { + return headMap(toKey, false); + } + + @Override + public NavigableMap tailMap(@ParametricNullness K fromKey, boolean inclusive) { + return forward().headMap(fromKey, inclusive).descendingMap(); + } + + @Override + public SortedMap tailMap(@ParametricNullness K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public Collection values() { + return new Values<>(this); + } + + @Override + public String toString() { + return standardToString(); + } + } + + /** Returns a map from the ith element of list to i. */ + static ImmutableMap indexMap(Collection list) { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(list.size()); + int i = 0; + for (E e : list) { + builder.put(e, i++); + } + return builder.buildOrThrow(); + } + + /** + * Returns a view of the portion of {@code map} whose keys are contained by {@code range}. + * + *

This method delegates to the appropriate methods of {@link NavigableMap} (namely {@link + * NavigableMap#subMap(Object, boolean, Object, boolean) subMap()}, {@link + * NavigableMap#tailMap(Object, boolean) tailMap()}, and {@link NavigableMap#headMap(Object, + * boolean) headMap()}) to actually construct the view. Consult these methods for a full + * description of the returned view's behavior. + * + *

Warning: {@code Range}s always represent a range of values using the values' natural + * ordering. {@code NavigableMap} on the other hand can specify a custom ordering via a {@link + * Comparator}, which can violate the natural ordering. Using this method (or in general using + * {@code Range}) with unnaturally-ordered maps can lead to unexpected and undefined behavior. + * + * @since 20.0 + */ + @GwtIncompatible // NavigableMap + public static , V extends Object> NavigableMap subMap( + NavigableMap map, Range range) { + if (map.comparator() != null + && map.comparator() != Ordering.natural() + && range.hasLowerBound() + && range.hasUpperBound()) { + checkArgument( + map.comparator().compare(range.lowerEndpoint(), range.upperEndpoint()) <= 0, + "map is using a custom comparator which is inconsistent with the natural ordering."); + } + if (range.hasLowerBound() && range.hasUpperBound()) { + return map.subMap( + range.lowerEndpoint(), + range.lowerBoundType() == BoundType.CLOSED, + range.upperEndpoint(), + range.upperBoundType() == BoundType.CLOSED); + } else if (range.hasLowerBound()) { + return map.tailMap(range.lowerEndpoint(), range.lowerBoundType() == BoundType.CLOSED); + } else if (range.hasUpperBound()) { + return map.headMap(range.upperEndpoint(), range.upperBoundType() == BoundType.CLOSED); + } + return checkNotNull(map); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MinMaxPriorityQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MinMaxPriorityQueue.java new file mode 100644 index 0000000000000..64ef849c52992 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MinMaxPriorityQueue.java @@ -0,0 +1,961 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.math.IntMath; + +import java.util.AbstractQueue; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.Queue; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * A double-ended priority queue, which provides constant-time access to both its least element and + * its greatest element, as determined by the queue's specified comparator. If no comparator is + * given at creation time, the natural order of elements is used. If no maximum size is given at + * creation time, the queue is unbounded. + * + *

Usage example: + * + *

{@code
+ * MinMaxPriorityQueue users = MinMaxPriorityQueue.orderedBy(userComparator)
+ *     .maximumSize(1000)
+ *     .create();
+ * }
+ * + *

As a {@link Queue} it functions exactly as a {@link PriorityQueue}: its head element -- the + * implicit target of the methods {@link #peek()}, {@link #poll()} and {@link #remove()} -- is + * defined as the least element in the queue according to the queue's comparator. But unlike + * a regular priority queue, the methods {@link #peekLast}, {@link #pollLast} and {@link + * #removeLast} are also provided, to act on the greatest element in the queue instead. + * + *

A min-max priority queue can be configured with a maximum size. If so, each time the size of + * the queue exceeds that value, the queue automatically removes its greatest element according to + * its comparator (which might be the element that was just added). This is different from + * conventional bounded queues, which either block or reject new elements when full. + * + *

This implementation is based on the min-max heap developed by Atkinson, et al. + * Unlike many other double-ended priority queues, it stores elements in a single array, as compact + * as the traditional heap data structure used in {@link PriorityQueue}. + * + *

This class is not thread-safe, and does not accept null elements. + * + *

Performance notes: + * + *

    + *
  • If you only access one end of the queue, and do use a maximum size, this class will perform + * significantly worse than a {@code PriorityQueue} with manual eviction above the maximum + * size. In many cases {@link Ordering#leastOf} may work for your use case with significantly + * improved (and asymptotically superior) performance. + *
  • The retrieval operations {@link #peek}, {@link #peekFirst}, {@link #peekLast}, {@link + * #element}, and {@link #size} are constant-time. + *
  • The enqueuing and dequeuing operations ({@link #offer}, {@link #add}, and all the forms of + * {@link #poll} and {@link #remove()}) run in {@code O(log n) time}. + *
  • The {@link #remove(Object)} and {@link #contains} operations require linear ({@code O(n)}) + * time. + *
  • If you only access one end of the queue, and don't use a maximum size, this class is + * functionally equivalent to {@link PriorityQueue}, but significantly slower. + *
+ * + * @author Sverre Sundsdal + * @author Torbjorn Gannholm + * @since 8.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class MinMaxPriorityQueue extends AbstractQueue { + + /** + * Creates a new min-max priority queue with default settings: natural order, no maximum size, no + * initial contents, and an initial expected size of 11. + */ + public static > MinMaxPriorityQueue create() { + return new Builder>(Ordering.natural()).create(); + } + + /** + * Creates a new min-max priority queue using natural order, no maximum size, and initially + * containing the given elements. + */ + public static > MinMaxPriorityQueue create( + Iterable initialContents) { + return new Builder(Ordering.natural()).create(initialContents); + } + + /** + * Creates and returns a new builder, configured to build {@code MinMaxPriorityQueue} instances + * that use {@code comparator} to determine the least and greatest elements. + */ + /* + * TODO(cpovirk): Change to Comparator to permit Comparator<@Nullable ...> and + * Comparator? What we have here matches the immutable collections, but those also + * expose a public Builder constructor that accepts "? super." So maybe we should do *that* + * instead. + */ + public static Builder orderedBy(Comparator comparator) { + return new Builder<>(comparator); + } + + /** + * Creates and returns a new builder, configured to build {@code MinMaxPriorityQueue} instances + * sized appropriately to hold {@code expectedSize} elements. + */ + public static Builder expectedSize(int expectedSize) { + return new Builder(Ordering.natural()).expectedSize(expectedSize); + } + + /** + * Creates and returns a new builder, configured to build {@code MinMaxPriorityQueue} instances + * that are limited to {@code maximumSize} elements. Each time a queue grows beyond this bound, it + * immediately removes its greatest element (according to its comparator), which might be the + * element that was just added. + */ + public static Builder maximumSize(int maximumSize) { + return new Builder(Ordering.natural()).maximumSize(maximumSize); + } + + /** + * The builder class used in creation of min-max priority queues. Instead of constructing one + * directly, use {@link MinMaxPriorityQueue#orderedBy(Comparator)}, {@link + * MinMaxPriorityQueue#expectedSize(int)} or {@link MinMaxPriorityQueue#maximumSize(int)}. + * + * @param the upper bound on the eventual type that can be produced by this builder (for + * example, a {@code Builder} can produce a {@code Queue} or {@code + * Queue} but not a {@code Queue}). + * @since 8.0 + */ + public static final class Builder { + /* + * TODO(kevinb): when the dust settles, see if we still need this or can + * just default to DEFAULT_CAPACITY. + */ + private static final int UNSET_EXPECTED_SIZE = -1; + + private final Comparator comparator; + private int expectedSize = UNSET_EXPECTED_SIZE; + private int maximumSize = Integer.MAX_VALUE; + + private Builder(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + /** + * Configures this builder to build min-max priority queues with an initial expected size of + * {@code expectedSize}. + */ + public Builder expectedSize(int expectedSize) { + checkArgument(expectedSize >= 0); + this.expectedSize = expectedSize; + return this; + } + + /** + * Configures this builder to build {@code MinMaxPriorityQueue} instances that are limited to + * {@code maximumSize} elements. Each time a queue grows beyond this bound, it immediately + * removes its greatest element (according to its comparator), which might be the element that + * was just added. + */ + public Builder maximumSize(int maximumSize) { + checkArgument(maximumSize > 0); + this.maximumSize = maximumSize; + return this; + } + + /** + * Builds a new min-max priority queue using the previously specified options, and having no + * initial contents. + */ + public MinMaxPriorityQueue create() { + return create(Collections.emptySet()); + } + + /** + * Builds a new min-max priority queue using the previously specified options, and having the + * given initial elements. + */ + public MinMaxPriorityQueue create(Iterable initialContents) { + MinMaxPriorityQueue queue = + new MinMaxPriorityQueue<>( + this, initialQueueSize(expectedSize, maximumSize, initialContents)); + for (T element : initialContents) { + queue.offer(element); + } + return queue; + } + + @SuppressWarnings("unchecked") // safe "contravariant cast" + private Ordering ordering() { + return Ordering.from((Comparator) comparator); + } + } + + private final Heap minHeap; + private final Heap maxHeap; + @VisibleForTesting final int maximumSize; + private Object[] queue; + private int size; + private int modCount; + + private MinMaxPriorityQueue(Builder builder, int queueSize) { + Ordering ordering = builder.ordering(); + this.minHeap = new Heap(ordering); + this.maxHeap = new Heap(ordering.reverse()); + minHeap.otherHeap = maxHeap; + maxHeap.otherHeap = minHeap; + + this.maximumSize = builder.maximumSize; + // TODO(kevinb): pad? + this.queue = new Object[queueSize]; + } + + @Override + public int size() { + return size; + } + + /** + * Adds the given element to this queue. If this queue has a maximum size, after adding {@code + * element} the queue will automatically evict its greatest element (according to its comparator), + * which may be {@code element} itself. + * + * @return {@code true} always + */ + @Override + public boolean add(E element) { + offer(element); + return true; + } + + @Override + public boolean addAll(Collection newElements) { + boolean modified = false; + for (E element : newElements) { + offer(element); + modified = true; + } + return modified; + } + + /** + * Adds the given element to this queue. If this queue has a maximum size, after adding {@code + * element} the queue will automatically evict its greatest element (according to its comparator), + * which may be {@code element} itself. + */ + @Override + public boolean offer(E element) { + checkNotNull(element); + modCount++; + int insertIndex = size++; + + growIfNeeded(); + + // Adds the element to the end of the heap and bubbles it up to the correct + // position. + heapForIndex(insertIndex).bubbleUp(insertIndex, element); + return size <= maximumSize || pollLast() != element; + } + + @Override + public E poll() { + return isEmpty() ? null : removeAndGet(0); + } + + @SuppressWarnings("unchecked") // we must carefully only allow Es to get in + E elementData(int index) { + /* + * requireNonNull is safe as long as we're careful to call this method only with populated + * indexes. + */ + return (E) requireNonNull(queue[index]); + } + + @Override + public E peek() { + return isEmpty() ? null : elementData(0); + } + + /** Returns the index of the max element. */ + private int getMaxElementIndex() { + switch (size) { + case 1: + return 0; // The lone element in the queue is the maximum. + case 2: + return 1; // The lone element in the maxHeap is the maximum. + default: + // The max element must sit on the first level of the maxHeap. It is + // actually the *lesser* of the two from the maxHeap's perspective. + return (maxHeap.compareElements(1, 2) <= 0) ? 1 : 2; + } + } + + /** + * Removes and returns the least element of this queue, or returns {@code null} if the queue is + * empty. + */ + public E pollFirst() { + return poll(); + } + + /** + * Removes and returns the least element of this queue. + * + * @throws NoSuchElementException if the queue is empty + */ + public E removeFirst() { + return remove(); + } + + /** + * Retrieves, but does not remove, the least element of this queue, or returns {@code null} if the + * queue is empty. + */ + public E peekFirst() { + return peek(); + } + + /** + * Removes and returns the greatest element of this queue, or returns {@code null} if the queue is + * empty. + */ + public E pollLast() { + return isEmpty() ? null : removeAndGet(getMaxElementIndex()); + } + + /** + * Removes and returns the greatest element of this queue. + * + * @throws NoSuchElementException if the queue is empty + */ + public E removeLast() { + if (isEmpty()) { + throw new NoSuchElementException(); + } + return removeAndGet(getMaxElementIndex()); + } + + /** + * Retrieves, but does not remove, the greatest element of this queue, or returns {@code null} if + * the queue is empty. + */ + public E peekLast() { + return isEmpty() ? null : elementData(getMaxElementIndex()); + } + + /** + * Removes the element at position {@code index}. + * + *

Normally this method leaves the elements at up to {@code index - 1}, inclusive, untouched. + * Under these circumstances, it returns {@code null}. + * + *

Occasionally, in order to maintain the heap invariant, it must swap a later element of the + * list with one before {@code index}. Under these circumstances it returns a pair of elements as + * a {@link MoveDesc}. The first one is the element that was previously at the end of the heap and + * is now at some position before {@code index}. The second element is the one that was swapped + * down to replace the element at {@code index}. This fact is used by iterator.remove so as to + * visit elements during a traversal once and only once. + */ + @VisibleForTesting + MoveDesc removeAt(int index) { + checkPositionIndex(index, size); + modCount++; + size--; + if (size == index) { + queue[size] = null; + return null; + } + E actualLastElement = elementData(size); + int lastElementAt = heapForIndex(size).swapWithConceptuallyLastElement(actualLastElement); + if (lastElementAt == index) { + // 'actualLastElement' is now at 'lastElementAt', and the element that was at 'lastElementAt' + // is now at the end of queue. If that's the element we wanted to remove in the first place, + // don't try to (incorrectly) trickle it. Instead, just delete it and we're done. + queue[size] = null; + return null; + } + E toTrickle = elementData(size); + queue[size] = null; + MoveDesc changes = fillHole(index, toTrickle); + if (lastElementAt < index) { + // Last element is moved to before index, swapped with trickled element. + if (changes == null) { + // The trickled element is still after index. + return new MoveDesc<>(actualLastElement, toTrickle); + } else { + // The trickled element is back before index, but the replaced element + // has now been moved after index. + return new MoveDesc<>(actualLastElement, changes.replaced); + } + } + // Trickled element was after index to begin with, no adjustment needed. + return changes; + } + + private MoveDesc fillHole(int index, E toTrickle) { + Heap heap = heapForIndex(index); + // We consider elementData(index) a "hole", and we want to fill it + // with the last element of the heap, toTrickle. + // Since the last element of the heap is from the bottom level, we + // optimistically fill index position with elements from lower levels, + // moving the hole down. In most cases this reduces the number of + // comparisons with toTrickle, but in some cases we will need to bubble it + // all the way up again. + int vacated = heap.fillHoleAt(index); + // Try to see if toTrickle can be bubbled up min levels. + int bubbledTo = heap.bubbleUpAlternatingLevels(vacated, toTrickle); + if (bubbledTo == vacated) { + // Could not bubble toTrickle up min levels, try moving + // it from min level to max level (or max to min level) and bubble up + // there. + return heap.tryCrossOverAndBubbleUp(index, vacated, toTrickle); + } else { + return (bubbledTo < index) ? new MoveDesc(toTrickle, elementData(index)) : null; + } + } + + // Returned from removeAt() to iterator.remove() + static class MoveDesc { + final E toTrickle; + final E replaced; + + MoveDesc(E toTrickle, E replaced) { + this.toTrickle = toTrickle; + this.replaced = replaced; + } + } + + /** Removes and returns the value at {@code index}. */ + private E removeAndGet(int index) { + E value = elementData(index); + removeAt(index); + return value; + } + + private Heap heapForIndex(int i) { + return isEvenLevel(i) ? minHeap : maxHeap; + } + + private static final int EVEN_POWERS_OF_TWO = 0x55555555; + private static final int ODD_POWERS_OF_TWO = 0xaaaaaaaa; + + @VisibleForTesting + static boolean isEvenLevel(int index) { + int oneBased = ~~(index + 1); // for GWT + checkState(oneBased > 0, "negative index"); + return (oneBased & EVEN_POWERS_OF_TWO) > (oneBased & ODD_POWERS_OF_TWO); + } + + /** + * Returns {@code true} if the MinMax heap structure holds. This is only used in testing. + * + *

TODO(kevinb): move to the test class? + */ + @VisibleForTesting + boolean isIntact() { + for (int i = 1; i < size; i++) { + if (!heapForIndex(i).verifyIndex(i)) { + return false; + } + } + return true; + } + + /** + * Each instance of MinMaxPriorityQueue encapsulates two instances of Heap: a min-heap and a + * max-heap. Conceptually, these might each have their own array for storage, but for efficiency's + * sake they are stored interleaved on alternate heap levels in the same array (MMPQ.queue). + */ + private class Heap { + final Ordering ordering; + + @SuppressWarnings("nullness:initialization.field.uninitialized") + Heap otherHeap; // always initialized immediately after construction + + Heap(Ordering ordering) { + this.ordering = ordering; + } + + int compareElements(int a, int b) { + return ordering.compare(elementData(a), elementData(b)); + } + + /** + * Tries to move {@code toTrickle} from a min to a max level and bubble up there. If it moved + * before {@code removeIndex} this method returns a pair as described in {@link #removeAt}. + */ + MoveDesc tryCrossOverAndBubbleUp(int removeIndex, int vacated, E toTrickle) { + int crossOver = crossOver(vacated, toTrickle); + if (crossOver == vacated) { + return null; + } + // Successfully crossed over from min to max. + // Bubble up max levels. + E parent; + // If toTrickle is moved up to a parent of removeIndex, the parent is + // placed in removeIndex position. We must return that to the iterator so + // that it knows to skip it. + if (crossOver < removeIndex) { + // We crossed over to the parent level in crossOver, so the parent + // has already been moved. + parent = elementData(removeIndex); + } else { + parent = elementData(getParentIndex(removeIndex)); + } + // bubble it up the opposite heap + if (otherHeap.bubbleUpAlternatingLevels(crossOver, toTrickle) < removeIndex) { + return new MoveDesc<>(toTrickle, parent); + } else { + return null; + } + } + + /** Bubbles a value from {@code index} up the appropriate heap if required. */ + void bubbleUp(int index, E x) { + int crossOver = crossOverUp(index, x); + + Heap heap; + if (crossOver == index) { + heap = this; + } else { + index = crossOver; + heap = otherHeap; + } + heap.bubbleUpAlternatingLevels(index, x); + } + + /** + * Bubbles a value from {@code index} up the levels of this heap, and returns the index the + * element ended up at. + */ + int bubbleUpAlternatingLevels(int index, E x) { + while (index > 2) { + int grandParentIndex = getGrandparentIndex(index); + E e = elementData(grandParentIndex); + if (ordering.compare(e, x) <= 0) { + break; + } + queue[index] = e; + index = grandParentIndex; + } + queue[index] = x; + return index; + } + + /** + * Returns the index of minimum value between {@code index} and {@code index + len}, or {@code + * -1} if {@code index} is greater than {@code size}. + */ + int findMin(int index, int len) { + if (index >= size) { + return -1; + } + checkState(index > 0); + int limit = Math.min(index, size - len) + len; + int minIndex = index; + for (int i = index + 1; i < limit; i++) { + if (compareElements(i, minIndex) < 0) { + minIndex = i; + } + } + return minIndex; + } + + /** Returns the minimum child or {@code -1} if no child exists. */ + int findMinChild(int index) { + return findMin(getLeftChildIndex(index), 2); + } + + /** Returns the minimum grand child or -1 if no grand child exists. */ + int findMinGrandChild(int index) { + int leftChildIndex = getLeftChildIndex(index); + if (leftChildIndex < 0) { + return -1; + } + return findMin(getLeftChildIndex(leftChildIndex), 4); + } + + /** + * Moves an element one level up from a min level to a max level (or vice versa). Returns the + * new position of the element. + */ + int crossOverUp(int index, E x) { + if (index == 0) { + queue[0] = x; + return 0; + } + int parentIndex = getParentIndex(index); + E parentElement = elementData(parentIndex); + if (parentIndex != 0) { + /* + * This is a guard for the case of the childless aunt node. Since the end of the array is + * actually the middle of the heap, a smaller childless aunt node can become a child of x + * when we bubble up alternate levels, violating the invariant. + */ + int grandparentIndex = getParentIndex(parentIndex); + int auntIndex = getRightChildIndex(grandparentIndex); + if (auntIndex != parentIndex && getLeftChildIndex(auntIndex) >= size) { + E auntElement = elementData(auntIndex); + if (ordering.compare(auntElement, parentElement) < 0) { + parentIndex = auntIndex; + parentElement = auntElement; + } + } + } + if (ordering.compare(parentElement, x) < 0) { + queue[index] = parentElement; + queue[parentIndex] = x; + return parentIndex; + } + queue[index] = x; + return index; + } + + // About the term "aunt node": it's better to leave gender out of it, but for this the English + // language has nothing for us. Except for the whimsical neologism "pibling" (!) which we + // obviously could not expect to increase anyone's understanding of the code. + + /** + * Swap {@code actualLastElement} with the conceptually correct last element of the heap. + * Returns the index that {@code actualLastElement} now resides in. + * + *

Since the last element of the array is actually in the middle of the sorted structure, a + * childless aunt node could be smaller, which would corrupt the invariant if this element + * becomes the new parent of the aunt node. In that case, we first switch the last element with + * its aunt node, before returning. + */ + int swapWithConceptuallyLastElement(E actualLastElement) { + int parentIndex = getParentIndex(size); + if (parentIndex != 0) { + int grandparentIndex = getParentIndex(parentIndex); + int auntIndex = getRightChildIndex(grandparentIndex); + if (auntIndex != parentIndex && getLeftChildIndex(auntIndex) >= size) { + E auntElement = elementData(auntIndex); + if (ordering.compare(auntElement, actualLastElement) < 0) { + queue[auntIndex] = actualLastElement; + queue[size] = auntElement; + return auntIndex; + } + } + } + return size; + } + + /** + * Crosses an element over to the opposite heap by moving it one level down (or up if there are + * no elements below it). + * + *

Returns the new position of the element. + */ + int crossOver(int index, E x) { + int minChildIndex = findMinChild(index); + // TODO(kevinb): split the && into two if's and move crossOverUp so it's + // only called when there's no child. + if ((minChildIndex > 0) && (ordering.compare(elementData(minChildIndex), x) < 0)) { + queue[index] = elementData(minChildIndex); + queue[minChildIndex] = x; + return minChildIndex; + } + return crossOverUp(index, x); + } + + /** + * Fills the hole at {@code index} by moving in the least of its grandchildren to this position, + * then recursively filling the new hole created. + * + * @return the position of the new hole (where the lowest grandchild moved from, that had no + * grandchild to replace it) + */ + int fillHoleAt(int index) { + int minGrandchildIndex; + while ((minGrandchildIndex = findMinGrandChild(index)) > 0) { + queue[index] = elementData(minGrandchildIndex); + index = minGrandchildIndex; + } + return index; + } + + private boolean verifyIndex(int i) { + if ((getLeftChildIndex(i) < size) && (compareElements(i, getLeftChildIndex(i)) > 0)) { + return false; + } + if ((getRightChildIndex(i) < size) && (compareElements(i, getRightChildIndex(i)) > 0)) { + return false; + } + if ((i > 0) && (compareElements(i, getParentIndex(i)) > 0)) { + return false; + } + if ((i > 2) && (compareElements(getGrandparentIndex(i), i) > 0)) { + return false; + } + return true; + } + + // These would be static if inner classes could have static members. + + private int getLeftChildIndex(int i) { + return i * 2 + 1; + } + + private int getRightChildIndex(int i) { + return i * 2 + 2; + } + + private int getParentIndex(int i) { + return (i - 1) / 2; + } + + private int getGrandparentIndex(int i) { + return getParentIndex(getParentIndex(i)); // (i - 3) / 4 + } + } + + /** + * Iterates the elements of the queue in no particular order. + * + *

If the underlying queue is modified during iteration an exception will be thrown. + */ + private class QueueIterator implements Iterator { + private int cursor = -1; + private int nextCursor = -1; + private int expectedModCount = modCount; + // The same element is not allowed in both forgetMeNot and skipMe, but duplicates are allowed in + // either of them, up to the same multiplicity as the queue. + private Queue forgetMeNot; + private List skipMe; + private E lastFromForgetMeNot; + private boolean canRemove; + + @Override + public boolean hasNext() { + checkModCount(); + nextNotInSkipMe(cursor + 1); + return (nextCursor < size()) || ((forgetMeNot != null) && !forgetMeNot.isEmpty()); + } + + @Override + public E next() { + checkModCount(); + nextNotInSkipMe(cursor + 1); + if (nextCursor < size()) { + cursor = nextCursor; + canRemove = true; + return elementData(cursor); + } else if (forgetMeNot != null) { + cursor = size(); + lastFromForgetMeNot = forgetMeNot.poll(); + if (lastFromForgetMeNot != null) { + canRemove = true; + return lastFromForgetMeNot; + } + } + throw new NoSuchElementException("iterator moved past last element in queue."); + } + + @Override + public void remove() { + checkRemove(canRemove); + checkModCount(); + canRemove = false; + expectedModCount++; + if (cursor < size()) { + MoveDesc moved = removeAt(cursor); + if (moved != null) { + // Either both are null or neither is, but we check both to satisfy the nullness checker. + if (forgetMeNot == null || skipMe == null) { + forgetMeNot = new ArrayDeque<>(); + skipMe = new ArrayList<>(3); + } + if (!foundAndRemovedExactReference(skipMe, moved.toTrickle)) { + forgetMeNot.add(moved.toTrickle); + } + if (!foundAndRemovedExactReference(forgetMeNot, moved.replaced)) { + skipMe.add(moved.replaced); + } + } + cursor--; + nextCursor--; + } else { // we must have set lastFromForgetMeNot in next() + checkState(removeExact(requireNonNull(lastFromForgetMeNot))); + lastFromForgetMeNot = null; + } + } + + /** Returns true if an exact reference (==) was found and removed from the supplied iterable. */ + private boolean foundAndRemovedExactReference(Iterable elements, E target) { + for (Iterator it = elements.iterator(); it.hasNext(); ) { + E element = it.next(); + if (element == target) { + it.remove(); + return true; + } + } + return false; + } + + /** Removes only this exact instance, not others that are equals() */ + private boolean removeExact(Object target) { + for (int i = 0; i < size; i++) { + if (queue[i] == target) { + removeAt(i); + return true; + } + } + return false; + } + + private void checkModCount() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + /** + * Advances nextCursor to the index of the first element after {@code c} that is not in {@code + * skipMe} and returns {@code size()} if there is no such element. + */ + private void nextNotInSkipMe(int c) { + if (nextCursor < c) { + if (skipMe != null) { + while (c < size() && foundAndRemovedExactReference(skipMe, elementData(c))) { + c++; + } + } + nextCursor = c; + } + } + } + + /** + * Returns an iterator over the elements contained in this collection, in no particular + * order. + * + *

The iterator is fail-fast: If the MinMaxPriorityQueue is modified at any time after + * the iterator is created, in any way except through the iterator's own remove method, the + * iterator will generally throw a {@link ConcurrentModificationException}. Thus, in the face of + * concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, + * non-deterministic behavior at an undetermined time in the future. + * + *

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally + * speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent + * modification. Fail-fast iterators throw {@code ConcurrentModificationException} on a + * best-effort basis. Therefore, it would be wrong to write a program that depended on this + * exception for its correctness: the fail-fast behavior of iterators should be used only to + * detect bugs. + * + * @return an iterator over the elements contained in this collection + */ + @Override + public Iterator iterator() { + return new QueueIterator(); + } + + @Override + public void clear() { + for (int i = 0; i < size; i++) { + queue[i] = null; + } + size = 0; + } + + @Override + @J2ktIncompatible // Incompatible return type change. Use inherited (unoptimized) implementation + public Object[] toArray() { + Object[] copyTo = new Object[size]; + System.arraycopy(queue, 0, copyTo, 0, size); + return copyTo; + } + + /** + * Returns the comparator used to order the elements in this queue. Obeys the general contract of + * {@link PriorityQueue#comparator}, but returns {@link Ordering#natural} instead of {@code null} + * to indicate natural ordering. + */ + public Comparator comparator() { + return minHeap.ordering; + } + + @VisibleForTesting + int capacity() { + return queue.length; + } + + // Size/capacity-related methods + + private static final int DEFAULT_CAPACITY = 11; + + @VisibleForTesting + static int initialQueueSize( + int configuredExpectedSize, int maximumSize, Iterable initialContents) { + // Start with what they said, if they said it, otherwise DEFAULT_CAPACITY + int result = + (configuredExpectedSize == Builder.UNSET_EXPECTED_SIZE) + ? DEFAULT_CAPACITY + : configuredExpectedSize; + + // Enlarge to contain initial contents + if (initialContents instanceof Collection) { + int initialSize = ((Collection) initialContents).size(); + result = Math.max(result, initialSize); + } + + // Now cap it at maxSize + 1 + return capAtMaximumSize(result, maximumSize); + } + + private void growIfNeeded() { + if (size > queue.length) { + int newCapacity = calculateNewCapacity(); + Object[] newQueue = new Object[newCapacity]; + System.arraycopy(queue, 0, newQueue, 0, queue.length); + queue = newQueue; + } + } + + /** Returns ~2x the old capacity if small; ~1.5x otherwise. */ + private int calculateNewCapacity() { + int oldCapacity = queue.length; + int newCapacity = + (oldCapacity < 64) ? (oldCapacity + 1) * 2 : IntMath.checkedMultiply(oldCapacity / 2, 3); + return capAtMaximumSize(newCapacity, maximumSize); + } + + /** There's no reason for the queueSize to ever be more than maxSize + 1 */ + private static int capAtMaximumSize(int queueSize, int maximumSize) { + return Math.min(queueSize - 1, maximumSize) + 1; // don't overflow + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MoreCollectors.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MoreCollectors.java new file mode 100644 index 0000000000000..ab795a5561116 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MoreCollectors.java @@ -0,0 +1,175 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.stream.Collector; + +import static java.util.Collections.emptyList; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Collectors not present in {@code java.util.stream.Collectors} that are not otherwise associated + * with a {@code org.apache.iotdb.google.common} type. + * + * @author Louis Wasserman + * @since 21.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class MoreCollectors { + + /* + * TODO(lowasser): figure out if we can convert this to a concurrent AtomicReference-based + * collector without breaking j2cl? + */ + private static final Collector> TO_OPTIONAL = + Collector.of( + ToOptionalState::new, + ToOptionalState::add, + ToOptionalState::combine, + ToOptionalState::getOptional, + Collector.Characteristics.UNORDERED); + + /** + * A collector that converts a stream of zero or one elements to an {@code Optional}. + * + * @throws IllegalArgumentException if the stream consists of two or more elements. + * @throws NullPointerException if any element in the stream is {@code null}. + * @return {@code Optional.of(onlyElement)} if the stream has exactly one element (must not be + * {@code null}) and returns {@code Optional.empty()} if it has none. + */ + @SuppressWarnings("unchecked") + public static Collector> toOptional() { + return (Collector) TO_OPTIONAL; + } + + private static final Object NULL_PLACEHOLDER = new Object(); + + private static final Collector ONLY_ELEMENT = + Collector.of( + ToOptionalState::new, + (state, o) -> state.add((o == null) ? NULL_PLACEHOLDER : o), + ToOptionalState::combine, + state -> { + Object result = state.getElement(); + return (result == NULL_PLACEHOLDER) ? null : result; + }, + Collector.Characteristics.UNORDERED); + + /** + * A collector that takes a stream containing exactly one element and returns that element. The + * returned collector throws an {@code IllegalArgumentException} if the stream consists of two or + * more elements, and a {@code NoSuchElementException} if the stream is empty. + */ + @SuppressWarnings("unchecked") + public static Collector onlyElement() { + return (Collector) ONLY_ELEMENT; + } + + /** + * This atrocity is here to let us report several of the elements in the stream if there were more + * than one, not just two. + */ + private static final class ToOptionalState { + static final int MAX_EXTRAS = 4; + + Object element; + List extras; + + ToOptionalState() { + element = null; + extras = emptyList(); + } + + IllegalArgumentException multiples(boolean overflow) { + StringBuilder sb = + new StringBuilder().append("expected one element but was: <").append(element); + for (Object o : extras) { + sb.append(", ").append(o); + } + if (overflow) { + sb.append(", ..."); + } + sb.append('>'); + throw new IllegalArgumentException(sb.toString()); + } + + void add(Object o) { + checkNotNull(o); + if (element == null) { + this.element = o; + } else if (extras.isEmpty()) { + // Replace immutable empty list with mutable list. + extras = new ArrayList<>(MAX_EXTRAS); + extras.add(o); + } else if (extras.size() < MAX_EXTRAS) { + extras.add(o); + } else { + throw multiples(true); + } + } + + ToOptionalState combine(ToOptionalState other) { + if (element == null) { + return other; + } else if (other.element == null) { + return this; + } else { + if (extras.isEmpty()) { + // Replace immutable empty list with mutable list. + extras = new ArrayList<>(); + } + extras.add(other.element); + extras.addAll(other.extras); + if (extras.size() > MAX_EXTRAS) { + extras.subList(MAX_EXTRAS, extras.size()).clear(); + throw multiples(true); + } + return this; + } + } + + Optional getOptional() { + if (extras.isEmpty()) { + return Optional.ofNullable(element); + } else { + throw multiples(false); + } + } + + Object getElement() { + if (element == null) { + throw new NoSuchElementException(); + } else if (extras.isEmpty()) { + return element; + } else { + throw multiples(false); + } + } + } + + private MoreCollectors() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multimap.java new file mode 100644 index 0000000000000..62afa53b57365 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multimap.java @@ -0,0 +1,382 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A collection that maps keys to values, similar to {@link Map}, but in which each key may be + * associated with multiple values. You can visualize the contents of a multimap either as a + * map from keys to nonempty collections of values: + * + *
    + *
  • a → 1, 2 + *
  • b → 3 + *
+ * + * ... or as a single "flattened" collection of key-value pairs: + * + *
    + *
  • a → 1 + *
  • a → 2 + *
  • b → 3 + *
+ * + *

Important: although the first interpretation resembles how most multimaps are + * implemented, the design of the {@code Multimap} API is based on the second form. + * So, using the multimap shown above as an example, the {@link #size} is {@code 3}, not {@code 2}, + * and the {@link #values} collection is {@code [1, 2, 3]}, not {@code [[1, 2], [3]]}. For those + * times when the first style is more useful, use the multimap's {@link #asMap} view (or create a + * {@code Map>} in the first place). + * + *

Example

+ * + *

The following code: + * + *

{@code
+ * ListMultimap multimap = ArrayListMultimap.create();
+ * for (President pres : US_PRESIDENTS_IN_ORDER) {
+ *   multimap.put(pres.firstName(), pres.lastName());
+ * }
+ * for (String firstName : multimap.keySet()) {
+ *   List lastNames = multimap.get(firstName);
+ *   out.println(firstName + ": " + lastNames);
+ * }
+ * }
+ * + * ... produces output such as: + * + *
{@code
+ * Zachary: [Taylor]
+ * John: [Adams, Adams, Tyler, Kennedy]  // Remember, Quincy!
+ * George: [Washington, Bush, Bush]
+ * Grover: [Cleveland, Cleveland]        // Two, non-consecutive terms, rep'ing NJ!
+ * ...
+ * }
+ * + *

Views

+ * + *

Much of the power of the multimap API comes from the view collections it provides. + * These always reflect the latest state of the multimap itself. When they support modification, the + * changes are write-through (they automatically update the backing multimap). These view + * collections are: + * + *

    + *
  • {@link #asMap}, mentioned above + *
  • {@link #keys}, {@link #keySet}, {@link #values}, {@link #entries}, which are similar to the + * corresponding view collections of {@link Map} + *
  • and, notably, even the collection returned by {@link #get get(key)} is an active view of + * the values corresponding to {@code key} + *
+ * + *

The collections returned by the {@link #replaceValues replaceValues} and {@link #removeAll + * removeAll} methods, which contain values that have just been removed from the multimap, are + * naturally not views. + * + *

Subinterfaces

+ * + *

Instead of using the {@code Multimap} interface directly, prefer the subinterfaces {@link + * ListMultimap} and {@link SetMultimap}. These take their names from the fact that the collections + * they return from {@code get} behave like (and, of course, implement) {@link List} and {@link + * Set}, respectively. + * + *

For example, the "presidents" code snippet above used a {@code ListMultimap}; if it had used a + * {@code SetMultimap} instead, two presidents would have vanished, and last names might or might + * not appear in chronological order. + * + *

Warning: instances of type {@code Multimap} may not implement {@link Object#equals} in + * the way you expect. Multimaps containing the same key-value pairs, even in the same order, may or + * may not be equal and may or may not have the same {@code hashCode}. The recommended subinterfaces + * provide much stronger guarantees. + * + *

Comparison to a map of collections

+ * + *

Multimaps are commonly used in places where a {@code Map>} would otherwise + * have appeared. The differences include: + * + *

    + *
  • There is no need to populate an empty collection before adding an entry with {@link #put + * put}. + *
  • {@code get} never returns {@code null}, only an empty collection. + *
  • A key is contained in the multimap if and only if it maps to at least one value. Any + * operation that causes a key to have zero associated values has the effect of + * removing that key from the multimap. + *
  • The total entry count is available as {@link #size}. + *
  • Many complex operations become easier; for example, {@code + * Collections.min(multimap.values())} finds the smallest value across all keys. + *
+ * + *

Implementations

+ * + *
    + *
  • {@link ImmutableListMultimap} + *
  • {@link ImmutableSetMultimap} + *
  • Configure your own mutable multimap with {@link MultimapBuilder} + *
  • {@link LinkedListMultimap} (for one unusual kind of mutable {@code Multimap}) + *
+ * + * Guava contains a number of other multimap implementations, such as {@link ArrayListMultimap}. In + * new code, we recommend using {@link MultimapBuilder} instead: It provides better control of how + * keys and values are stored. + * + *

Other Notes

+ * + *

As with {@code Map}, the behavior of a {@code Multimap} is not specified if key objects + * already present in the multimap change in a manner that affects {@code equals} comparisons. Use + * caution if mutable objects are used as keys in a {@code Multimap}. + * + *

All methods that modify the multimap are optional. The view collections returned by the + * multimap may or may not be modifiable. Any modification method that is not supported will throw + * {@link UnsupportedOperationException}. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface Multimap { + // Query Operations + + /** + * Returns the number of key-value pairs in this multimap. + * + *

Note: this method does not return the number of distinct keys in the multimap, + * which is given by {@code keySet().size()} or {@code asMap().size()}. See the opening section of + * the {@link Multimap} class documentation for clarification. + */ + int size(); + + /** + * Returns {@code true} if this multimap contains no key-value pairs. Equivalent to {@code size() + * == 0}, but can in some cases be more efficient. + */ + boolean isEmpty(); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair with the key {@code + * key}. + */ + boolean containsKey(Object key); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair with the value + * {@code value}. + */ + boolean containsValue(Object value); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair with the key {@code + * key} and the value {@code value}. + */ + boolean containsEntry(Object key, Object value); + + // Modification Operations + + /** + * Stores a key-value pair in this multimap. + * + *

Some multimap implementations allow duplicate key-value pairs, in which case {@code put} + * always adds a new key-value pair and increases the multimap size by 1. Other implementations + * prohibit duplicates, and storing a key-value pair that's already in the multimap has no effect. + * + * @return {@code true} if the method increased the size of the multimap, or {@code false} if the + * multimap already contained the key-value pair and doesn't allow duplicates + */ + boolean put(@ParametricNullness K key, @ParametricNullness V value); + + /** + * Removes a single key-value pair with the key {@code key} and the value {@code value} from this + * multimap, if such exists. If multiple key-value pairs in the multimap fit this description, + * which one is removed is unspecified. + * + * @return {@code true} if the multimap changed + */ + boolean remove(Object key, Object value); + + // Bulk Operations + + /** + * Stores a key-value pair in this multimap for each of {@code values}, all using the same key, + * {@code key}. Equivalent to (but expected to be more efficient than): + * + *

{@code
+   * for (V value : values) {
+   *   put(key, value);
+   * }
+   * }
+ * + *

In particular, this is a no-op if {@code values} is empty. + * + * @return {@code true} if the multimap changed + */ + boolean putAll(@ParametricNullness K key, Iterable values); + + /** + * Stores all key-value pairs of {@code multimap} in this multimap, in the order returned by + * {@code multimap.entries()}. + * + * @return {@code true} if the multimap changed + */ + boolean putAll(Multimap multimap); + + /** + * Stores a collection of values with the same key, replacing any existing values for that key. + * + *

If {@code values} is empty, this is equivalent to {@link #removeAll(Object) removeAll(key)}. + * + * @return the collection of replaced values, or an empty collection if no values were previously + * associated with the key. The collection may be modifiable, but updating it will have + * no effect on the multimap. + */ + Collection replaceValues(@ParametricNullness K key, Iterable values); + + /** + * Removes all values associated with the key {@code key}. + * + *

Once this method returns, {@code key} will not be mapped to any values, so it will not + * appear in {@link #keySet()}, {@link #asMap()}, or any other views. + * + * @return the values that were removed (possibly empty). The returned collection may be + * modifiable, but updating it will have no effect on the multimap. + */ + Collection removeAll(Object key); + + /** Removes all key-value pairs from the multimap, leaving it {@linkplain #isEmpty empty}. */ + void clear(); + + // Views + + /** + * Returns a view collection of the values associated with {@code key} in this multimap, if any. + * Note that when {@code containsKey(key)} is false, this returns an empty collection, not {@code + * null}. + * + *

Changes to the returned collection will update the underlying multimap, and vice versa. + */ + Collection get(@ParametricNullness K key); + + /** + * Returns a view collection of all distinct keys contained in this multimap. Note that the + * key set contains a key if and only if this multimap maps that key to at least one value. + * + *

Changes to the returned set will update the underlying multimap, and vice versa. However, + * adding to the returned set is not possible. + */ + Set keySet(); + + /** + * Returns a view collection containing the key from each key-value pair in this multimap, + * without collapsing duplicates. This collection has the same size as this multimap, and + * {@code keys().count(k) == get(k).size()} for all {@code k}. + * + *

Changes to the returned multiset will update the underlying multimap, and vice versa. + * However, adding to the returned collection is not possible. + */ + Multiset keys(); + + /** + * Returns a view collection containing the value from each key-value pair contained in + * this multimap, without collapsing duplicates (so {@code values().size() == size()}). + * + *

Changes to the returned collection will update the underlying multimap, and vice versa. + * However, adding to the returned collection is not possible. + */ + Collection values(); + + /** + * Returns a view collection of all key-value pairs contained in this multimap, as {@link Entry} + * instances. + * + *

Changes to the returned collection or the entries it contains will update the underlying + * multimap, and vice versa. However, adding to the returned collection is not possible. + */ + Collection> entries(); + + /** + * Performs the given action for all key-value pairs contained in this multimap. If an ordering is + * specified by the {@code Multimap} implementation, actions will be performed in the order of + * iteration of {@link #entries()}. Exceptions thrown by the action are relayed to the caller. + * + *

To loop over all keys and their associated value collections, write {@code + * Multimaps.asMap(multimap).forEach((key, valueCollection) -> action())}. + * + * @since 21.0 + */ + default void forEach(BiConsumer action) { + checkNotNull(action); + entries().forEach(entry -> action.accept(entry.getKey(), entry.getValue())); + } + + /** + * Returns a view of this multimap as a {@code Map} from each distinct key to the nonempty + * collection of that key's associated values. Note that {@code this.asMap().get(k)} is equivalent + * to {@code this.get(k)} only when {@code k} is a key contained in the multimap; otherwise it + * returns {@code null} as opposed to an empty collection. + * + *

Changes to the returned map or the collections that serve as its values will update the + * underlying multimap, and vice versa. The map does not support {@code put} or {@code putAll}, + * nor do its entries support {@link Entry#setValue setValue}. + */ + Map> asMap(); + + // Comparison and hashing + + /** + * Compares the specified object with this multimap for equality. Two multimaps are equal when + * their map views, as returned by {@link #asMap}, are also equal. + * + *

In general, two multimaps with identical key-value mappings may or may not be equal, + * depending on the implementation. For example, two {@link SetMultimap} instances with the same + * key-value mappings are equal, but equality of two {@link ListMultimap} instances depends on the + * ordering of the values for each key. + * + *

A non-empty {@link SetMultimap} cannot be equal to a non-empty {@link ListMultimap}, since + * their {@link #asMap} views contain unequal collections as values. However, any two empty + * multimaps are equal, because they both have empty {@link #asMap} views. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code for this multimap. + * + *

The hash code of a multimap is defined as the hash code of the map view, as returned by + * {@link Multimap#asMap}. + * + *

In general, two multimaps with identical key-value mappings may or may not have the same + * hash codes, depending on the implementation. For example, two {@link SetMultimap} instances + * with the same key-value mappings will have the same {@code hashCode}, but the {@code hashCode} + * of {@link ListMultimap} instances depends on the ordering of the values for each key. + */ + @Override + int hashCode(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MultimapBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MultimapBuilder.java new file mode 100644 index 0000000000000..e34cc9c44886e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MultimapBuilder.java @@ -0,0 +1,476 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * An immutable builder for {@link Multimap} instances, letting you independently select the desired + * behaviors (for example, ordering) of the backing map and value-collections. Example: + * + *

{@code
+ * ListMultimap errorsByUser =
+ *     MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ * SortedSetMultimap methodsForName =
+ *     MultimapBuilder.treeKeys().treeSetValues(this::compareMethods).build();
+ * }
+ * + *

{@code MultimapBuilder} instances are immutable. Invoking a configuration method has no effect + * on the receiving instance; you must store and use the new builder instance it returns instead. + * + *

The generated multimaps are serializable if the key and value types are serializable, unless + * stated otherwise in one of the configuration methods. + * + * @author Louis Wasserman + * @param An upper bound on the key type of the generated multimap. + * @param An upper bound on the value type of the generated multimap. + * @since 16.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class MultimapBuilder { + /* + * Leaving K and V as upper bounds rather than the actual key and value types allows type + * parameters to be left implicit more often. CacheBuilder uses the same technique. + */ + + private MultimapBuilder() {} + + private static final int DEFAULT_EXPECTED_KEYS = 8; + + /** Uses a hash table to map keys to value collections. */ + public static MultimapBuilderWithKeys hashKeys() { + return hashKeys(DEFAULT_EXPECTED_KEYS); + } + + /** + * Uses a hash table to map keys to value collections, initialized to expect the specified number + * of keys. + * + * @throws IllegalArgumentException if {@code expectedKeys < 0} + */ + public static MultimapBuilderWithKeys hashKeys(int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new MultimapBuilderWithKeys() { + @Override + Map> createMap() { + return Platform.newHashMapWithExpectedSize(expectedKeys); + } + }; + } + + /** + * Uses a hash table to map keys to value collections. + * + *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link + * Multimap#asMap()} will iterate through the keys in the order that they were first added to the + * multimap, save that if all values associated with a key are removed and then the key is added + * back into the multimap, that key will come last in the key iteration order. + */ + public static MultimapBuilderWithKeys linkedHashKeys() { + return linkedHashKeys(DEFAULT_EXPECTED_KEYS); + } + + /** + * Uses an hash table to map keys to value collections, initialized to expect the specified number + * of keys. + * + *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link + * Multimap#asMap()} will iterate through the keys in the order that they were first added to the + * multimap, save that if all values associated with a key are removed and then the key is added + * back into the multimap, that key will come last in the key iteration order. + */ + public static MultimapBuilderWithKeys linkedHashKeys(int expectedKeys) { + checkNonnegative(expectedKeys, "expectedKeys"); + return new MultimapBuilderWithKeys() { + @Override + Map> createMap() { + return Platform.newLinkedHashMapWithExpectedSize(expectedKeys); + } + }; + } + + /** + * Uses a naturally-ordered {@link TreeMap} to map keys to value collections. + * + *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link + * Multimap#asMap()} will iterate through the keys in sorted order. + * + *

For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be + * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be + * cast to a {@link java.util.SortedMap}. + */ + @SuppressWarnings("rawtypes") + public static MultimapBuilderWithKeys treeKeys() { + return treeKeys(Ordering.natural()); + } + + /** + * Uses a {@link TreeMap} sorted by the specified comparator to map keys to value collections. + * + *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link + * Multimap#asMap()} will iterate through the keys in sorted order. + * + *

For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be + * safely cast to a {@link java.util.SortedSet}, and the {@link Multimap#asMap()} can safely be + * cast to a {@link java.util.SortedMap}. + * + *

Multimaps generated by the resulting builder will not be serializable if {@code comparator} + * is not serializable. + */ + public static MultimapBuilderWithKeys treeKeys( + Comparator comparator) { + checkNotNull(comparator); + return new MultimapBuilderWithKeys() { + @Override + Map> createMap() { + return new TreeMap<>(comparator); + } + }; + } + + /** + * Uses an {@link EnumMap} to map keys to value collections. + * + * @since 16.0 + */ + public static > MultimapBuilderWithKeys enumKeys(Class keyClass) { + checkNotNull(keyClass); + return new MultimapBuilderWithKeys() { + @SuppressWarnings("unchecked") + @Override + Map> createMap() { + // K must actually be K0, since enums are effectively final + // (their subclasses are inaccessible) + return (Map>) new EnumMap>(keyClass); + } + }; + } + + private static final class ArrayListSupplier + implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + ArrayListSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + @Override + public List get() { + return new ArrayList<>(expectedValuesPerKey); + } + } + + private enum LinkedListSupplier implements Supplier> { + INSTANCE; + + public static Supplier> instance() { + // Each call generates a fresh LinkedList, which can serve as a List for any V. + @SuppressWarnings({"rawtypes", "unchecked"}) + Supplier> result = (Supplier) INSTANCE; + return result; + } + + @Override + public List get() { + return new LinkedList<>(); + } + } + + private static final class HashSetSupplier + implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + HashSetSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + @Override + public Set get() { + return Platform.newHashSetWithExpectedSize(expectedValuesPerKey); + } + } + + private static final class LinkedHashSetSupplier + implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + LinkedHashSetSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + @Override + public Set get() { + return Platform.newLinkedHashSetWithExpectedSize(expectedValuesPerKey); + } + } + + private static final class TreeSetSupplier + implements Supplier>, Serializable { + private final Comparator comparator; + + TreeSetSupplier(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public SortedSet get() { + return new TreeSet<>(comparator); + } + } + + private static final class EnumSetSupplier> + implements Supplier>, Serializable { + private final Class clazz; + + EnumSetSupplier(Class clazz) { + this.clazz = checkNotNull(clazz); + } + + @Override + public Set get() { + return EnumSet.noneOf(clazz); + } + } + + /** + * An intermediate stage in a {@link MultimapBuilder} in which the key-value collection map + * implementation has been specified, but the value collection implementation has not. + * + * @param The upper bound on the key type of the generated multimap. + * @since 16.0 + */ + public abstract static class MultimapBuilderWithKeys { + + private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2; + + MultimapBuilderWithKeys() {} + + abstract Map> createMap(); + + /** Uses an {@link ArrayList} to store value collections. */ + public ListMultimapBuilder arrayListValues() { + return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY); + } + + /** + * Uses an {@link ArrayList} to store value collections, initialized to expect the specified + * number of values per key. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} + */ + public ListMultimapBuilder arrayListValues(int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new ListMultimapBuilder() { + @Override + public ListMultimap build() { + return Multimaps.newListMultimap( + MultimapBuilderWithKeys.this.createMap(), + new ArrayListSupplier(expectedValuesPerKey)); + } + }; + } + + /** Uses a {@link LinkedList} to store value collections. */ + public ListMultimapBuilder linkedListValues() { + return new ListMultimapBuilder() { + @Override + public ListMultimap build() { + return Multimaps.newListMultimap( + MultimapBuilderWithKeys.this.createMap(), LinkedListSupplier.instance()); + } + }; + } + + /** Uses a hash-based {@code Set} to store value collections. */ + public SetMultimapBuilder hashSetValues() { + return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); + } + + /** + * Uses a hash-based {@code Set} to store value collections, initialized to expect the specified + * number of values per key. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} + */ + public SetMultimapBuilder hashSetValues(int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new SetMultimapBuilder() { + @Override + public SetMultimap build() { + return Multimaps.newSetMultimap( + MultimapBuilderWithKeys.this.createMap(), + new HashSetSupplier(expectedValuesPerKey)); + } + }; + } + + /** Uses an insertion-ordered hash-based {@code Set} to store value collections. */ + public SetMultimapBuilder linkedHashSetValues() { + return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); + } + + /** + * Uses an insertion-ordered hash-based {@code Set} to store value collections, initialized to + * expect the specified number of values per key. + * + * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} + */ + public SetMultimapBuilder linkedHashSetValues(int expectedValuesPerKey) { + checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new SetMultimapBuilder() { + @Override + public SetMultimap build() { + return Multimaps.newSetMultimap( + MultimapBuilderWithKeys.this.createMap(), + new LinkedHashSetSupplier(expectedValuesPerKey)); + } + }; + } + + /** Uses a naturally-ordered {@link TreeSet} to store value collections. */ + @SuppressWarnings("rawtypes") + public SortedSetMultimapBuilder treeSetValues() { + return treeSetValues(Ordering.natural()); + } + + /** + * Uses a {@link TreeSet} ordered by the specified comparator to store value collections. + * + *

Multimaps generated by the resulting builder will not be serializable if {@code + * comparator} is not serializable. + */ + public SortedSetMultimapBuilder treeSetValues( + Comparator comparator) { + checkNotNull(comparator, "comparator"); + return new SortedSetMultimapBuilder() { + @Override + public SortedSetMultimap build() { + return Multimaps.newSortedSetMultimap( + MultimapBuilderWithKeys.this.createMap(), new TreeSetSupplier(comparator)); + } + }; + } + + /** Uses an {@link EnumSet} to store value collections. */ + public > SetMultimapBuilder enumSetValues(Class valueClass) { + checkNotNull(valueClass, "valueClass"); + return new SetMultimapBuilder() { + @Override + public SetMultimap build() { + // V must actually be V0, since enums are effectively final + // (their subclasses are inaccessible) + @SuppressWarnings({"unchecked", "rawtypes"}) + Supplier> factory = (Supplier) new EnumSetSupplier(valueClass); + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), factory); + } + }; + } + } + + /** Returns a new, empty {@code Multimap} with the specified implementation. */ + public abstract Multimap build(); + + /** + * Returns a {@code Multimap} with the specified implementation, initialized with the entries of + * {@code multimap}. + */ + public Multimap build( + Multimap multimap) { + Multimap result = build(); + result.putAll(multimap); + return result; + } + + /** + * A specialization of {@link MultimapBuilder} that generates {@link ListMultimap} instances. + * + * @since 16.0 + */ + public abstract static class ListMultimapBuilder + extends MultimapBuilder { + ListMultimapBuilder() {} + + @Override + public abstract ListMultimap build(); + + @Override + public ListMultimap build( + Multimap multimap) { + return (ListMultimap) super.build(multimap); + } + } + + /** + * A specialization of {@link MultimapBuilder} that generates {@link SetMultimap} instances. + * + * @since 16.0 + */ + public abstract static class SetMultimapBuilder + extends MultimapBuilder { + SetMultimapBuilder() {} + + @Override + public abstract SetMultimap build(); + + @Override + public SetMultimap build( + Multimap multimap) { + return (SetMultimap) super.build(multimap); + } + } + + /** + * A specialization of {@link MultimapBuilder} that generates {@link SortedSetMultimap} instances. + * + * @since 16.0 + */ + public abstract static class SortedSetMultimapBuilder + extends SetMultimapBuilder { + SortedSetMultimapBuilder() {} + + @Override + public abstract SortedSetMultimap build(); + + @Override + public SortedSetMultimap build( + Multimap multimap) { + return (SortedSetMultimap) super.build(multimap); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multimaps.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multimaps.java new file mode 100644 index 0000000000000..bd1d3834b6533 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multimaps.java @@ -0,0 +1,2214 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.collect.Maps.EntryTransformer; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collector; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * Provides static methods acting on or generating a {@code Multimap}. + * + *

See the Guava User Guide article on {@code + * Multimaps}. + * + * @author Jared Levy + * @author Robert Konigsberg + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Multimaps { + private Multimaps() {} + + /** + * Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the + * specified supplier. The keys and values of the entries are the result of applying the provided + * mapping functions to the input elements, accumulated in the encounter order of the stream. + * + *

Example: + * + *

{@code
+   * static final ListMultimap FIRST_LETTER_MULTIMAP =
+   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
+   *         .collect(
+   *             toMultimap(
+   *                  str -> str.charAt(0),
+   *                  str -> str.substring(1),
+   *                  MultimapBuilder.treeKeys().arrayListValues()::build));
+   *
+   * // is equivalent to
+   *
+   * static final ListMultimap FIRST_LETTER_MULTIMAP;
+   *
+   * static {
+   *     FIRST_LETTER_MULTIMAP = MultimapBuilder.treeKeys().arrayListValues().build();
+   *     FIRST_LETTER_MULTIMAP.put('b', "anana");
+   *     FIRST_LETTER_MULTIMAP.put('a', "pple");
+   *     FIRST_LETTER_MULTIMAP.put('a', "sparagus");
+   *     FIRST_LETTER_MULTIMAP.put('c', "arrot");
+   *     FIRST_LETTER_MULTIMAP.put('c', "herry");
+   * }
+   * }
+ * + *

To collect to an {@link ImmutableMultimap}, use either {@link + * ImmutableSetMultimap#toImmutableSetMultimap} or {@link + * ImmutableListMultimap#toImmutableListMultimap}. + * + * @since 21.0 + */ + public static > + Collector toMultimap( + java.util.function.Function keyFunction, + java.util.function.Function valueFunction, + java.util.function.Supplier multimapSupplier) { + return CollectCollectors.toMultimap(keyFunction, valueFunction, multimapSupplier); + } + + /** + * Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the + * specified supplier. Each input element is mapped to a key and a stream of values, each of which + * are put into the resulting {@code Multimap}, in the encounter order of the stream and the + * encounter order of the streams of values. + * + *

Example: + * + *

{@code
+   * static final ListMultimap FIRST_LETTER_MULTIMAP =
+   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
+   *         .collect(
+   *             flatteningToMultimap(
+   *                  str -> str.charAt(0),
+   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c),
+   *                  MultimapBuilder.linkedHashKeys().arrayListValues()::build));
+   *
+   * // is equivalent to
+   *
+   * static final ListMultimap FIRST_LETTER_MULTIMAP;
+   *
+   * static {
+   *     FIRST_LETTER_MULTIMAP = MultimapBuilder.linkedHashKeys().arrayListValues().build();
+   *     FIRST_LETTER_MULTIMAP.putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'));
+   *     FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('p', 'p', 'l', 'e'));
+   *     FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'));
+   *     FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'));
+   *     FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'));
+   * }
+   * }
+ * + * @since 21.0 + */ + public static > + Collector flatteningToMultimap( + java.util.function.Function keyFunction, + java.util.function.Function> valueFunction, + java.util.function.Supplier multimapSupplier) { + return CollectCollectors.flatteningToMultimap(keyFunction, valueFunction, multimapSupplier); + } + + /** + * Creates a new {@code Multimap} backed by {@code map}, whose internal value collections are + * generated by {@code factory}. + * + *

Warning: do not use this method when the collections returned by {@code factory} + * implement either {@link List} or {@code Set}! Use the more specific method {@link + * #newListMultimap}, {@link #newSetMultimap} or {@link #newSortedSetMultimap} instead, to avoid + * very surprising behavior from {@link Multimap#equals}. + * + *

The {@code factory}-generated and {@code map} classes determine the multimap iteration + * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code + * toString} methods for the multimap and its returned views. However, the multimap's {@code get} + * method returns instances of a different class than {@code factory.get()} does. + * + *

The multimap is serializable if {@code map}, {@code factory}, the collections generated by + * {@code factory}, and the multimap contents are all serializable. + * + *

The multimap is not threadsafe when any concurrent operations update the multimap, even if + * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will + * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link + * #synchronizedMultimap}. + * + *

Call this method only when the simpler methods {@link ArrayListMultimap#create()}, {@link + * HashMultimap#create()}, {@link LinkedHashMultimap#create()}, {@link + * LinkedListMultimap#create()}, {@link TreeMultimap#create()}, and {@link + * TreeMultimap#create(Comparator, Comparator)} won't suffice. + * + *

Note: the multimap assumes complete ownership over of {@code map} and the collections + * returned by {@code factory}. Those objects should not be manually updated and they should not + * use soft, weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding values + * @param factory supplier of new, empty collections that will each hold all values for a given + * key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static Multimap newMultimap( + Map> map, final Supplier> factory) { + return new CustomMultimap<>(map, factory); + } + + private static class CustomMultimap + extends AbstractMapBasedMultimap { + transient Supplier> factory; + + CustomMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + Set createKeySet() { + return createMaybeNavigableKeySet(); + } + + @Override + Map> createAsMap() { + return createMaybeNavigableAsMap(); + } + + @Override + protected Collection createCollection() { + return factory.get(); + } + + @Override + Collection unmodifiableCollectionSubclass(Collection collection) { + if (collection instanceof NavigableSet) { + return Sets.unmodifiableNavigableSet((NavigableSet) collection); + } else if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set) collection); + } else if (collection instanceof List) { + return Collections.unmodifiableList((List) collection); + } else { + return Collections.unmodifiableCollection(collection); + } + } + + @Override + Collection wrapCollection(@ParametricNullness K key, Collection collection) { + if (collection instanceof List) { + return wrapList(key, (List) collection, null); + } else if (collection instanceof NavigableSet) { + return new WrappedNavigableSet(key, (NavigableSet) collection, null); + } else if (collection instanceof SortedSet) { + return new WrappedSortedSet(key, (SortedSet) collection, null); + } else if (collection instanceof Set) { + return new WrappedSet(key, (Set) collection); + } else { + return new WrappedCollection(key, collection, null); + } + } + + // can't use Serialization writeMultimap and populateMultimap methods since + // there's no way to generate the empty backing map. + + /** + * @serialData the factory and the backing map + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible // java serialization not supported + @J2ktIncompatible + private static final long serialVersionUID = 0; + } + + /** + * Creates a new {@code ListMultimap} that uses the provided map and factory. It can generate a + * multimap based on arbitrary {@link Map} and {@link List} classes. + * + *

The {@code factory}-generated and {@code map} classes determine the multimap iteration + * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code + * toString} methods for the multimap and its returned views. The multimap's {@code get}, {@code + * removeAll}, and {@code replaceValues} methods return {@code RandomAccess} lists if the factory + * does. However, the multimap's {@code get} method returns instances of a different class than + * does {@code factory.get()}. + * + *

The multimap is serializable if {@code map}, {@code factory}, the lists generated by {@code + * factory}, and the multimap contents are all serializable. + * + *

The multimap is not threadsafe when any concurrent operations update the multimap, even if + * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will + * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link + * #synchronizedListMultimap}. + * + *

Call this method only when the simpler methods {@link ArrayListMultimap#create()} and {@link + * LinkedListMultimap#create()} won't suffice. + * + *

Note: the multimap assumes complete ownership over of {@code map} and the lists returned by + * {@code factory}. Those objects should not be manually updated, they should be empty when + * provided, and they should not use soft, weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding values + * @param factory supplier of new, empty lists that will each hold all values for a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static ListMultimap newListMultimap( + Map> map, final Supplier> factory) { + return new CustomListMultimap<>(map, factory); + } + + private static class CustomListMultimap + extends AbstractListMultimap { + transient Supplier> factory; + + CustomListMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + Set createKeySet() { + return createMaybeNavigableKeySet(); + } + + @Override + Map> createAsMap() { + return createMaybeNavigableAsMap(); + } + + @Override + protected List createCollection() { + return factory.get(); + } + + /** + * @serialData the factory and the backing map + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible // java serialization not supported + @J2ktIncompatible + private static final long serialVersionUID = 0; + } + + /** + * Creates a new {@code SetMultimap} that uses the provided map and factory. It can generate a + * multimap based on arbitrary {@link Map} and {@link Set} classes. + * + *

The {@code factory}-generated and {@code map} classes determine the multimap iteration + * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code + * toString} methods for the multimap and its returned views. However, the multimap's {@code get} + * method returns instances of a different class than {@code factory.get()} does. + * + *

The multimap is serializable if {@code map}, {@code factory}, the sets generated by {@code + * factory}, and the multimap contents are all serializable. + * + *

The multimap is not threadsafe when any concurrent operations update the multimap, even if + * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will + * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link + * #synchronizedSetMultimap}. + * + *

Call this method only when the simpler methods {@link HashMultimap#create()}, {@link + * LinkedHashMultimap#create()}, {@link TreeMultimap#create()}, and {@link + * TreeMultimap#create(Comparator, Comparator)} won't suffice. + * + *

Note: the multimap assumes complete ownership over of {@code map} and the sets returned by + * {@code factory}. Those objects should not be manually updated and they should not use soft, + * weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding values + * @param factory supplier of new, empty sets that will each hold all values for a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static SetMultimap newSetMultimap( + Map> map, final Supplier> factory) { + return new CustomSetMultimap<>(map, factory); + } + + private static class CustomSetMultimap + extends AbstractSetMultimap { + transient Supplier> factory; + + CustomSetMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + Set createKeySet() { + return createMaybeNavigableKeySet(); + } + + @Override + Map> createAsMap() { + return createMaybeNavigableAsMap(); + } + + @Override + protected Set createCollection() { + return factory.get(); + } + + @Override + Collection unmodifiableCollectionSubclass(Collection collection) { + if (collection instanceof NavigableSet) { + return Sets.unmodifiableNavigableSet((NavigableSet) collection); + } else if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } else { + return Collections.unmodifiableSet((Set) collection); + } + } + + @Override + Collection wrapCollection(@ParametricNullness K key, Collection collection) { + if (collection instanceof NavigableSet) { + return new WrappedNavigableSet(key, (NavigableSet) collection, null); + } else if (collection instanceof SortedSet) { + return new WrappedSortedSet(key, (SortedSet) collection, null); + } else { + return new WrappedSet(key, (Set) collection); + } + } + + /** + * @serialData the factory and the backing map + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible // not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; + } + + /** + * Creates a new {@code SortedSetMultimap} that uses the provided map and factory. It can generate + * a multimap based on arbitrary {@link Map} and {@link SortedSet} classes. + * + *

The {@code factory}-generated and {@code map} classes determine the multimap iteration + * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code + * toString} methods for the multimap and its returned views. However, the multimap's {@code get} + * method returns instances of a different class than {@code factory.get()} does. + * + *

The multimap is serializable if {@code map}, {@code factory}, the sets generated by {@code + * factory}, and the multimap contents are all serializable. + * + *

The multimap is not threadsafe when any concurrent operations update the multimap, even if + * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will + * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link + * #synchronizedSortedSetMultimap}. + * + *

Call this method only when the simpler methods {@link TreeMultimap#create()} and {@link + * TreeMultimap#create(Comparator, Comparator)} won't suffice. + * + *

Note: the multimap assumes complete ownership over of {@code map} and the sets returned by + * {@code factory}. Those objects should not be manually updated and they should not use soft, + * weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding values + * @param factory supplier of new, empty sorted sets that will each hold all values for a given + * key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static SortedSetMultimap newSortedSetMultimap( + Map> map, final Supplier> factory) { + return new CustomSortedSetMultimap<>(map, factory); + } + + private static class CustomSortedSetMultimap + extends AbstractSortedSetMultimap { + transient Supplier> factory; + transient Comparator valueComparator; + + CustomSortedSetMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + valueComparator = factory.get().comparator(); + } + + @Override + Set createKeySet() { + return createMaybeNavigableKeySet(); + } + + @Override + Map> createAsMap() { + return createMaybeNavigableAsMap(); + } + + @Override + protected SortedSet createCollection() { + return factory.get(); + } + + @Override + public Comparator valueComparator() { + return valueComparator; + } + + /** + * @serialData the factory and the backing map + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + valueComparator = factory.get().comparator(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + + @GwtIncompatible // not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; + } + + /** + * Copies each key-value mapping in {@code source} into {@code dest}, with its key and value + * reversed. + * + *

If {@code source} is an {@link ImmutableMultimap}, consider using {@link + * ImmutableMultimap#inverse} instead. + * + * @param source any multimap + * @param dest the multimap to copy into; usually empty + * @return {@code dest} + */ + public static > M invertFrom( + Multimap source, M dest) { + checkNotNull(dest); + for (Map.Entry entry : source.entries()) { + dest.put(entry.getValue(), entry.getKey()); + } + return dest; + } + + /** + * Returns a synchronized (thread-safe) multimap backed by the specified multimap. In order to + * guarantee serial access, it is critical that all access to the backing multimap is + * accomplished through the returned multimap. + * + *

It is imperative that the user manually synchronize on the returned multimap when accessing + * any of its collection views: + * + *

{@code
+   * Multimap multimap = Multimaps.synchronizedMultimap(
+   *     HashMultimap.create());
+   * ...
+   * Collection values = multimap.get(key);  // Needn't be in synchronized block
+   * ...
+   * synchronized (multimap) {  // Synchronizing on multimap, not values!
+   *   Iterator i = values.iterator(); // Must be in synchronized block
+   *   while (i.hasNext()) {
+   *     foo(i.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

Note that the generated multimap's {@link Multimap#removeAll} and {@link + * Multimap#replaceValues} methods return collections that aren't synchronized. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param multimap the multimap to be wrapped in a synchronized view + * @return a synchronized view of the specified multimap + */ + public static Multimap synchronizedMultimap( + Multimap multimap) { + return Synchronized.multimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified multimap. Query operations on the returned + * multimap "read through" to the specified multimap, and attempts to modify the returned + * multimap, either directly or through the multimap's views, result in an {@code + * UnsupportedOperationException}. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static Multimap unmodifiableMultimap( + Multimap delegate) { + if (delegate instanceof UnmodifiableMultimap || delegate instanceof ImmutableMultimap) { + return delegate; + } + return new UnmodifiableMultimap<>(delegate); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static Multimap unmodifiableMultimap(ImmutableMultimap delegate) { + return checkNotNull(delegate); + } + + private static class UnmodifiableMultimap + extends ForwardingMultimap implements Serializable { + final Multimap delegate; + transient Collection> entries; + transient Multiset keys; + transient Set keySet; + transient Collection values; + transient Map> map; + + UnmodifiableMultimap(final Multimap delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + protected Multimap delegate() { + return delegate; + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Map> asMap() { + Map> result = map; + if (result == null) { + result = + map = + Collections.unmodifiableMap( + Maps.transformValues( + delegate.asMap(), collection -> unmodifiableValueCollection(collection))); + } + return result; + } + + @Override + public Collection> entries() { + Collection> result = entries; + if (result == null) { + entries = result = unmodifiableEntries(delegate.entries()); + } + return result; + } + + @Override + public void forEach(BiConsumer consumer) { + delegate.forEach(checkNotNull(consumer)); + } + + @Override + public Collection get(@ParametricNullness K key) { + return unmodifiableValueCollection(delegate.get(key)); + } + + @Override + public Multiset keys() { + Multiset result = keys; + if (result == null) { + keys = result = Multisets.unmodifiableMultiset(delegate.keys()); + } + return result; + } + + @Override + public Set keySet() { + Set result = keySet; + if (result == null) { + keySet = result = Collections.unmodifiableSet(delegate.keySet()); + } + return result; + } + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection values() { + Collection result = values; + if (result == null) { + values = result = Collections.unmodifiableCollection(delegate.values()); + } + return result; + } + + private static final long serialVersionUID = 0; + } + + private static class UnmodifiableListMultimap + extends UnmodifiableMultimap implements ListMultimap { + UnmodifiableListMultimap(ListMultimap delegate) { + super(delegate); + } + + @Override + public ListMultimap delegate() { + return (ListMultimap) super.delegate(); + } + + @Override + public List get(@ParametricNullness K key) { + return Collections.unmodifiableList(delegate().get(key)); + } + + @Override + public List removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public List replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; + } + + private static class UnmodifiableSetMultimap + extends UnmodifiableMultimap implements SetMultimap { + UnmodifiableSetMultimap(SetMultimap delegate) { + super(delegate); + } + + @Override + public SetMultimap delegate() { + return (SetMultimap) super.delegate(); + } + + @Override + public Set get(@ParametricNullness K key) { + /* + * Note that this doesn't return a SortedSet when delegate is a + * SortedSetMultiset, unlike (SortedSet) super.get(). + */ + return Collections.unmodifiableSet(delegate().get(key)); + } + + @Override + public Set> entries() { + return Maps.unmodifiableEntrySet(delegate().entries()); + } + + @Override + public Set removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; + } + + private static class UnmodifiableSortedSetMultimap + extends UnmodifiableSetMultimap implements SortedSetMultimap { + UnmodifiableSortedSetMultimap(SortedSetMultimap delegate) { + super(delegate); + } + + @Override + public SortedSetMultimap delegate() { + return (SortedSetMultimap) super.delegate(); + } + + @Override + public SortedSet get(@ParametricNullness K key) { + return Collections.unmodifiableSortedSet(delegate().get(key)); + } + + @Override + public SortedSet removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public SortedSet replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public Comparator valueComparator() { + return delegate().valueComparator(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a synchronized (thread-safe) {@code SetMultimap} backed by the specified multimap. + * + *

You must follow the warnings described in {@link #synchronizedMultimap}. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param multimap the multimap to be wrapped + * @return a synchronized view of the specified multimap + */ + public static SetMultimap synchronizedSetMultimap( + SetMultimap multimap) { + return Synchronized.setMultimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified {@code SetMultimap}. Query operations on the + * returned multimap "read through" to the specified multimap, and attempts to modify the returned + * multimap, either directly or through the multimap's views, result in an {@code + * UnsupportedOperationException}. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static SetMultimap unmodifiableSetMultimap( + SetMultimap delegate) { + if (delegate instanceof UnmodifiableSetMultimap || delegate instanceof ImmutableSetMultimap) { + return delegate; + } + return new UnmodifiableSetMultimap<>(delegate); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static SetMultimap unmodifiableSetMultimap( + ImmutableSetMultimap delegate) { + return checkNotNull(delegate); + } + + /** + * Returns a synchronized (thread-safe) {@code SortedSetMultimap} backed by the specified + * multimap. + * + *

You must follow the warnings described in {@link #synchronizedMultimap}. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param multimap the multimap to be wrapped + * @return a synchronized view of the specified multimap + */ + public static + SortedSetMultimap synchronizedSortedSetMultimap(SortedSetMultimap multimap) { + return Synchronized.sortedSetMultimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified {@code SortedSetMultimap}. Query operations on + * the returned multimap "read through" to the specified multimap, and attempts to modify the + * returned multimap, either directly or through the multimap's views, result in an {@code + * UnsupportedOperationException}. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static + SortedSetMultimap unmodifiableSortedSetMultimap(SortedSetMultimap delegate) { + if (delegate instanceof UnmodifiableSortedSetMultimap) { + return delegate; + } + return new UnmodifiableSortedSetMultimap<>(delegate); + } + + /** + * Returns a synchronized (thread-safe) {@code ListMultimap} backed by the specified multimap. + * + *

You must follow the warnings described in {@link #synchronizedMultimap}. + * + * @param multimap the multimap to be wrapped + * @return a synchronized view of the specified multimap + */ + public static ListMultimap synchronizedListMultimap( + ListMultimap multimap) { + return Synchronized.listMultimap(multimap, null); + } + + /** + * Returns an unmodifiable view of the specified {@code ListMultimap}. Query operations on the + * returned multimap "read through" to the specified multimap, and attempts to modify the returned + * multimap, either directly or through the multimap's views, result in an {@code + * UnsupportedOperationException}. + * + *

The returned multimap will be serializable if the specified multimap is serializable. + * + * @param delegate the multimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified multimap + */ + public static ListMultimap unmodifiableListMultimap( + ListMultimap delegate) { + if (delegate instanceof UnmodifiableListMultimap || delegate instanceof ImmutableListMultimap) { + return delegate; + } + return new UnmodifiableListMultimap<>(delegate); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static ListMultimap unmodifiableListMultimap( + ImmutableListMultimap delegate) { + return checkNotNull(delegate); + } + + /** + * Returns an unmodifiable view of the specified collection, preserving the interface for + * instances of {@code SortedSet}, {@code Set}, {@code List} and {@code Collection}, in that order + * of preference. + * + * @param collection the collection for which to return an unmodifiable view + * @return an unmodifiable view of the collection + */ + private static Collection unmodifiableValueCollection( + Collection collection) { + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set) collection); + } else if (collection instanceof List) { + return Collections.unmodifiableList((List) collection); + } + return Collections.unmodifiableCollection(collection); + } + + /** + * Returns an unmodifiable view of the specified collection of entries. The {@link Entry#setValue} + * operation throws an {@link UnsupportedOperationException}. If the specified collection is a + * {@code Set}, the returned collection is also a {@code Set}. + * + * @param entries the entries for which to return an unmodifiable view + * @return an unmodifiable view of the entries + */ + private static Collection> unmodifiableEntries( + Collection> entries) { + if (entries instanceof Set) { + return Maps.unmodifiableEntrySet((Set>) entries); + } + return new Maps.UnmodifiableEntries<>(Collections.unmodifiableCollection(entries)); + } + + /** + * Returns {@link ListMultimap#asMap multimap.asMap()}, with its type corrected from {@code Map>} to {@code Map>}. + * + * @since 15.0 + */ + @SuppressWarnings("unchecked") + // safe by specification of ListMultimap.asMap() + public static Map> asMap( + ListMultimap multimap) { + return (Map>) (Map) multimap.asMap(); + } + + /** + * Returns {@link SetMultimap#asMap multimap.asMap()}, with its type corrected from {@code Map>} to {@code Map>}. + * + * @since 15.0 + */ + @SuppressWarnings("unchecked") + // safe by specification of SetMultimap.asMap() + public static Map> asMap( + SetMultimap multimap) { + return (Map>) (Map) multimap.asMap(); + } + + /** + * Returns {@link SortedSetMultimap#asMap multimap.asMap()}, with its type corrected from {@code + * Map>} to {@code Map>}. + * + * @since 15.0 + */ + @SuppressWarnings("unchecked") + // safe by specification of SortedSetMultimap.asMap() + public static Map> asMap( + SortedSetMultimap multimap) { + return (Map>) (Map) multimap.asMap(); + } + + /** + * Returns {@link Multimap#asMap multimap.asMap()}. This is provided for parity with the other + * more strongly-typed {@code asMap()} implementations. + * + * @since 15.0 + */ + public static Map> asMap( + Multimap multimap) { + return multimap.asMap(); + } + + /** + * Returns a multimap view of the specified map. The multimap is backed by the map, so changes to + * the map are reflected in the multimap, and vice versa. If the map is modified while an + * iteration over one of the multimap's collection views is in progress (except through the + * iterator's own {@code remove} operation, or through the {@code setValue} operation on a map + * entry returned by the iterator), the results of the iteration are undefined. + * + *

The multimap supports mapping removal, which removes the corresponding mapping from the map. + * It does not support any operations which might add mappings, such as {@code put}, {@code + * putAll} or {@code replaceValues}. + * + *

The returned multimap will be serializable if the specified map is serializable. + * + * @param map the backing map for the returned multimap view + */ + public static SetMultimap forMap(Map map) { + return new MapMultimap<>(map); + } + + /** + * @see Multimaps#forMap + */ + private static class MapMultimap + extends AbstractMultimap implements SetMultimap, Serializable { + final Map map; + + MapMultimap(Map map) { + this.map = checkNotNull(map); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public boolean containsEntry(Object key, Object value) { + return map.entrySet().contains(Maps.immutableEntry(key, value)); + } + + @Override + public Set get(@ParametricNullness final K key) { + return new Sets.ImprovedAbstractSet() { + @Override + public Iterator iterator() { + return new Iterator() { + int i; + + @Override + public boolean hasNext() { + return (i == 0) && map.containsKey(key); + } + + @Override + @ParametricNullness + public V next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + i++; + /* + * The cast is safe because of the containsKey check in hasNext(). (That means it's + * unsafe under concurrent modification, but all bets are off then, anyway.) + */ + return uncheckedCastNullableTToT(map.get(key)); + } + + @Override + public void remove() { + checkRemove(i == 1); + i = -1; + map.remove(key); + } + }; + } + + @Override + public int size() { + return map.containsKey(key) ? 1 : 0; + } + }; + } + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + @Override + public Set replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object key, Object value) { + return map.entrySet().remove(Maps.immutableEntry(key, value)); + } + + @Override + public Set removeAll(Object key) { + Set values = new HashSet(2); + if (!map.containsKey(key)) { + return values; + } + values.add(map.remove(key)); + return values; + } + + @Override + public void clear() { + map.clear(); + } + + @Override + Set createKeySet() { + return map.keySet(); + } + + @Override + Collection createValues() { + return map.values(); + } + + @Override + public Set> entries() { + return map.entrySet(); + } + + @Override + Collection> createEntries() { + throw new AssertionError("unreachable"); + } + + @Override + Multiset createKeys() { + return new Multimaps.Keys(this); + } + + @Override + Iterator> entryIterator() { + return map.entrySet().iterator(); + } + + @Override + Map> createAsMap() { + return new AsMap<>(this); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + private static final long serialVersionUID = 7845222491160860175L; + } + + /** + * Returns a view of a multimap where each value is transformed by a function. All other + * properties of the multimap, such as iteration order, are left intact. For example, the code: + * + *

{@code
+   * Multimap multimap =
+   *     ImmutableSetMultimap.of("a", 2, "b", -3, "b", -3, "a", 4, "c", 6);
+   * Function square = new Function() {
+   *     public String apply(Integer in) {
+   *       return Integer.toString(in * in);
+   *     }
+   * };
+   * Multimap transformed =
+   *     Multimaps.transformValues(multimap, square);
+   *   System.out.println(transformed);
+   * }
+ * + * ... prints {@code {a=[4, 16], b=[9, 9], c=[36]}}. + * + *

Changes in the underlying multimap are reflected in this view. Conversely, this view + * supports removal operations, and these are reflected in the underlying multimap. + * + *

It's acceptable for the underlying multimap to contain null keys, and even null values + * provided that the function is capable of accepting null input. The transformed multimap might + * contain null values, if the function sometimes gives a null result. + * + *

The returned multimap is not thread-safe or serializable, even if the underlying multimap + * is. The {@code equals} and {@code hashCode} methods of the returned multimap are meaningless, + * since there is not a definition of {@code equals} or {@code hashCode} for general collections, + * and {@code get()} will return a general {@code Collection} as opposed to a {@code List} or a + * {@code Set}. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned + * multimap to be a view, but it means that the function will be applied many times for bulk + * operations like {@link Multimap#containsValue} and {@code Multimap.toString()}. For this to + * perform well, {@code function} should be fast. To avoid lazy evaluation when the returned + * multimap doesn't need to be a view, copy the returned multimap into a new multimap of your + * choosing. + * + * @since 7.0 + */ + public static + Multimap transformValues( + Multimap fromMultimap, final Function function) { + checkNotNull(function); + EntryTransformer transformer = Maps.asEntryTransformer(function); + return transformEntries(fromMultimap, transformer); + } + + /** + * Returns a view of a {@code ListMultimap} where each value is transformed by a function. All + * other properties of the multimap, such as iteration order, are left intact. For example, the + * code: + * + *

{@code
+   * ListMultimap multimap
+   *      = ImmutableListMultimap.of("a", 4, "a", 16, "b", 9);
+   * Function sqrt =
+   *     new Function() {
+   *       public Double apply(Integer in) {
+   *         return Math.sqrt((int) in);
+   *       }
+   *     };
+   * ListMultimap transformed = Multimaps.transformValues(map,
+   *     sqrt);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {a=[2.0, 4.0], b=[3.0]}}. + * + *

Changes in the underlying multimap are reflected in this view. Conversely, this view + * supports removal operations, and these are reflected in the underlying multimap. + * + *

It's acceptable for the underlying multimap to contain null keys, and even null values + * provided that the function is capable of accepting null input. The transformed multimap might + * contain null values, if the function sometimes gives a null result. + * + *

The returned multimap is not thread-safe or serializable, even if the underlying multimap + * is. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned + * multimap to be a view, but it means that the function will be applied many times for bulk + * operations like {@link Multimap#containsValue} and {@code Multimap.toString()}. For this to + * perform well, {@code function} should be fast. To avoid lazy evaluation when the returned + * multimap doesn't need to be a view, copy the returned multimap into a new multimap of your + * choosing. + * + * @since 7.0 + */ + public static + ListMultimap transformValues( + ListMultimap fromMultimap, final Function function) { + checkNotNull(function); + EntryTransformer transformer = Maps.asEntryTransformer(function); + return transformEntries(fromMultimap, transformer); + } + + /** + * Returns a view of a multimap whose values are derived from the original multimap's entries. In + * contrast to {@link #transformValues}, this method's entry-transformation logic may depend on + * the key as well as the value. + * + *

All other properties of the transformed multimap, such as iteration order, are left intact. + * For example, the code: + * + *

{@code
+   * SetMultimap multimap =
+   *     ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
+   * EntryTransformer transformer =
+   *     new EntryTransformer() {
+   *       public String transformEntry(String key, Integer value) {
+   *          return (value >= 0) ? key : "no" + key;
+   *       }
+   *     };
+   * Multimap transformed =
+   *     Multimaps.transformEntries(multimap, transformer);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {a=[a, a], b=[nob]}}. + * + *

Changes in the underlying multimap are reflected in this view. Conversely, this view + * supports removal operations, and these are reflected in the underlying multimap. + * + *

It's acceptable for the underlying multimap to contain null keys and null values provided + * that the transformer is capable of accepting null inputs. The transformed multimap might + * contain null values if the transformer sometimes gives a null result. + * + *

The returned multimap is not thread-safe or serializable, even if the underlying multimap + * is. The {@code equals} and {@code hashCode} methods of the returned multimap are meaningless, + * since there is not a definition of {@code equals} or {@code hashCode} for general collections, + * and {@code get()} will return a general {@code Collection} as opposed to a {@code List} or a + * {@code Set}. + * + *

The transformer is applied lazily, invoked when needed. This is necessary for the returned + * multimap to be a view, but it means that the transformer will be applied many times for bulk + * operations like {@link Multimap#containsValue} and {@link Object#toString}. For this to perform + * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned multimap + * doesn't need to be a view, copy the returned multimap into a new multimap of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of {@code + * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of + * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the + * transformed multimap. + * + * @since 7.0 + */ + public static + Multimap transformEntries( + Multimap fromMap, EntryTransformer transformer) { + return new TransformedEntriesMultimap<>(fromMap, transformer); + } + + /** + * Returns a view of a {@code ListMultimap} whose values are derived from the original multimap's + * entries. In contrast to {@link #transformValues(ListMultimap, Function)}, this method's + * entry-transformation logic may depend on the key as well as the value. + * + *

All other properties of the transformed multimap, such as iteration order, are left intact. + * For example, the code: + * + *

{@code
+   * Multimap multimap =
+   *     ImmutableMultimap.of("a", 1, "a", 4, "b", 6);
+   * EntryTransformer transformer =
+   *     new EntryTransformer() {
+   *       public String transformEntry(String key, Integer value) {
+   *         return key + value;
+   *       }
+   *     };
+   * Multimap transformed =
+   *     Multimaps.transformEntries(multimap, transformer);
+   * System.out.println(transformed);
+   * }
+ * + * ... prints {@code {"a"=["a1", "a4"], "b"=["b6"]}}. + * + *

Changes in the underlying multimap are reflected in this view. Conversely, this view + * supports removal operations, and these are reflected in the underlying multimap. + * + *

It's acceptable for the underlying multimap to contain null keys and null values provided + * that the transformer is capable of accepting null inputs. The transformed multimap might + * contain null values if the transformer sometimes gives a null result. + * + *

The returned multimap is not thread-safe or serializable, even if the underlying multimap + * is. + * + *

The transformer is applied lazily, invoked when needed. This is necessary for the returned + * multimap to be a view, but it means that the transformer will be applied many times for bulk + * operations like {@link Multimap#containsValue} and {@link Object#toString}. For this to perform + * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned multimap + * doesn't need to be a view, copy the returned multimap into a new multimap of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of {@code + * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of + * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the + * transformed multimap. + * + * @since 7.0 + */ + public static + ListMultimap transformEntries( + ListMultimap fromMap, EntryTransformer transformer) { + return new TransformedEntriesListMultimap<>(fromMap, transformer); + } + + private static class TransformedEntriesMultimap< + K extends Object, V1 extends Object, V2 extends Object> + extends AbstractMultimap { + final Multimap fromMultimap; + final EntryTransformer transformer; + + TransformedEntriesMultimap( + Multimap fromMultimap, + final EntryTransformer transformer) { + this.fromMultimap = checkNotNull(fromMultimap); + this.transformer = checkNotNull(transformer); + } + + Collection transform(@ParametricNullness K key, Collection values) { + Function function = Maps.asValueToValueFunction(transformer, key); + if (values instanceof List) { + return Lists.transform((List) values, function); + } else { + return Collections2.transform(values, function); + } + } + + @Override + Map> createAsMap() { + return Maps.transformEntries(fromMultimap.asMap(), (key, value) -> transform(key, value)); + } + + @Override + public void clear() { + fromMultimap.clear(); + } + + @Override + public boolean containsKey(Object key) { + return fromMultimap.containsKey(key); + } + + @Override + Collection> createEntries() { + return new Entries(); + } + + @Override + Iterator> entryIterator() { + return Iterators.transform( + fromMultimap.entries().iterator(), Maps.asEntryToEntryFunction(transformer)); + } + + @Override + public Collection get(@ParametricNullness final K key) { + return transform(key, fromMultimap.get(key)); + } + + @Override + public boolean isEmpty() { + return fromMultimap.isEmpty(); + } + + @Override + Set createKeySet() { + return fromMultimap.keySet(); + } + + @Override + Multiset createKeys() { + return fromMultimap.keys(); + } + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V2 value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("unchecked") + @Override + public boolean remove(Object key, Object value) { + return get((K) key).remove(value); + } + + @SuppressWarnings("unchecked") + @Override + public Collection removeAll(Object key) { + return transform((K) key, fromMultimap.removeAll(key)); + } + + @Override + public Collection replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + return fromMultimap.size(); + } + + @Override + Collection createValues() { + return Collections2.transform( + fromMultimap.entries(), Maps.asEntryToValueFunction(transformer)); + } + } + + private static final class TransformedEntriesListMultimap< + K extends Object, V1 extends Object, V2 extends Object> + extends TransformedEntriesMultimap implements ListMultimap { + + TransformedEntriesListMultimap( + ListMultimap fromMultimap, EntryTransformer transformer) { + super(fromMultimap, transformer); + } + + @Override + List transform(@ParametricNullness K key, Collection values) { + return Lists.transform((List) values, Maps.asValueToValueFunction(transformer, key)); + } + + @Override + public List get(@ParametricNullness K key) { + return transform(key, fromMultimap.get(key)); + } + + @SuppressWarnings("unchecked") + @Override + public List removeAll(Object key) { + return transform((K) key, fromMultimap.removeAll(key)); + } + + @Override + public List replaceValues(@ParametricNullness K key, Iterable values) { + throw new UnsupportedOperationException(); + } + } + + /** + * Creates an index {@code ImmutableListMultimap} that contains the results of applying a + * specified function to each item in an {@code Iterable} of values. Each value will be stored as + * a value in the resulting multimap, yielding a multimap with the same size as the input + * iterable. The key used to store that value in the multimap will be the result of calling the + * function on that value. The resulting multimap is created as an immutable snapshot. In the + * returned multimap, keys appear in the order they are first encountered, and the values + * corresponding to each key appear in the same order as they are encountered. + * + *

For example, + * + *

{@code
+   * List badGuys =
+   *     Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
+   * Function stringLengthFunction = ...;
+   * Multimap index =
+   *     Multimaps.index(badGuys, stringLengthFunction);
+   * System.out.println(index);
+   * }
+ * + *

prints + * + *

{@code
+   * {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
+   * }
+ * + *

The returned multimap is serializable if its keys and values are all serializable. + * + * @param values the values to use when constructing the {@code ImmutableListMultimap} + * @param keyFunction the function used to produce the key for each value + * @return {@code ImmutableListMultimap} mapping the result of evaluating the function {@code + * keyFunction} on each value in the input collection to that value + * @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code + * keyFunction} produces {@code null} for any key + */ + public static ImmutableListMultimap index( + Iterable values, Function keyFunction) { + return index(values.iterator(), keyFunction); + } + + /** + * Creates an index {@code ImmutableListMultimap} that contains the results of applying a + * specified function to each item in an {@code Iterator} of values. Each value will be stored as + * a value in the resulting multimap, yielding a multimap with the same size as the input + * iterator. The key used to store that value in the multimap will be the result of calling the + * function on that value. The resulting multimap is created as an immutable snapshot. In the + * returned multimap, keys appear in the order they are first encountered, and the values + * corresponding to each key appear in the same order as they are encountered. + * + *

For example, + * + *

{@code
+   * List badGuys =
+   *     Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
+   * Function stringLengthFunction = ...;
+   * Multimap index =
+   *     Multimaps.index(badGuys.iterator(), stringLengthFunction);
+   * System.out.println(index);
+   * }
+ * + *

prints + * + *

{@code
+   * {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
+   * }
+ * + *

The returned multimap is serializable if its keys and values are all serializable. + * + * @param values the values to use when constructing the {@code ImmutableListMultimap} + * @param keyFunction the function used to produce the key for each value + * @return {@code ImmutableListMultimap} mapping the result of evaluating the function {@code + * keyFunction} on each value in the input collection to that value + * @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code + * keyFunction} produces {@code null} for any key + * @since 10.0 + */ + public static ImmutableListMultimap index( + Iterator values, Function keyFunction) { + checkNotNull(keyFunction); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + while (values.hasNext()) { + V value = values.next(); + checkNotNull(value, values); + builder.put(keyFunction.apply(value), value); + } + return builder.build(); + } + + static class Keys extends AbstractMultiset { + final Multimap multimap; + + Keys(Multimap multimap) { + this.multimap = multimap; + } + + @Override + Iterator> entryIterator() { + return new TransformedIterator>, Multiset.Entry>( + multimap.asMap().entrySet().iterator()) { + @Override + Multiset.Entry transform(final Map.Entry> backingEntry) { + return new Multisets.AbstractEntry() { + @Override + @ParametricNullness + public K getElement() { + return backingEntry.getKey(); + } + + @Override + public int getCount() { + return backingEntry.getValue().size(); + } + }; + } + }; + } + + @Override + public Spliterator spliterator() { + return CollectSpliterators.map(multimap.entries().spliterator(), Map.Entry::getKey); + } + + @Override + public void forEach(Consumer consumer) { + checkNotNull(consumer); + multimap.entries().forEach(entry -> consumer.accept(entry.getKey())); + } + + @Override + int distinctElements() { + return multimap.asMap().size(); + } + + @Override + public int size() { + return multimap.size(); + } + + @Override + public boolean contains(Object element) { + return multimap.containsKey(element); + } + + @Override + public Iterator iterator() { + return Maps.keyIterator(multimap.entries().iterator()); + } + + @Override + public int count(Object element) { + Collection values = Maps.safeGet(multimap.asMap(), element); + return (values == null) ? 0 : values.size(); + } + + @Override + public int remove(Object element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + + Collection values = Maps.safeGet(multimap.asMap(), element); + + if (values == null) { + return 0; + } + + int oldCount = values.size(); + if (occurrences >= oldCount) { + values.clear(); + } else { + Iterator iterator = values.iterator(); + for (int i = 0; i < occurrences; i++) { + iterator.next(); + iterator.remove(); + } + } + return oldCount; + } + + @Override + public void clear() { + multimap.clear(); + } + + @Override + public Set elementSet() { + return multimap.keySet(); + } + + @Override + Iterator elementIterator() { + throw new AssertionError("should never be called"); + } + } + + /** A skeleton implementation of {@link Multimap#entries()}. */ + abstract static class Entries + extends AbstractCollection> { + abstract Multimap multimap(); + + @Override + public int size() { + return multimap().size(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) o; + return multimap().containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) o; + return multimap().remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public void clear() { + multimap().clear(); + } + } + + /** A skeleton implementation of {@link Multimap#asMap()}. */ + static final class AsMap + extends Maps.ViewCachingAbstractMap> { + private final Multimap multimap; + + AsMap(Multimap multimap) { + this.multimap = checkNotNull(multimap); + } + + @Override + public int size() { + return multimap.keySet().size(); + } + + @Override + protected Set>> createEntrySet() { + return new EntrySet(); + } + + void removeValuesForKey(Object key) { + multimap.keySet().remove(key); + } + + class EntrySet extends Maps.EntrySet> { + @Override + Map> map() { + return AsMap.this; + } + + @Override + public Iterator>> iterator() { + return Maps.asMapEntryIterator(multimap.keySet(), key -> multimap.get(key)); + } + + @Override + public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + // requireNonNull is safe because of the contains check. + Map.Entry entry = requireNonNull((Map.Entry) o); + removeValuesForKey(entry.getKey()); + return true; + } + } + + @SuppressWarnings("unchecked") + @Override + public Collection get(Object key) { + return containsKey(key) ? multimap.get((K) key) : null; + } + + @Override + public Collection remove(Object key) { + return containsKey(key) ? multimap.removeAll(key) : null; + } + + @Override + public Set keySet() { + return multimap.keySet(); + } + + @Override + public boolean isEmpty() { + return multimap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return multimap.containsKey(key); + } + + @Override + public void clear() { + multimap.clear(); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a + * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect + * the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a key that doesn't + * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose keys satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code keyPredicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 11.0 + */ + public static Multimap filterKeys( + Multimap unfiltered, final Predicate keyPredicate) { + if (unfiltered instanceof SetMultimap) { + return filterKeys((SetMultimap) unfiltered, keyPredicate); + } else if (unfiltered instanceof ListMultimap) { + return filterKeys((ListMultimap) unfiltered, keyPredicate); + } else if (unfiltered instanceof FilteredKeyMultimap) { + FilteredKeyMultimap prev = (FilteredKeyMultimap) unfiltered; + return new FilteredKeyMultimap<>( + prev.unfiltered, Predicates.and(prev.keyPredicate, keyPredicate)); + } else if (unfiltered instanceof FilteredMultimap) { + FilteredMultimap prev = (FilteredMultimap) unfiltered; + return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); + } else { + return new FilteredKeyMultimap<>(unfiltered, keyPredicate); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a + * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect + * the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a key that doesn't + * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose keys satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code keyPredicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 14.0 + */ + public static SetMultimap filterKeys( + SetMultimap unfiltered, final Predicate keyPredicate) { + if (unfiltered instanceof FilteredKeySetMultimap) { + FilteredKeySetMultimap prev = (FilteredKeySetMultimap) unfiltered; + return new FilteredKeySetMultimap<>( + prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); + } else if (unfiltered instanceof FilteredSetMultimap) { + FilteredSetMultimap prev = (FilteredSetMultimap) unfiltered; + return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); + } else { + return new FilteredKeySetMultimap<>(unfiltered, keyPredicate); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a + * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect + * the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a key that doesn't + * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose keys satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code keyPredicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 14.0 + */ + public static ListMultimap filterKeys( + ListMultimap unfiltered, final Predicate keyPredicate) { + if (unfiltered instanceof FilteredKeyListMultimap) { + FilteredKeyListMultimap prev = (FilteredKeyListMultimap) unfiltered; + return new FilteredKeyListMultimap<>( + prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); + } else { + return new FilteredKeyListMultimap<>(unfiltered, keyPredicate); + } + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose values satisfy a + * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect + * the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a value that doesn't + * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose value satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code valuePredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 11.0 + */ + public static Multimap filterValues( + Multimap unfiltered, final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} whose values satisfy a + * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect + * the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a value that doesn't + * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose value satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code valuePredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. + * + * @since 14.0 + */ + public static SetMultimap filterValues( + SetMultimap unfiltered, final Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} that satisfy a predicate. The + * returned multimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a key/value pair that + * doesn't satisfy the predicate, multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose keys satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. + * + * @since 11.0 + */ + public static Multimap filterEntries( + Multimap unfiltered, Predicate> entryPredicate) { + checkNotNull(entryPredicate); + if (unfiltered instanceof SetMultimap) { + return filterEntries((SetMultimap) unfiltered, entryPredicate); + } + return (unfiltered instanceof FilteredMultimap) + ? filterFiltered((FilteredMultimap) unfiltered, entryPredicate) + : new FilteredEntryMultimap(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Returns a multimap containing the mappings in {@code unfiltered} that satisfy a predicate. The + * returned multimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting multimap's views have iterators that don't support {@code remove()}, but all + * other methods are supported by the multimap and its views. When adding a key/value pair that + * doesn't satisfy the predicate, multimap's {@code put()}, {@code putAll()}, and {@code + * replaceValues()} methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * multimap or its views, only mappings whose keys satisfy the filter will be removed from the + * underlying multimap. + * + *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying multimap and determine which satisfy the filter. When a + * live view is not needed, it may be faster to copy the filtered multimap and use the + * copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals, as documented + * at {@link Predicate#apply}. + * + * @since 14.0 + */ + public static SetMultimap filterEntries( + SetMultimap unfiltered, Predicate> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredSetMultimap) + ? filterFiltered((FilteredSetMultimap) unfiltered, entryPredicate) + : new FilteredEntrySetMultimap(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Support removal operations when filtering a filtered multimap. Since a filtered multimap has + * iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would + * lead to a multimap whose removal operations would fail. This method combines the predicates to + * avoid that problem. + */ + private static Multimap filterFiltered( + FilteredMultimap multimap, Predicate> entryPredicate) { + Predicate> predicate = + Predicates.>and(multimap.entryPredicate(), entryPredicate); + return new FilteredEntryMultimap<>(multimap.unfiltered(), predicate); + } + + /** + * Support removal operations when filtering a filtered multimap. Since a filtered multimap has + * iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would + * lead to a multimap whose removal operations would fail. This method combines the predicates to + * avoid that problem. + */ + private static SetMultimap filterFiltered( + FilteredSetMultimap multimap, Predicate> entryPredicate) { + Predicate> predicate = + Predicates.>and(multimap.entryPredicate(), entryPredicate); + return new FilteredEntrySetMultimap<>(multimap.unfiltered(), predicate); + } + + static boolean equalsImpl(Multimap multimap, Object object) { + if (object == multimap) { + return true; + } + if (object instanceof Multimap) { + Multimap that = (Multimap) object; + return multimap.asMap().equals(that.asMap()); + } + return false; + } + + // TODO(jlevy): Create methods that filter a SortedSetMultimap. +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multiset.java new file mode 100644 index 0000000000000..232bd1aa6c2cf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multiset.java @@ -0,0 +1,480 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A collection that supports order-independent equality, like {@link Set}, but may have duplicate + * elements. A multiset is also sometimes called a bag. + * + *

Elements of a multiset that are equal to one another are referred to as occurrences of + * the same single element. The total number of occurrences of an element in a multiset is called + * the count of that element (the terms "frequency" and "multiplicity" are equivalent, but + * not used in this API). Since the count of an element is represented as an {@code int}, a multiset + * may never contain more than {@link Integer#MAX_VALUE} occurrences of any one element. + * + *

{@code Multiset} refines the specifications of several methods from {@code Collection}. It + * also defines an additional query operation, {@link #count}, which returns the count of an + * element. There are five new bulk-modification operations, for example {@link #add(Object, int)}, + * to add or remove multiple occurrences of an element at once, or to set the count of an element to + * a specific value. These modification operations are optional, but implementations which support + * the standard collection operations {@link #add(Object)} or {@link #remove(Object)} are encouraged + * to implement the related methods as well. Finally, two collection views are provided: {@link + * #elementSet} contains the distinct elements of the multiset "with duplicates collapsed", and + * {@link #entrySet} is similar but contains {@link Entry Multiset.Entry} instances, each providing + * both a distinct element and the count of that element. + * + *

In addition to these required methods, implementations of {@code Multiset} are expected to + * provide two {@code static} creation methods: {@code create()}, returning an empty multiset, and + * {@code create(Iterable)}, returning a multiset containing the given initial + * elements. This is simply a refinement of {@code Collection}'s constructor recommendations, + * reflecting the new developments of Java 5. + * + *

As with other collection types, the modification operations are optional, and should throw + * {@link UnsupportedOperationException} when they are not implemented. Most implementations should + * support either all add operations or none of them, all removal operations or none of them, and if + * and only if all of these are supported, the {@code setCount} methods as well. + * + *

A multiset uses {@link Object#equals} to determine whether two instances should be considered + * "the same," unless specified otherwise by the implementation. + * + *

Warning: as with normal {@link Set}s, it is almost always a bad idea to modify an + * element (in a way that affects its {@link Object#equals} behavior) while it is contained in a + * multiset. Undefined behavior and bugs will result. + * + *

Implementations

+ * + *
    + *
  • {@link ImmutableMultiset} + *
  • {@link ImmutableSortedMultiset} + *
  • {@link HashMultiset} + *
  • {@link LinkedHashMultiset} + *
  • {@link TreeMultiset} + *
  • {@link EnumMultiset} + *
  • {@link ConcurrentHashMultiset} + *
+ * + *

If your values may be zero, negative, or outside the range of an int, you may wish to use + * {@link org.apache.iotdb.google.common.util.concurrent.AtomicLongMap} instead. Note, however, that + * unlike {@code Multiset}, {@code AtomicLongMap} does not automatically remove zeros. + * + *

See the Guava User Guide article on {@code Multiset}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface Multiset extends Collection { + // Query Operations + + /** + * Returns the total number of all occurrences of all elements in this multiset. + * + *

Note: this method does not return the number of distinct elements in the + * multiset, which is given by {@code entrySet().size()}. + */ + @Override + int size(); + + /** + * Returns the number of occurrences of an element in this multiset (the count of the + * element). Note that for an {@link Object#equals}-based multiset, this gives the same result as + * {@link Collections#frequency} (which would presumably perform more poorly). + * + *

Note: the utility method {@link Iterables#frequency} generalizes this operation; it + * correctly delegates to this method when dealing with a multiset, but it can also accept any + * other iterable type. + * + * @param element the element to count occurrences of + * @return the number of occurrences of the element in this multiset; possibly zero but never + * negative + */ + int count(Object element); + + // Bulk Operations + + /** + * Adds a number of occurrences of an element to this multiset. Note that if {@code occurrences == + * 1}, this method has the identical effect to {@link #add(Object)}. This method is functionally + * equivalent (except in the case of overflow) to the call {@code + * addAll(Collections.nCopies(element, occurrences))}, which would presumably perform much more + * poorly. + * + * @param element the element to add occurrences of; may be null only if explicitly allowed by the + * implementation + * @param occurrences the number of occurrences of the element to add. May be zero, in which case + * no change will be made. + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation would + * result in more than {@link Integer#MAX_VALUE} occurrences of the element + * @throws NullPointerException if {@code element} is null and this implementation does not permit + * null elements. Note that if {@code occurrences} is zero, the implementation may opt to + * return normally. + */ + int add(@ParametricNullness E element, int occurrences); + + /** + * Adds a single occurrence of the specified element to this multiset. + * + *

This method refines {@link Collection#add}, which only ensures the presence of the + * element, to further specify that a successful call must always increment the count of the + * element, and the overall size of the collection, by one. + * + *

To both add the element and obtain the previous count of that element, use {@link + * #add(Object, int) add}{@code (element, 1)} instead. + * + * @param element the element to add one occurrence of; may be null only if explicitly allowed by + * the implementation + * @return {@code true} always, since this call is required to modify the multiset, unlike other + * {@link Collection} types + * @throws NullPointerException if {@code element} is null and this implementation does not permit + * null elements + * @throws IllegalArgumentException if {@link Integer#MAX_VALUE} occurrences of {@code element} + * are already contained in this multiset + */ + @Override + boolean add(@ParametricNullness E element); + + /** + * Removes a number of occurrences of the specified element from this multiset. If the multiset + * contains fewer than this number of occurrences to begin with, all occurrences will be removed. + * Note that if {@code occurrences == 1}, this is functionally equivalent to the call {@code + * remove(element)}. + * + * @param element the element to conditionally remove occurrences of + * @param occurrences the number of occurrences of the element to remove. May be zero, in which + * case no change will be made. + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code occurrences} is negative + */ + int remove(Object element, int occurrences); + + /** + * Removes a single occurrence of the specified element from this multiset, if present. + * + *

This method refines {@link Collection#remove} to further specify that it may not + * throw an exception in response to {@code element} being null or of the wrong type. + * + *

To both remove the element and obtain the previous count of that element, use {@link + * #remove(Object, int) remove}{@code (element, 1)} instead. + * + * @param element the element to remove one occurrence of + * @return {@code true} if an occurrence was found and removed + */ + @Override + boolean remove(Object element); + + /** + * Adds or removes the necessary occurrences of an element such that the element attains the + * desired count. + * + * @param element the element to add or remove occurrences of; may be null only if explicitly + * allowed by the implementation + * @param count the desired count of the element in this multiset + * @return the count of the element before the operation; possibly zero + * @throws IllegalArgumentException if {@code count} is negative + * @throws NullPointerException if {@code element} is null and this implementation does not permit + * null elements. Note that if {@code count} is zero, the implementor may optionally return + * zero instead. + */ + int setCount(@ParametricNullness E element, int count); + + /** + * Conditionally sets the count of an element to a new value, as described in {@link + * #setCount(Object, int)}, provided that the element has the expected current count. If the + * current count is not {@code oldCount}, no change is made. + * + * @param element the element to conditionally set the count of; may be null only if explicitly + * allowed by the implementation + * @param oldCount the expected present count of the element in this multiset + * @param newCount the desired count of the element in this multiset + * @return {@code true} if the condition for modification was met. This implies that the multiset + * was indeed modified, unless {@code oldCount == newCount}. + * @throws IllegalArgumentException if {@code oldCount} or {@code newCount} is negative + * @throws NullPointerException if {@code element} is null and the implementation does not permit + * null elements. Note that if {@code oldCount} and {@code newCount} are both zero, the + * implementor may optionally return {@code true} instead. + */ + boolean setCount(@ParametricNullness E element, int oldCount, int newCount); + + // Views + + /** + * Returns the set of distinct elements contained in this multiset. The element set is backed by + * the same data as the multiset, so any change to either is immediately reflected in the other. + * The order of the elements in the element set is unspecified. + * + *

If the element set supports any removal operations, these necessarily cause all + * occurrences of the removed element(s) to be removed from the multiset. Implementations are not + * expected to support the add operations, although this is possible. + * + *

A common use for the element set is to find the number of distinct elements in the multiset: + * {@code elementSet().size()}. + * + * @return a view of the set of distinct elements in this multiset + */ + Set elementSet(); + + /** + * Returns a view of the contents of this multiset, grouped into {@code Multiset.Entry} instances, + * each providing an element of the multiset and the count of that element. This set contains + * exactly one entry for each distinct element in the multiset (thus it always has the same size + * as the {@link #elementSet}). The order of the elements in the element set is unspecified. + * + *

The entry set is backed by the same data as the multiset, so any change to either is + * immediately reflected in the other. However, multiset changes may or may not be reflected in + * any {@code Entry} instances already retrieved from the entry set (this is + * implementation-dependent). Furthermore, implementations are not required to support + * modifications to the entry set at all, and the {@code Entry} instances themselves don't even + * have methods for modification. See the specific implementation class for more details on how + * its entry set handles modifications. + * + * @return a set of entries representing the data of this multiset + */ + Set> entrySet(); + + /** + * An unmodifiable element-count pair for a multiset. The {@link Multiset#entrySet} method returns + * a view of the multiset whose elements are of this class. A multiset implementation may return + * Entry instances that are either live "read-through" views to the Multiset, or immutable + * snapshots. Note that this type is unrelated to the similarly-named type {@code Map.Entry}. + * + * @since 2.0 + */ + interface Entry { + + /** + * Returns the multiset element corresponding to this entry. Multiple calls to this method + * always return the same instance. + * + * @return the element corresponding to this entry + */ + @ParametricNullness + E getElement(); + + /** + * Returns the count of the associated element in the underlying multiset. This count may either + * be an unchanging snapshot of the count at the time the entry was retrieved, or a live view of + * the current count of the element in the multiset, depending on the implementation. Note that + * in the former case, this method can never return zero, while in the latter, it will return + * zero if all occurrences of the element were since removed from the multiset. + * + * @return the count of the element; never negative + */ + int getCount(); + + /** + * {@inheritDoc} + * + *

Returns {@code true} if the given object is also a multiset entry and the two entries + * represent the same element and count. That is, two entries {@code a} and {@code b} are equal + * if: + * + *

{@code
+     * Objects.equal(a.getElement(), b.getElement())
+     *     && a.getCount() == b.getCount()
+     * }
+ */ + @Override + // TODO(kevinb): check this wrt TreeMultiset? + boolean equals(Object o); + + /** + * {@inheritDoc} + * + *

The hash code of a multiset entry for element {@code element} and count {@code count} is + * defined as: + * + *

{@code
+     * ((element == null) ? 0 : element.hashCode()) ^ count
+     * }
+ */ + @Override + int hashCode(); + + /** + * Returns the canonical string representation of this entry, defined as follows. If the count + * for this entry is one, this is simply the string representation of the corresponding element. + * Otherwise, it is the string representation of the element, followed by the three characters + * {@code " x "} (space, letter x, space), followed by the count. + */ + @Override + String toString(); + } + + /** + * Runs the specified action for each distinct element in this multiset, and the number of + * occurrences of that element. For some {@code Multiset} implementations, this may be more + * efficient than iterating over the {@link #entrySet()} either explicitly or with {@code + * entrySet().forEach(action)}. + * + * @since 21.0 + */ + default void forEachEntry(ObjIntConsumer action) { + checkNotNull(action); + entrySet().forEach(entry -> action.accept(entry.getElement(), entry.getCount())); + } + + // Comparison and hashing + + /** + * Compares the specified object with this multiset for equality. Returns {@code true} if the + * given object is also a multiset and contains equal elements with equal counts, regardless of + * order. + */ + @Override + // TODO(kevinb): caveats about equivalence-relation? + boolean equals(Object object); + + /** + * Returns the hash code for this multiset. This is defined as the sum of + * + *
{@code
+   * ((element == null) ? 0 : element.hashCode()) ^ count(element)
+   * }
+ * + *

over all distinct elements in the multiset. It follows that a multiset and its entry set + * always have the same hash code. + */ + @Override + int hashCode(); + + /** + * {@inheritDoc} + * + *

It is recommended, though not mandatory, that this method return the result of invoking + * {@link #toString} on the {@link #entrySet}, yielding a result such as {@code [a x 3, c, d x 2, + * e]}. + */ + @Override + String toString(); + + // Refined Collection Methods + + /** + * {@inheritDoc} + * + *

Elements that occur multiple times in the multiset will appear multiple times in this + * iterator, though not necessarily sequentially. + */ + @Override + Iterator iterator(); + + /** + * Determines whether this multiset contains the specified element. + * + *

This method refines {@link Collection#contains} to further specify that it may not + * throw an exception in response to {@code element} being null or of the wrong type. + * + * @param element the element to check for + * @return {@code true} if this multiset contains at least one occurrence of the element + */ + @Override + boolean contains(Object element); + + /** + * Returns {@code true} if this multiset contains at least one occurrence of each element in the + * specified collection. + * + *

This method refines {@link Collection#containsAll} to further specify that it may not + * throw an exception in response to any of {@code elements} being null or of the wrong type. + * + *

Note: this method does not take into account the occurrence count of an element in + * the two collections; it may still return {@code true} even if {@code elements} contains several + * occurrences of an element and this multiset contains only one. This is no different than any + * other collection type like {@link List}, but it may be unexpected to the user of a multiset. + * + * @param elements the collection of elements to be checked for containment in this multiset + * @return {@code true} if this multiset contains at least one occurrence of each element + * contained in {@code elements} + * @throws NullPointerException if {@code elements} is null + */ + @Override + boolean containsAll(Collection elements); + + /** + * {@inheritDoc} + * + *

Note: This method ignores how often any element might appear in {@code c}, and only + * cares whether or not an element appears at all. If you wish to remove one occurrence in this + * multiset for every occurrence in {@code c}, see {@link Multisets#removeOccurrences(Multiset, + * Multiset)}. + * + *

This method refines {@link Collection#removeAll} to further specify that it may not + * throw an exception in response to any of {@code elements} being null or of the wrong type. + */ + @Override + boolean removeAll(Collection c); + + /** + * {@inheritDoc} + * + *

Note: This method ignores how often any element might appear in {@code c}, and only + * cares whether or not an element appears at all. If you wish to remove one occurrence in this + * multiset for every occurrence in {@code c}, see {@link Multisets#retainOccurrences(Multiset, + * Multiset)}. + * + *

This method refines {@link Collection#retainAll} to further specify that it may not + * throw an exception in response to any of {@code elements} being null or of the wrong type. + * + * @see Multisets#retainOccurrences(Multiset, Multiset) + */ + @Override + boolean retainAll(Collection c); + + /** + * {@inheritDoc} + * + *

Elements that occur multiple times in the multiset will be passed to the {@code Consumer} + * correspondingly many times, though not necessarily sequentially. + */ + @Override + default void forEach(Consumer action) { + checkNotNull(action); + entrySet() + .forEach( + entry -> { + E elem = entry.getElement(); + int count = entry.getCount(); + for (int i = 0; i < count; i++) { + action.accept(elem); + } + }); + } + + @Override + default Spliterator spliterator() { + return Multisets.spliteratorImpl(this); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multisets.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multisets.java new file mode 100644 index 0000000000000..da1650e76158f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Multisets.java @@ -0,0 +1,1188 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.collect.Multiset.Entry; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.stream.Collector; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkRemove; + +/** + * Provides static utility methods for creating and working with {@link Multiset} instances. + * + *

See the Guava User Guide article on {@code + * Multisets}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Multisets { + private Multisets() {} + + /** + * Returns a {@code Collector} that accumulates elements into a multiset created via the specified + * {@code Supplier}, whose elements are the result of applying {@code elementFunction} to the + * inputs, with counts equal to the result of applying {@code countFunction} to the inputs. + * Elements are added in encounter order. + * + *

If the mapped elements contain duplicates (according to {@link Object#equals}), the element + * will be added more than once, with the count summed over all appearances of the element. + * + *

Note that {@code stream.collect(toMultiset(function, e -> 1, supplier))} is equivalent to + * {@code stream.map(function).collect(Collectors.toCollection(supplier))}. + * + *

To collect to an {@link ImmutableMultiset}, use {@link + * ImmutableMultiset#toImmutableMultiset}. + * + * @since 22.0 + */ + public static > + Collector toMultiset( + Function elementFunction, + ToIntFunction countFunction, + Supplier multisetSupplier) { + return CollectCollectors.toMultiset(elementFunction, countFunction, multisetSupplier); + } + + /** + * Returns an unmodifiable view of the specified multiset. Query operations on the returned + * multiset "read through" to the specified multiset, and attempts to modify the returned multiset + * result in an {@link UnsupportedOperationException}. + * + *

The returned multiset will be serializable if the specified multiset is serializable. + * + * @param multiset the multiset for which an unmodifiable view is to be generated + * @return an unmodifiable view of the multiset + */ + public static Multiset unmodifiableMultiset( + Multiset multiset) { + if (multiset instanceof UnmodifiableMultiset || multiset instanceof ImmutableMultiset) { + @SuppressWarnings("unchecked") // Since it's unmodifiable, the covariant cast is safe + Multiset result = (Multiset) multiset; + return result; + } + return new UnmodifiableMultiset(checkNotNull(multiset)); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ + @Deprecated + public static Multiset unmodifiableMultiset(ImmutableMultiset multiset) { + return checkNotNull(multiset); + } + + static class UnmodifiableMultiset extends ForwardingMultiset + implements Serializable { + final Multiset delegate; + + UnmodifiableMultiset(Multiset delegate) { + this.delegate = delegate; + } + + @SuppressWarnings("unchecked") + @Override + protected Multiset delegate() { + // This is safe because all non-covariant methods are overridden + return (Multiset) delegate; + } + + transient Set elementSet; + + Set createElementSet() { + return Collections.unmodifiableSet(delegate.elementSet()); + } + + @Override + public Set elementSet() { + Set es = elementSet; + return (es == null) ? elementSet = createElementSet() : es; + } + + transient Set> entrySet; + + @SuppressWarnings("unchecked") + @Override + public Set> entrySet() { + Set> es = entrySet; + return (es == null) + // Safe because the returned set is made unmodifiable and Entry + // itself is readonly + ? entrySet = (Set) Collections.unmodifiableSet(delegate.entrySet()) + : es; + } + + @Override + public Iterator iterator() { + return Iterators.unmodifiableIterator(delegate.iterator()); + } + + @Override + public boolean add(@ParametricNullness E element) { + throw new UnsupportedOperationException(); + } + + @Override + public int add(@ParametricNullness E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection elementsToAdd) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection elementsToRemove) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection elementsToRetain) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public int setCount(@ParametricNullness E element, int count) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an unmodifiable view of the specified sorted multiset. Query operations on the returned + * multiset "read through" to the specified multiset, and attempts to modify the returned multiset + * result in an {@link UnsupportedOperationException}. + * + *

The returned multiset will be serializable if the specified multiset is serializable. + * + * @param sortedMultiset the sorted multiset for which an unmodifiable view is to be generated + * @return an unmodifiable view of the multiset + * @since 11.0 + */ + public static SortedMultiset unmodifiableSortedMultiset( + SortedMultiset sortedMultiset) { + // it's in its own file so it can be emulated for GWT + return new UnmodifiableSortedMultiset(checkNotNull(sortedMultiset)); + } + + /** + * Returns an immutable multiset entry with the specified element and count. The entry will be + * serializable if {@code e} is. + * + * @param e the element to be associated with the returned entry + * @param n the count to be associated with the returned entry + * @throws IllegalArgumentException if {@code n} is negative + */ + public static Multiset.Entry immutableEntry( + @ParametricNullness E e, int n) { + return new ImmutableEntry(e, n); + } + + static class ImmutableEntry extends AbstractEntry implements Serializable { + @ParametricNullness private final E element; + private final int count; + + ImmutableEntry(@ParametricNullness E element, int count) { + this.element = element; + this.count = count; + checkNonnegative(count, "count"); + } + + @Override + @ParametricNullness + public final E getElement() { + return element; + } + + @Override + public final int getCount() { + return count; + } + + public ImmutableEntry nextInBucket() { + return null; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a view of the elements of {@code unfiltered} that satisfy a predicate. The returned + * multiset is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting multiset's iterators, and those of its {@code entrySet()} and {@code + * elementSet()}, do not support {@code remove()}. However, all other multiset methods supported + * by {@code unfiltered} are supported by the returned multiset. When given an element that + * doesn't satisfy the predicate, the multiset's {@code add()} and {@code addAll()} methods throw + * an {@link IllegalArgumentException}. When methods such as {@code removeAll()} and {@code + * clear()} are called on the filtered multiset, only elements that satisfy the filter will be + * removed from the underlying multiset. + * + *

The returned multiset isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered multiset's methods, such as {@code size()}, iterate across every + * element in the underlying multiset and determine which elements satisfy the filter. When a live + * view is not needed, it may be faster to copy the returned multiset and use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link + * Iterables#filter(Iterable, Class)} for related functionality.) + * + * @since 14.0 + */ + public static Multiset filter( + Multiset unfiltered, Predicate predicate) { + if (unfiltered instanceof FilteredMultiset) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + FilteredMultiset filtered = (FilteredMultiset) unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredMultiset(filtered.unfiltered, combinedPredicate); + } + return new FilteredMultiset(unfiltered, predicate); + } + + private static final class FilteredMultiset extends ViewMultiset { + final Multiset unfiltered; + final Predicate predicate; + + FilteredMultiset(Multiset unfiltered, Predicate predicate) { + this.unfiltered = checkNotNull(unfiltered); + this.predicate = checkNotNull(predicate); + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.filter(unfiltered.iterator(), predicate); + } + + @Override + Set createElementSet() { + return Sets.filter(unfiltered.elementSet(), predicate); + } + + @Override + Iterator elementIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Set> createEntrySet() { + return Sets.filter( + unfiltered.entrySet(), + new Predicate>() { + @Override + public boolean apply(Entry entry) { + return predicate.apply(entry.getElement()); + } + }); + } + + @Override + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + @Override + public int count(Object element) { + int count = unfiltered.count(element); + if (count > 0) { + @SuppressWarnings("unchecked") // element is equal to an E + E e = (E) element; + return predicate.apply(e) ? count : 0; + } + return 0; + } + + @Override + public int add(@ParametricNullness E element, int occurrences) { + checkArgument( + predicate.apply(element), "Element %s does not match predicate %s", element, predicate); + return unfiltered.add(element, occurrences); + } + + @Override + public int remove(Object element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } else { + return contains(element) ? unfiltered.remove(element, occurrences) : 0; + } + } + } + + /** + * Returns the expected number of distinct elements given the specified elements. The number of + * distinct elements is only computed if {@code elements} is an instance of {@code Multiset}; + * otherwise the default value of 11 is returned. + */ + static int inferDistinctElements(Iterable elements) { + if (elements instanceof Multiset) { + return ((Multiset) elements).elementSet().size(); + } + return 11; // initial capacity will be rounded up to 16 + } + + /** + * Returns an unmodifiable view of the union of two multisets. In the returned multiset, the count + * of each element is the maximum of its counts in the two backing multisets. The iteration + * order of the returned multiset matches that of the element set of {@code multiset1} followed by + * the members of the element set of {@code multiset2} that are not contained in {@code + * multiset1}, with repeated occurrences of the same element appearing consecutively. + * + *

Results are undefined if {@code multiset1} and {@code multiset2} are based on different + * equivalence relations (as {@code HashMultiset} and {@code TreeMultiset} are). + * + * @since 14.0 + */ + public static Multiset union( + final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + return new ViewMultiset() { + @Override + public boolean contains(Object element) { + return multiset1.contains(element) || multiset2.contains(element); + } + + @Override + public boolean isEmpty() { + return multiset1.isEmpty() && multiset2.isEmpty(); + } + + @Override + public int count(Object element) { + return Math.max(multiset1.count(element), multiset2.count(element)); + } + + @Override + Set createElementSet() { + return Sets.union(multiset1.elementSet(), multiset2.elementSet()); + } + + @Override + Iterator elementIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + final Iterator> iterator2 = multiset2.entrySet().iterator(); + // TODO(lowasser): consider making the entries live views + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + if (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = Math.max(entry1.getCount(), multiset2.count(element)); + return immutableEntry(element, count); + } + while (iterator2.hasNext()) { + Entry entry2 = iterator2.next(); + E element = entry2.getElement(); + if (!multiset1.contains(element)) { + return immutableEntry(element, entry2.getCount()); + } + } + return endOfData(); + } + }; + } + }; + } + + /** + * Returns an unmodifiable view of the intersection of two multisets. In the returned multiset, + * the count of each element is the minimum of its counts in the two backing multisets, + * with elements that would have a count of 0 not included. The iteration order of the returned + * multiset matches that of the element set of {@code multiset1}, with repeated occurrences of the + * same element appearing consecutively. + * + *

Results are undefined if {@code multiset1} and {@code multiset2} are based on different + * equivalence relations (as {@code HashMultiset} and {@code TreeMultiset} are). + * + * @since 2.0 + */ + public static Multiset intersection( + final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + return new ViewMultiset() { + @Override + public int count(Object element) { + int count1 = multiset1.count(element); + return (count1 == 0) ? 0 : Math.min(count1, multiset2.count(element)); + } + + @Override + Set createElementSet() { + return Sets.intersection(multiset1.elementSet(), multiset2.elementSet()); + } + + @Override + Iterator elementIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + // TODO(lowasser): consider making the entries live views + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + while (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = Math.min(entry1.getCount(), multiset2.count(element)); + if (count > 0) { + return immutableEntry(element, count); + } + } + return endOfData(); + } + }; + } + }; + } + + /** + * Returns an unmodifiable view of the sum of two multisets. In the returned multiset, the count + * of each element is the sum of its counts in the two backing multisets. The iteration + * order of the returned multiset matches that of the element set of {@code multiset1} followed by + * the members of the element set of {@code multiset2} that are not contained in {@code + * multiset1}, with repeated occurrences of the same element appearing consecutively. + * + *

Results are undefined if {@code multiset1} and {@code multiset2} are based on different + * equivalence relations (as {@code HashMultiset} and {@code TreeMultiset} are). + * + * @since 14.0 + */ + public static Multiset sum( + final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + // TODO(lowasser): consider making the entries live views + return new ViewMultiset() { + @Override + public boolean contains(Object element) { + return multiset1.contains(element) || multiset2.contains(element); + } + + @Override + public boolean isEmpty() { + return multiset1.isEmpty() && multiset2.isEmpty(); + } + + @Override + public int size() { + return IntMath.saturatedAdd(multiset1.size(), multiset2.size()); + } + + @Override + public int count(Object element) { + return multiset1.count(element) + multiset2.count(element); + } + + @Override + Set createElementSet() { + return Sets.union(multiset1.elementSet(), multiset2.elementSet()); + } + + @Override + Iterator elementIterator() { + throw new AssertionError("should never be called"); + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + final Iterator> iterator2 = multiset2.entrySet().iterator(); + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + if (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = entry1.getCount() + multiset2.count(element); + return immutableEntry(element, count); + } + while (iterator2.hasNext()) { + Entry entry2 = iterator2.next(); + E element = entry2.getElement(); + if (!multiset1.contains(element)) { + return immutableEntry(element, entry2.getCount()); + } + } + return endOfData(); + } + }; + } + }; + } + + /** + * Returns an unmodifiable view of the difference of two multisets. In the returned multiset, the + * count of each element is the result of the zero-truncated subtraction of its count in + * the second multiset from its count in the first multiset, with elements that would have a count + * of 0 not included. The iteration order of the returned multiset matches that of the element set + * of {@code multiset1}, with repeated occurrences of the same element appearing consecutively. + * + *

Results are undefined if {@code multiset1} and {@code multiset2} are based on different + * equivalence relations (as {@code HashMultiset} and {@code TreeMultiset} are). + * + * @since 14.0 + */ + public static Multiset difference( + final Multiset multiset1, final Multiset multiset2) { + checkNotNull(multiset1); + checkNotNull(multiset2); + + // TODO(lowasser): consider making the entries live views + return new ViewMultiset() { + @Override + public int count(Object element) { + int count1 = multiset1.count(element); + return (count1 == 0) ? 0 : Math.max(0, count1 - multiset2.count(element)); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + Iterator elementIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + return new AbstractIterator() { + @Override + protected E computeNext() { + while (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + if (entry1.getCount() > multiset2.count(element)) { + return element; + } + } + return endOfData(); + } + }; + } + + @Override + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + return new AbstractIterator>() { + @Override + protected Entry computeNext() { + while (iterator1.hasNext()) { + Entry entry1 = iterator1.next(); + E element = entry1.getElement(); + int count = entry1.getCount() - multiset2.count(element); + if (count > 0) { + return immutableEntry(element, count); + } + } + return endOfData(); + } + }; + } + + @Override + int distinctElements() { + return Iterators.size(entryIterator()); + } + }; + } + + /** + * Returns {@code true} if {@code subMultiset.count(o) <= superMultiset.count(o)} for all {@code + * o}. + * + * @since 10.0 + */ + public static boolean containsOccurrences(Multiset superMultiset, Multiset subMultiset) { + checkNotNull(superMultiset); + checkNotNull(subMultiset); + for (Entry entry : subMultiset.entrySet()) { + int superCount = superMultiset.count(entry.getElement()); + if (superCount < entry.getCount()) { + return false; + } + } + return true; + } + + /** + * Modifies {@code multisetToModify} so that its count for an element {@code e} is at most {@code + * multisetToRetain.count(e)}. + * + *

To be precise, {@code multisetToModify.count(e)} is set to {@code + * Math.min(multisetToModify.count(e), multisetToRetain.count(e))}. This is similar to {@link + * #intersection(Multiset, Multiset) intersection} {@code (multisetToModify, multisetToRetain)}, + * but mutates {@code multisetToModify} instead of returning a view. + * + *

In contrast, {@code multisetToModify.retainAll(multisetToRetain)} keeps all occurrences of + * elements that appear at all in {@code multisetToRetain}, and deletes all occurrences of all + * other elements. + * + * @return {@code true} if {@code multisetToModify} was changed as a result of this operation + * @since 10.0 + */ + public static boolean retainOccurrences( + Multiset multisetToModify, Multiset multisetToRetain) { + return retainOccurrencesImpl(multisetToModify, multisetToRetain); + } + + /** Delegate implementation which cares about the element type. */ + private static boolean retainOccurrencesImpl( + Multiset multisetToModify, Multiset occurrencesToRetain) { + checkNotNull(multisetToModify); + checkNotNull(occurrencesToRetain); + // Avoiding ConcurrentModificationExceptions is tricky. + Iterator> entryIterator = multisetToModify.entrySet().iterator(); + boolean changed = false; + while (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + int retainCount = occurrencesToRetain.count(entry.getElement()); + if (retainCount == 0) { + entryIterator.remove(); + changed = true; + } else if (retainCount < entry.getCount()) { + multisetToModify.setCount(entry.getElement(), retainCount); + changed = true; + } + } + return changed; + } + + /** + * For each occurrence of an element {@code e} in {@code occurrencesToRemove}, removes one + * occurrence of {@code e} in {@code multisetToModify}. + * + *

Equivalently, this method modifies {@code multisetToModify} so that {@code + * multisetToModify.count(e)} is set to {@code Math.max(0, multisetToModify.count(e) - + * Iterables.frequency(occurrencesToRemove, e))}. + * + *

This is not the same as {@code multisetToModify.} {@link Multiset#removeAll + * removeAll}{@code (occurrencesToRemove)}, which removes all occurrences of elements that appear + * in {@code occurrencesToRemove}. However, this operation is equivalent to, albeit + * sometimes more efficient than, the following: + * + *

{@code
+   * for (E e : occurrencesToRemove) {
+   *   multisetToModify.remove(e);
+   * }
+   * }
+ * + * @return {@code true} if {@code multisetToModify} was changed as a result of this operation + * @since 18.0 (present in 10.0 with a requirement that the second parameter be a {@code + * Multiset}) + */ + public static boolean removeOccurrences( + Multiset multisetToModify, Iterable occurrencesToRemove) { + if (occurrencesToRemove instanceof Multiset) { + return removeOccurrences(multisetToModify, (Multiset) occurrencesToRemove); + } else { + checkNotNull(multisetToModify); + checkNotNull(occurrencesToRemove); + boolean changed = false; + for (Object o : occurrencesToRemove) { + changed |= multisetToModify.remove(o); + } + return changed; + } + } + + /** + * For each occurrence of an element {@code e} in {@code occurrencesToRemove}, removes one + * occurrence of {@code e} in {@code multisetToModify}. + * + *

Equivalently, this method modifies {@code multisetToModify} so that {@code + * multisetToModify.count(e)} is set to {@code Math.max(0, multisetToModify.count(e) - + * occurrencesToRemove.count(e))}. + * + *

This is not the same as {@code multisetToModify.} {@link Multiset#removeAll + * removeAll}{@code (occurrencesToRemove)}, which removes all occurrences of elements that appear + * in {@code occurrencesToRemove}. However, this operation is equivalent to, albeit + * sometimes more efficient than, the following: + * + *

{@code
+   * for (E e : occurrencesToRemove) {
+   *   multisetToModify.remove(e);
+   * }
+   * }
+ * + * @return {@code true} if {@code multisetToModify} was changed as a result of this operation + * @since 10.0 (missing in 18.0 when only the overload taking an {@code Iterable} was present) + */ + public static boolean removeOccurrences( + Multiset multisetToModify, Multiset occurrencesToRemove) { + checkNotNull(multisetToModify); + checkNotNull(occurrencesToRemove); + + boolean changed = false; + Iterator> entryIterator = multisetToModify.entrySet().iterator(); + while (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + int removeCount = occurrencesToRemove.count(entry.getElement()); + if (removeCount >= entry.getCount()) { + entryIterator.remove(); + changed = true; + } else if (removeCount > 0) { + multisetToModify.remove(entry.getElement(), removeCount); + changed = true; + } + } + return changed; + } + + /** + * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} methods of {@link + * Multiset.Entry}. + */ + abstract static class AbstractEntry implements Multiset.Entry { + /** + * Indicates whether an object equals this entry, following the behavior specified in {@link + * Multiset.Entry#equals}. + */ + @Override + public boolean equals(Object object) { + if (object instanceof Multiset.Entry) { + Multiset.Entry that = (Multiset.Entry) object; + return this.getCount() == that.getCount() + && Objects.equal(this.getElement(), that.getElement()); + } + return false; + } + + /** + * Return this entry's hash code, following the behavior specified in {@link + * Multiset.Entry#hashCode}. + */ + @Override + public int hashCode() { + E e = getElement(); + return ((e == null) ? 0 : e.hashCode()) ^ getCount(); + } + + /** + * Returns a string representation of this multiset entry. The string representation consists of + * the associated element if the associated count is one, and otherwise the associated element + * followed by the characters " x " (space, x and space) followed by the count. Elements and + * counts are converted to strings as by {@code String.valueOf}. + */ + @Override + public String toString() { + String text = String.valueOf(getElement()); + int n = getCount(); + return (n == 1) ? text : (text + " x " + n); + } + } + + /** An implementation of {@link Multiset#equals}. */ + static boolean equalsImpl(Multiset multiset, Object object) { + if (object == multiset) { + return true; + } + if (object instanceof Multiset) { + Multiset that = (Multiset) object; + /* + * We can't simply check whether the entry sets are equal, since that + * approach fails when a TreeMultiset has a comparator that returns 0 + * when passed unequal elements. + */ + + if (multiset.size() != that.size() || multiset.entrySet().size() != that.entrySet().size()) { + return false; + } + for (Entry entry : that.entrySet()) { + if (multiset.count(entry.getElement()) != entry.getCount()) { + return false; + } + } + return true; + } + return false; + } + + /** An implementation of {@link Multiset#addAll}. */ + static boolean addAllImpl(Multiset self, Collection elements) { + checkNotNull(self); + checkNotNull(elements); + if (elements instanceof Multiset) { + return addAllImpl(self, cast(elements)); + } else if (elements.isEmpty()) { + return false; + } else { + return Iterators.addAll(self, elements.iterator()); + } + } + + /** A specialization of {@code addAllImpl} for when {@code elements} is itself a Multiset. */ + private static boolean addAllImpl( + Multiset self, Multiset elements) { + if (elements.isEmpty()) { + return false; + } + elements.forEachEntry(self::add); + return true; + } + + /** An implementation of {@link Multiset#removeAll}. */ + static boolean removeAllImpl(Multiset self, Collection elementsToRemove) { + Collection collection = + (elementsToRemove instanceof Multiset) + ? ((Multiset) elementsToRemove).elementSet() + : elementsToRemove; + + return self.elementSet().removeAll(collection); + } + + /** An implementation of {@link Multiset#retainAll}. */ + static boolean retainAllImpl(Multiset self, Collection elementsToRetain) { + checkNotNull(elementsToRetain); + Collection collection = + (elementsToRetain instanceof Multiset) + ? ((Multiset) elementsToRetain).elementSet() + : elementsToRetain; + + return self.elementSet().retainAll(collection); + } + + /** An implementation of {@link Multiset#setCount(Object, int)}. */ + static int setCountImpl( + Multiset self, @ParametricNullness E element, int count) { + checkNonnegative(count, "count"); + + int oldCount = self.count(element); + + int delta = count - oldCount; + if (delta > 0) { + self.add(element, delta); + } else if (delta < 0) { + self.remove(element, -delta); + } + + return oldCount; + } + + /** An implementation of {@link Multiset#setCount(Object, int, int)}. */ + static boolean setCountImpl( + Multiset self, @ParametricNullness E element, int oldCount, int newCount) { + checkNonnegative(oldCount, "oldCount"); + checkNonnegative(newCount, "newCount"); + + if (self.count(element) == oldCount) { + self.setCount(element, newCount); + return true; + } else { + return false; + } + } + + static Iterator elementIterator(Iterator> entryIterator) { + return new TransformedIterator, E>(entryIterator) { + @Override + @ParametricNullness + E transform(Entry entry) { + return entry.getElement(); + } + }; + } + + abstract static class ElementSet extends Sets.ImprovedAbstractSet { + abstract Multiset multiset(); + + @Override + public void clear() { + multiset().clear(); + } + + @Override + public boolean contains(Object o) { + return multiset().contains(o); + } + + @Override + public boolean containsAll(Collection c) { + return multiset().containsAll(c); + } + + @Override + public boolean isEmpty() { + return multiset().isEmpty(); + } + + @Override + public abstract Iterator iterator(); + + @Override + public boolean remove(Object o) { + return multiset().remove(o, Integer.MAX_VALUE) > 0; + } + + @Override + public int size() { + return multiset().entrySet().size(); + } + } + + abstract static class EntrySet extends Sets.ImprovedAbstractSet> { + abstract Multiset multiset(); + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + /* + * The GWT compiler wrongly issues a warning here. + */ + @SuppressWarnings("cast") + Entry entry = (Entry) o; + if (entry.getCount() <= 0) { + return false; + } + int count = multiset().count(entry.getElement()); + return count == entry.getCount(); + } + return false; + } + + // GWT compiler warning; see contains(). + @SuppressWarnings("cast") + @Override + public boolean remove(Object object) { + if (object instanceof Multiset.Entry) { + Entry entry = (Entry) object; + Object element = entry.getElement(); + int entryCount = entry.getCount(); + if (entryCount != 0) { + // Safe as long as we never add a new entry, which we won't. + // (Presumably it can still throw CCE/NPE but only if the underlying Multiset does.) + @SuppressWarnings({"unchecked", "nullness"}) + Multiset multiset = (Multiset) multiset(); + return multiset.setCount(element, entryCount, 0); + } + } + return false; + } + + @Override + public void clear() { + multiset().clear(); + } + } + + /** An implementation of {@link Multiset#iterator}. */ + static Iterator iteratorImpl(Multiset multiset) { + return new MultisetIteratorImpl(multiset, multiset.entrySet().iterator()); + } + + static final class MultisetIteratorImpl implements Iterator { + private final Multiset multiset; + private final Iterator> entryIterator; + private Entry currentEntry; + + /** Count of subsequent elements equal to current element */ + private int laterCount; + + /** Count of all elements equal to current element */ + private int totalCount; + + private boolean canRemove; + + MultisetIteratorImpl(Multiset multiset, Iterator> entryIterator) { + this.multiset = multiset; + this.entryIterator = entryIterator; + } + + @Override + public boolean hasNext() { + return laterCount > 0 || entryIterator.hasNext(); + } + + @Override + @ParametricNullness + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + if (laterCount == 0) { + currentEntry = entryIterator.next(); + totalCount = laterCount = currentEntry.getCount(); + } + laterCount--; + canRemove = true; + /* + * requireNonNull is safe because laterCount starts at 0, forcing us to initialize + * currentEntry above. After that, we never clear it. + */ + return requireNonNull(currentEntry).getElement(); + } + + @Override + public void remove() { + checkRemove(canRemove); + if (totalCount == 1) { + entryIterator.remove(); + } else { + /* + * requireNonNull is safe because canRemove is set to true only after we initialize + * currentEntry (which we never subsequently clear). + */ + multiset.remove(requireNonNull(currentEntry).getElement()); + } + totalCount--; + canRemove = false; + } + } + + static Spliterator spliteratorImpl(Multiset multiset) { + Spliterator> entrySpliterator = multiset.entrySet().spliterator(); + return CollectSpliterators.flatMap( + entrySpliterator, + entry -> Collections.nCopies(entry.getCount(), entry.getElement()).spliterator(), + Spliterator.SIZED + | (entrySpliterator.characteristics() + & (Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.IMMUTABLE)), + multiset.size()); + } + + /** An implementation of {@link Multiset#size}. */ + static int linearTimeSizeImpl(Multiset multiset) { + long size = 0; + for (Entry entry : multiset.entrySet()) { + size += entry.getCount(); + } + return Ints.saturatedCast(size); + } + + /** Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 */ + static Multiset cast(Iterable iterable) { + return (Multiset) iterable; + } + + /** + * Returns a copy of {@code multiset} as an {@link ImmutableMultiset} whose iteration order puts + * the highest count first, with ties broken by the iteration order of the original multiset. + * + * @since 11.0 + */ + public static ImmutableMultiset copyHighestCountFirst(Multiset multiset) { + Entry[] entries = (Entry[]) multiset.entrySet().toArray(new Entry[0]); + Arrays.sort(entries, DecreasingCount.INSTANCE); + return ImmutableMultiset.copyFromEntries(Arrays.asList(entries)); + } + + private static final class DecreasingCount implements Comparator> { + static final DecreasingCount INSTANCE = new DecreasingCount(); + + @Override + public int compare(Entry entry1, Entry entry2) { + return entry2.getCount() - entry1.getCount(); // subtracting two nonnegative integers + } + } + + /** + * An {@link AbstractMultiset} with additional default implementations, some of them linear-time + * implementations in terms of {@code elementSet} and {@code entrySet}. + */ + private abstract static class ViewMultiset extends AbstractMultiset { + @Override + public int size() { + return linearTimeSizeImpl(this); + } + + @Override + public void clear() { + elementSet().clear(); + } + + @Override + public Iterator iterator() { + return iteratorImpl(this); + } + + @Override + int distinctElements() { + return elementSet().size(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MutableClassToInstanceMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MutableClassToInstanceMap.java new file mode 100644 index 0000000000000..3af5c40b3f593 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/MutableClassToInstanceMap.java @@ -0,0 +1,203 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.Primitives; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Spliterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A mutable class-to-instance map backed by an arbitrary user-provided map. See also {@link + * ImmutableClassToInstanceMap}. + * + *

See the Guava User Guide article on {@code + * ClassToInstanceMap}. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@J2ktIncompatible +@GwtIncompatible +@SuppressWarnings("serial") // using writeReplace instead of standard serialization +@ElementTypesAreNonnullByDefault +public final class MutableClassToInstanceMap + extends ForwardingMap, B> implements ClassToInstanceMap, Serializable { + + /** + * Returns a new {@code MutableClassToInstanceMap} instance backed by a {@link HashMap} using the + * default initial capacity and load factor. + */ + public static MutableClassToInstanceMap create() { + return new MutableClassToInstanceMap(new HashMap, B>()); + } + + /** + * Returns a new {@code MutableClassToInstanceMap} instance backed by a given empty {@code + * backingMap}. The caller surrenders control of the backing map, and thus should not allow any + * direct references to it to remain accessible. + */ + public static MutableClassToInstanceMap create( + Map, B> backingMap) { + return new MutableClassToInstanceMap(backingMap); + } + + private final Map, B> delegate; + + private MutableClassToInstanceMap(Map, B> delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + protected Map, B> delegate() { + return delegate; + } + + /** + * Wraps the {@code setValue} implementation of an {@code Entry} to enforce the class constraint. + */ + private static Entry, B> checkedEntry( + final Entry, B> entry) { + return new ForwardingMapEntry, B>() { + @Override + protected Entry, B> delegate() { + return entry; + } + + @Override + @ParametricNullness + public B setValue(@ParametricNullness B value) { + cast(getKey(), value); + return super.setValue(value); + } + }; + } + + @Override + public Set, B>> entrySet() { + return new ForwardingSet, B>>() { + + @Override + protected Set, B>> delegate() { + return MutableClassToInstanceMap.this.delegate().entrySet(); + } + + @Override + public Spliterator, B>> spliterator() { + return CollectSpliterators.map( + delegate().spliterator(), MutableClassToInstanceMap::checkedEntry); + } + + @Override + public Iterator, B>> iterator() { + return new TransformedIterator, B>, Entry, B>>( + delegate().iterator()) { + @Override + Entry, B> transform(Entry, B> from) { + return checkedEntry(from); + } + }; + } + + @Override + public Object[] toArray() { + /* + * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it + * can be used with collections that may contain null. This collection is a collection of + * non-null Entry objects (Entry objects that might contain null values but are not + * themselves null), so we can treat it as a plain `Object[]`. + */ + @SuppressWarnings("nullness") + Object[] result = standardToArray(); + return result; + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return standardToArray(array); + } + }; + } + + @Override + public B put(Class key, @ParametricNullness B value) { + cast(key, value); + return super.put(key, value); + } + + @Override + public void putAll(Map, ? extends B> map) { + Map, B> copy = new LinkedHashMap<>(map); + for (Entry, B> entry : copy.entrySet()) { + cast(entry.getKey(), entry.getValue()); + } + super.putAll(copy); + } + + @Override + public T putInstance(Class type, @ParametricNullness T value) { + return cast(type, put(type, value)); + } + + @Override + public T getInstance(Class type) { + return cast(type, get(type)); + } + + private static T cast(Class type, Object value) { + return Primitives.wrap(type).cast(value); + } + + private Object writeReplace() { + return new SerializedForm(delegate()); + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** Serialized form of the map, to avoid serializing the constraint. */ + private static final class SerializedForm implements Serializable { + private final Map, B> backingMap; + + SerializedForm(Map, B> backingMap) { + this.backingMap = backingMap; + } + + Object readResolve() { + return create(backingMap); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NaturalOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NaturalOrdering.java new file mode 100644 index 0000000000000..dfeb4f56a651d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NaturalOrdering.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** An ordering that uses the natural order of the values. */ +@GwtCompatible(serializable = true) +@SuppressWarnings({"unchecked", "rawtypes"}) // TODO(kevinb): the right way to explain this?? +@ElementTypesAreNonnullByDefault +final class NaturalOrdering extends Ordering> implements Serializable { + static final NaturalOrdering INSTANCE = new NaturalOrdering(); + + // TODO: b/287198172 - Consider eagerly initializing these (but think about serialization). + private transient Ordering> nullsFirst; + private transient Ordering> nullsLast; + + @Override + public int compare(Comparable left, Comparable right) { + checkNotNull(left); // for GWT + checkNotNull(right); + return ((Comparable) left).compareTo(right); + } + + @Override + public > Ordering nullsFirst() { + Ordering> result = nullsFirst; + if (result == null) { + result = nullsFirst = super.>nullsFirst(); + } + return (Ordering) result; + } + + @Override + public > Ordering nullsLast() { + Ordering> result = nullsLast; + if (result == null) { + result = nullsLast = super.>nullsLast(); + } + return (Ordering) result; + } + + @Override + public > Ordering reverse() { + return (Ordering) ReverseNaturalOrdering.INSTANCE; + } + + // preserving singleton-ness gives equals()/hashCode() for free + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.natural()"; + } + + private NaturalOrdering() {} + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullnessCasts.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullnessCasts.java new file mode 100644 index 0000000000000..28e5137beefd9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullnessCasts.java @@ -0,0 +1,71 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class NullnessCasts { + /** + * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that + * that conversion is safe. + * + *

This method is intended to help with usages of type parameters that have {@linkplain + * ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null + * types (or if the type is a non-variable type, like {@code String}), then code should almost + * never use this method, preferring instead to call {@code requireNonNull} so as to benefit from + * its runtime check. + * + *

An example use case for this method is in implementing an {@code Iterator} whose {@code + * next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the + * code would be responsible for populating a "real" {@code T} (which might still be the value + * {@code null}!) before returning it to callers. Depending on how the code is structured, a + * nullness analysis might not understand that the field has been populated. To avoid that problem + * without having to add {@code @SuppressWarnings}, the code can call this method. + * + *

Why not just add {@code SuppressWarnings}? The problem is that this method is + * typically useful for {@code return} statements. That leaves the code with two options: Either + * add the suppression to the whole method (which turns off checking for a large section of code), + * or extract a variable, and put the suppression on that. However, a local variable typically + * doesn't work: Because nullness analyses typically infer the nullness of local variables, + * there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the + * analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}. + * (Even if supported added {@code @NonNull}, that would not help, since the problem case + * addressed by this method is the case in which {@code T} has parametric nullness -- and thus its + * value may be legitimately {@code null}.) + */ + @ParametricNullness + @SuppressWarnings("nullness") + static T uncheckedCastNullableTToT(T t) { + return t; + } + + /** Returns {@code null} as any type, even one that does not include {@code null}. */ + @SuppressWarnings({"nullness", "TypeParameterUnusedInFormals", "ReturnMissingNullable"}) + // The warnings are legitimate. Each time we use this method, we document why. + @ParametricNullness + static T unsafeNull() { + return null; + } + + private NullnessCasts() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullsFirstOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullsFirstOrdering.java new file mode 100644 index 0000000000000..1b1b67995a7fb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullsFirstOrdering.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** An ordering that treats {@code null} as less than all other values. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class NullsFirstOrdering extends Ordering implements Serializable { + final Ordering ordering; + + NullsFirstOrdering(Ordering ordering) { + this.ordering = ordering; + } + + @Override + public int compare(T left, T right) { + if (left == right) { + return 0; + } + if (left == null) { + return RIGHT_IS_GREATER; + } + if (right == null) { + return LEFT_IS_GREATER; + } + return ordering.compare(left, right); + } + + @Override + @SuppressWarnings("nullness") // should be safe, but not sure if we can avoid the warning + public Ordering reverse() { + // ordering.reverse() might be optimized, so let it do its thing + return ordering.reverse().nullsLast(); + } + + @SuppressWarnings("unchecked") // still need the right way to explain this + @Override + public Ordering nullsFirst() { + return (Ordering) this; + } + + @Override + public Ordering nullsLast() { + return ordering.nullsLast(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof NullsFirstOrdering) { + NullsFirstOrdering that = (NullsFirstOrdering) object; + return this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return ordering.hashCode() ^ 957692532; // meaningless + } + + @Override + public String toString() { + return ordering + ".nullsFirst()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullsLastOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullsLastOrdering.java new file mode 100644 index 0000000000000..c05dc609947fd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/NullsLastOrdering.java @@ -0,0 +1,91 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** An ordering that treats {@code null} as greater than all other values. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class NullsLastOrdering extends Ordering implements Serializable { + final Ordering ordering; + + NullsLastOrdering(Ordering ordering) { + this.ordering = ordering; + } + + @Override + public int compare(T left, T right) { + if (left == right) { + return 0; + } + if (left == null) { + return LEFT_IS_GREATER; + } + if (right == null) { + return RIGHT_IS_GREATER; + } + return ordering.compare(left, right); + } + + @Override + @SuppressWarnings("nullness") // should be safe, but not sure if we can avoid the warning + public Ordering reverse() { + // ordering.reverse() might be optimized, so let it do its thing + return ordering.reverse().nullsFirst(); + } + + @Override + public Ordering nullsFirst() { + return ordering.nullsFirst(); + } + + @SuppressWarnings("unchecked") // still need the right way to explain this + @Override + public Ordering nullsLast() { + return (Ordering) this; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof NullsLastOrdering) { + NullsLastOrdering that = (NullsLastOrdering) object; + return this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return ordering.hashCode() ^ -921210296; // meaningless + } + + @Override + public String toString() { + return ordering + ".nullsLast()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ObjectArrays.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ObjectArrays.java new file mode 100644 index 0000000000000..b40062f09cd42 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ObjectArrays.java @@ -0,0 +1,231 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; + +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to object arrays. + * + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +@SuppressWarnings("AvoidObjectArrays") +public final class ObjectArrays { + + private ObjectArrays() {} + + /** + * Returns a new array of the given length with the specified component type. + * + * @param type the component type + * @param length the length of the new array + */ + @GwtIncompatible // Array.newInstance(Class, int) + @SuppressWarnings("unchecked") + public static T[] newArray(Class type, int length) { + return (T[]) Array.newInstance(type, length); + } + + /** + * Returns a new array of the given length with the same type as a reference array. + * + * @param reference any array of the desired type + * @param length the length of the new array + */ + public static T[] newArray(T[] reference, int length) { + return Platform.newArray(reference, length); + } + + /** + * Returns a new array that contains the concatenated contents of two arrays. + * + * @param first the first array of elements to concatenate + * @param second the second array of elements to concatenate + * @param type the component type of the returned array + */ + @GwtIncompatible // Array.newInstance(Class, int) + public static T[] concat(T[] first, T[] second, Class type) { + T[] result = newArray(type, first.length + second.length); + System.arraycopy(first, 0, result, 0, first.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + + /** + * Returns a new array that prepends {@code element} to {@code array}. + * + * @param element the element to prepend to the front of {@code array} + * @param array the array of elements to append + * @return an array whose size is one larger than {@code array}, with {@code element} occupying + * the first position, and the elements of {@code array} occupying the remaining elements. + */ + public static T[] concat(@ParametricNullness T element, T[] array) { + T[] result = newArray(array, array.length + 1); + result[0] = element; + System.arraycopy(array, 0, result, 1, array.length); + return result; + } + + /** + * Returns a new array that appends {@code element} to {@code array}. + * + * @param array the array of elements to prepend + * @param element the element to append to the end + * @return an array whose size is one larger than {@code array}, with the same contents as {@code + * array}, plus {@code element} occupying the last position. + */ + public static T[] concat(T[] array, @ParametricNullness T element) { + T[] result = Arrays.copyOf(array, array.length + 1); + result[array.length] = element; + return result; + } + + /** + * Returns an array containing all of the elements in the specified collection; the runtime type + * of the returned array is that of the specified array. If the collection fits in the specified + * array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of the specified collection. + * + *

If the collection fits in the specified array with room to spare (i.e., the array has more + * elements than the collection), the element in the array immediately following the end of the + * collection is set to {@code null}. This is useful in determining the length of the collection + * only if the caller knows that the collection does not contain any null elements. + * + *

This method returns the elements in the order they are returned by the collection's + * iterator. + * + *

TODO(kevinb): support concurrently modified collections? + * + * @param c the collection for which to return an array of elements + * @param array the array in which to place the collection elements + * @throws ArrayStoreException if the runtime type of the specified array is not a supertype of + * the runtime type of every element in the specified collection + */ + static T[] toArrayImpl(Collection c, T[] array) { + int size = c.size(); + if (array.length < size) { + array = newArray(array, size); + } + fillArray(c, array); + if (array.length > size) { + Object[] unsoundlyCovariantArray = array; + unsoundlyCovariantArray[size] = null; + } + return array; + } + + /** + * Implementation of {@link Collection#toArray(Object[])} for collections backed by an object + * array. the runtime type of the returned array is that of the specified array. If the collection + * fits in the specified array, it is returned therein. Otherwise, a new array is allocated with + * the runtime type of the specified array and the size of the specified collection. + * + *

If the collection fits in the specified array with room to spare (i.e., the array has more + * elements than the collection), the element in the array immediately following the end of the + * collection is set to {@code null}. This is useful in determining the length of the collection + * only if the caller knows that the collection does not contain any null elements. + */ + static T[] toArrayImpl(Object[] src, int offset, int len, T[] dst) { + checkPositionIndexes(offset, offset + len, src.length); + if (dst.length < len) { + dst = newArray(dst, len); + } else if (dst.length > len) { + Object[] unsoundlyCovariantArray = dst; + unsoundlyCovariantArray[len] = null; + } + System.arraycopy(src, offset, dst, 0, len); + return dst; + } + + /** + * Returns an array containing all of the elements in the specified collection. This method + * returns the elements in the order they are returned by the collection's iterator. The returned + * array is "safe" in that no references to it are maintained by the collection. The caller is + * thus free to modify the returned array. + * + *

This method assumes that the collection size doesn't change while the method is running. + * + *

TODO(kevinb): support concurrently modified collections? + * + * @param c the collection for which to return an array of elements + */ + static Object[] toArrayImpl(Collection c) { + return fillArray(c, new Object[c.size()]); + } + + /** + * Returns a copy of the specified subrange of the specified array that is literally an Object[], + * and not e.g. a {@code String[]}. + */ + static Object[] copyAsObjectArray(Object[] elements, int offset, int length) { + checkPositionIndexes(offset, offset + length, elements.length); + if (length == 0) { + return new Object[0]; + } + Object[] result = new Object[length]; + System.arraycopy(elements, offset, result, 0, length); + return result; + } + + private static Object[] fillArray(Iterable elements, Object[] array) { + int i = 0; + for (Object element : elements) { + array[i++] = element; + } + return array; + } + + /** Swaps {@code array[i]} with {@code array[j]}. */ + static void swap(Object[] array, int i, int j) { + Object temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + static Object[] checkElementsNotNull(Object... array) { + return checkElementsNotNull(array, array.length); + } + + static Object[] checkElementsNotNull(Object[] array, int length) { + for (int i = 0; i < length; i++) { + checkElementNotNull(array[i], i); + } + return array; + } + + // We do this instead of Preconditions.checkNotNull to save boxing and array + // creation cost. + static Object checkElementNotNull(Object element, int index) { + if (element == null) { + throw new NullPointerException("at index " + index); + } + return element; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Ordering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Ordering.java new file mode 100644 index 0000000000000..36e169185dbd8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Ordering.java @@ -0,0 +1,972 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Function; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * A comparator, with additional methods to support common operations. This is an "enriched" version + * of {@code Comparator} for pre-Java-8 users, in the same sense that {@link FluentIterable} is an + * enriched {@link Iterable} for pre-Java-8 users. + * + *

Three types of methods

+ * + * Like other fluent types, there are three types of methods present: methods for acquiring, + * chaining, and using. + * + *

Acquiring

+ * + *

The common ways to get an instance of {@code Ordering} are: + * + *

    + *
  • Subclass it and implement {@link #compare} instead of implementing {@link Comparator} + * directly + *
  • Pass a pre-existing {@link Comparator} instance to {@link #from(Comparator)} + *
  • Use the natural ordering, {@link Ordering#natural} + *
+ * + *

Chaining

+ * + *

Then you can use the chaining methods to get an altered version of that {@code + * Ordering}, including: + * + *

    + *
  • {@link #reverse} + *
  • {@link #compound(Comparator)} + *
  • {@link #onResultOf(Function)} + *
  • {@link #nullsFirst} / {@link #nullsLast} + *
+ * + *

Using

+ * + *

Finally, use the resulting {@code Ordering} anywhere a {@link Comparator} is required, or use + * any of its special operations, such as: + * + *

    + *
  • {@link #immutableSortedCopy} + *
  • {@link #isOrdered} / {@link #isStrictlyOrdered} + *
  • {@link #min} / {@link #max} + *
+ * + *

Understanding complex orderings

+ * + *

Complex chained orderings like the following example can be challenging to understand. + * + *

{@code
+ * Ordering ordering =
+ *     Ordering.natural()
+ *         .nullsFirst()
+ *         .onResultOf(getBarFunction)
+ *         .nullsLast();
+ * }
+ * + * Note that each chaining method returns a new ordering instance which is backed by the previous + * instance, but has the chance to act on values before handing off to that backing instance. + * As a result, it usually helps to read chained ordering expressions backwards. For example, + * when {@code compare} is called on the above ordering: + * + *
    + *
  1. First, if only one {@code Foo} is null, that null value is treated as greater + *
  2. Next, non-null {@code Foo} values are passed to {@code getBarFunction} (we will be + * comparing {@code Bar} values from now on) + *
  3. Next, if only one {@code Bar} is null, that null value is treated as lesser + *
  4. Finally, natural ordering is used (i.e. the result of {@code Bar.compareTo(Bar)} is + * returned) + *
+ * + *

Alas, {@link #reverse} is a little different. As you read backwards through a chain and + * encounter a call to {@code reverse}, continue working backwards until a result is determined, and + * then reverse that result. + * + *

Additional notes

+ * + *

Except as noted, the orderings returned by the factory methods of this class are serializable + * if and only if the provided instances that back them are. For example, if {@code ordering} and + * {@code function} can themselves be serialized, then {@code ordering.onResultOf(function)} can as + * well. + * + *

For Java 8 users

+ * + *

If you are using Java 8, this class is now obsolete. Most of its functionality is now provided + * by {@link java.util.stream.Stream Stream} and by {@link Comparator} itself, and the rest can now + * be found as static methods in our new {@link Comparators} class. See each method below for + * further instructions. Whenever possible, you should change any references of type {@code + * Ordering} to be of type {@code Comparator} instead. However, at this time we have no plan to + * deprecate this class. + * + *

Many replacements involve adopting {@code Stream}, and these changes can sometimes make your + * code verbose. Whenever following this advice, you should check whether {@code Stream} could be + * adopted more comprehensively in your code; the end result may be quite a bit simpler. + * + *

See also

+ * + *

See the Guava User Guide article on {@code Ordering}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class Ordering implements Comparator { + // Natural order + + /** + * Returns a serializable ordering that uses the natural order of the values. The ordering throws + * a {@link NullPointerException} when passed a null parameter. + * + *

The type specification is {@code }, instead of the technically correct + * {@code >}, to support legacy types from before Java 5. + * + *

Java 8 users: use {@link Comparator#naturalOrder} instead. + */ + @GwtCompatible(serializable = true) + @SuppressWarnings("unchecked") // TODO(kevinb): right way to explain this?? + public static Ordering natural() { + return (Ordering) NaturalOrdering.INSTANCE; + } + + // Static factories + + /** + * Returns an ordering based on an existing comparator instance. Note that it is + * unnecessary to create a new anonymous inner class implementing {@code Comparator} just + * to pass it in here. Instead, simply subclass {@code Ordering} and implement its {@code compare} + * method directly. + * + *

The returned object is serializable if {@code comparator} is serializable. + * + *

Java 8 users: this class is now obsolete as explained in the class documentation, so + * there is no need to use this method. + * + * @param comparator the comparator that defines the order + * @return comparator itself if it is already an {@code Ordering}; otherwise an ordering that + * wraps that comparator + */ + @GwtCompatible(serializable = true) + public static Ordering from(Comparator comparator) { + return (comparator instanceof Ordering) + ? (Ordering) comparator + : new ComparatorOrdering(comparator); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + */ + @GwtCompatible(serializable = true) + @Deprecated + public static Ordering from(Ordering ordering) { + return checkNotNull(ordering); + } + + /** + * Returns an ordering that compares objects according to the order in which they appear in the + * given list. Only objects present in the list (according to {@link Object#equals}) may be + * compared. This comparator imposes a "partial ordering" over the type {@code T}. Subsequent + * changes to the {@code valuesInOrder} list will have no effect on the returned comparator. Null + * values in the list are not supported. + * + *

The returned comparator throws a {@link ClassCastException} when it receives an input + * parameter that isn't among the provided values. + * + *

The generated comparator is serializable if all the provided values are serializable. + * + * @param valuesInOrder the values that the returned comparator will be able to compare, in the + * order the comparator should induce + * @return the comparator described above + * @throws NullPointerException if any of the provided values is null + * @throws IllegalArgumentException if {@code valuesInOrder} contains any duplicate values + * (according to {@link Object#equals}) + */ + // TODO(kevinb): provide replacement + @GwtCompatible(serializable = true) + public static Ordering explicit(List valuesInOrder) { + return new ExplicitOrdering(valuesInOrder); + } + + /** + * Returns an ordering that compares objects according to the order in which they are given to + * this method. Only objects present in the argument list (according to {@link Object#equals}) may + * be compared. This comparator imposes a "partial ordering" over the type {@code T}. Null values + * in the argument list are not supported. + * + *

The returned comparator throws a {@link ClassCastException} when it receives an input + * parameter that isn't among the provided values. + * + *

The generated comparator is serializable if all the provided values are serializable. + * + * @param leastValue the value which the returned comparator should consider the "least" of all + * values + * @param remainingValuesInOrder the rest of the values that the returned comparator will be able + * to compare, in the order the comparator should follow + * @return the comparator described above + * @throws NullPointerException if any of the provided values is null + * @throws IllegalArgumentException if any duplicate values (according to {@link + * Object#equals(Object)}) are present among the method arguments + */ + // TODO(kevinb): provide replacement + @GwtCompatible(serializable = true) + public static Ordering explicit(T leastValue, T... remainingValuesInOrder) { + return explicit(Lists.asList(leastValue, remainingValuesInOrder)); + } + + // Ordering singletons + + /** + * Returns an ordering which treats all values as equal, indicating "no ordering." Passing this + * ordering to any stable sort algorithm results in no change to the order of elements. + * Note especially that {@link #sortedCopy} and {@link #immutableSortedCopy} are stable, and in + * the returned instance these are implemented by simply copying the source list. + * + *

Example: + * + *

{@code
+   * Ordering.allEqual().nullsLast().sortedCopy(
+   *     asList(t, null, e, s, null, t, null))
+   * }
+ * + *

Assuming {@code t}, {@code e} and {@code s} are non-null, this returns {@code [t, e, s, t, + * null, null, null]} regardless of the true comparison order of those three values (which might + * not even implement {@link Comparable} at all). + * + *

Warning: by definition, this comparator is not consistent with equals (as + * defined {@linkplain Comparator here}). Avoid its use in APIs, such as {@link + * TreeSet#TreeSet(Comparator)}, where such consistency is expected. + * + *

The returned comparator is serializable. + * + *

Java 8 users: Use the lambda expression {@code (a, b) -> 0} instead (in certain cases + * you may need to cast that to {@code Comparator}). + * + * @since 13.0 + */ + @GwtCompatible(serializable = true) + @SuppressWarnings("unchecked") + public static Ordering allEqual() { + return AllEqualOrdering.INSTANCE; + } + + /** + * Returns an ordering that compares objects by the natural ordering of their string + * representations as returned by {@code toString()}. It does not support null values. + * + *

The comparator is serializable. + * + *

Java 8 users: Use {@code Comparator.comparing(Object::toString)} instead. + */ + @GwtCompatible(serializable = true) + public static Ordering usingToString() { + return UsingToStringOrdering.INSTANCE; + } + + /** + * Returns an arbitrary ordering over all objects, for which {@code compare(a, b) == 0} implies + * {@code a == b} (identity equality). There is no meaning whatsoever to the order imposed, but it + * is constant for the life of the VM. + * + *

Because the ordering is identity-based, it is not "consistent with {@link + * Object#equals(Object)}" as defined by {@link Comparator}. Use caution when building a {@link + * SortedSet} or {@link SortedMap} from it, as the resulting collection will not behave exactly + * according to spec. + * + *

This ordering is not serializable, as its implementation relies on {@link + * System#identityHashCode(Object)}, so its behavior cannot be preserved across serialization. + * + * @since 2.0 + */ + // TODO(kevinb): copy to Comparators, etc. + @J2ktIncompatible // MapMaker + public static Ordering arbitrary() { + return ArbitraryOrderingHolder.ARBITRARY_ORDERING; + } + + @J2ktIncompatible // MapMaker + private static class ArbitraryOrderingHolder { + static final Ordering ARBITRARY_ORDERING = new ArbitraryOrdering(); + } + + @J2ktIncompatible // MapMaker + @VisibleForTesting + static class ArbitraryOrdering extends Ordering { + + private final AtomicInteger counter = new AtomicInteger(0); + private final ConcurrentMap uids = + Platform.tryWeakKeys(new MapMaker()).makeMap(); + + private Integer getUid(Object obj) { + Integer uid = uids.get(obj); + if (uid == null) { + // One or more integer values could be skipped in the event of a race + // to generate a UID for the same object from multiple threads, but + // that shouldn't be a problem. + uid = counter.getAndIncrement(); + Integer alreadySet = uids.putIfAbsent(obj, uid); + if (alreadySet != null) { + uid = alreadySet; + } + } + return uid; + } + + @Override + public int compare(Object left, Object right) { + if (left == right) { + return 0; + } else if (left == null) { + return -1; + } else if (right == null) { + return 1; + } + int leftCode = identityHashCode(left); + int rightCode = identityHashCode(right); + if (leftCode != rightCode) { + return leftCode < rightCode ? -1 : 1; + } + + // identityHashCode collision (rare, but not as rare as you'd think) + int result = getUid(left).compareTo(getUid(right)); + if (result == 0) { + throw new AssertionError(); // extremely, extremely unlikely. + } + return result; + } + + @Override + public String toString() { + return "Ordering.arbitrary()"; + } + + /* + * We need to be able to mock identityHashCode() calls for tests, because it + * can take 1-10 seconds to find colliding objects. Mocking frameworks that + * can do magic to mock static method calls still can't do so for a system + * class, so we need the indirection. In production, Hotspot should still + * recognize that the call is 1-morphic and should still be willing to + * inline it if necessary. + */ + int identityHashCode(Object object) { + return System.identityHashCode(object); + } + } + + // Constructor + + /** + * Constructs a new instance of this class (only invokable by the subclass constructor, typically + * implicit). + */ + protected Ordering() {} + + // Instance-based factories (and any static equivalents) + + /** + * Returns the reverse of this ordering; the {@code Ordering} equivalent to {@link + * Collections#reverseOrder(Comparator)}. + * + *

Java 8 users: Use {@code thisComparator.reversed()} instead. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().reverse(); + @GwtCompatible(serializable = true) + public Ordering reverse() { + return new ReverseOrdering(this); + } + + /** + * Returns an ordering that treats {@code null} as less than all other values and uses {@code + * this} to compare non-null values. + * + *

The returned object is serializable if this object is serializable. + * + *

Java 8 users: Use {@code Comparator.nullsFirst(thisComparator)} instead. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().nullsFirst(); + @GwtCompatible(serializable = true) + public Ordering nullsFirst() { + return new NullsFirstOrdering(this); + } + + /** + * Returns an ordering that treats {@code null} as greater than all other values and uses this + * ordering to compare non-null values. + * + *

The returned object is serializable if this object is serializable. + * + *

Java 8 users: Use {@code Comparator.nullsLast(thisComparator)} instead. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().nullsLast(); + @GwtCompatible(serializable = true) + public Ordering nullsLast() { + return new NullsLastOrdering(this); + } + + /** + * Returns a new ordering on {@code F} which orders elements by first applying a function to them, + * then comparing those results using {@code this}. For example, to compare objects by their + * string forms, in a case-insensitive manner, use: + * + *

{@code
+   * Ordering.from(String.CASE_INSENSITIVE_ORDER)
+   *     .onResultOf(Functions.toStringFunction())
+   * }
+ * + *

Java 8 users: Use {@code Comparator.comparing(function, thisComparator)} instead (you + * can omit the comparator if it is the natural order). + */ + @GwtCompatible(serializable = true) + public Ordering onResultOf(Function function) { + return new ByFunctionOrdering<>(function, this); + } + + Ordering> onKeys() { + return onResultOf(Maps.keyFunction()); + } + + /** + * Returns an ordering which first uses the ordering {@code this}, but which in the event of a + * "tie", then delegates to {@code secondaryComparator}. For example, to sort a bug list first by + * status and second by priority, you might use {@code byStatus.compound(byPriority)}. For a + * compound ordering with three or more components, simply chain multiple calls to this method. + * + *

An ordering produced by this method, or a chain of calls to this method, is equivalent to + * one created using {@link Ordering#compound(Iterable)} on the same component comparators. + * + *

The returned object is serializable if this object and {@code secondaryComparator} are both + * serializable. + * + *

Java 8 users: Use {@code thisComparator.thenComparing(secondaryComparator)} instead. + * Depending on what {@code secondaryComparator} is, one of the other overloads of {@code + * thenComparing} may be even more useful. + */ + @GwtCompatible(serializable = true) + public Ordering compound(Comparator secondaryComparator) { + return new CompoundOrdering(this, checkNotNull(secondaryComparator)); + } + + /** + * Returns an ordering which tries each given comparator in order until a non-zero result is + * found, returning that result, and returning zero only if all comparators return zero. The + * returned ordering is based on the state of the {@code comparators} iterable at the time it was + * provided to this method. + * + *

The returned ordering is equivalent to that produced using {@code + * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. + * + *

The returned object is serializable if each of the {@code comparators} is serializable. + * + *

Warning: Supplying an argument with undefined iteration order, such as a {@link + * HashSet}, will produce non-deterministic results. + * + *

Java 8 users: Use a chain of calls to {@link Comparator#thenComparing(Comparator)}, + * or {@code comparatorCollection.stream().reduce(Comparator::thenComparing).get()} (if the + * collection might be empty, also provide a default comparator as the {@code identity} parameter + * to {@code reduce}). + * + * @param comparators the comparators to try in order + */ + @GwtCompatible(serializable = true) + public static Ordering compound( + Iterable> comparators) { + return new CompoundOrdering(comparators); + } + + /** + * Returns a new ordering which sorts iterables by comparing corresponding elements pairwise until + * a nonzero result is found; imposes "dictionary order". If the end of one iterable is reached, + * but not the other, the shorter iterable is considered to be less than the longer one. For + * example, a lexicographical natural ordering over integers considers {@code [] < [1] < [1, 1] < + * [1, 2] < [2]}. + * + *

Note that {@code ordering.lexicographical().reverse()} is not equivalent to {@code + * ordering.reverse().lexicographical()} (consider how each would order {@code [1]} and {@code [1, + * 1]}). + * + *

Java 8 users: Use {@link Comparators#lexicographical(Comparator)} instead. + * + * @since 2.0 + */ + @GwtCompatible(serializable = true) + // type parameter lets us avoid the extra in statements like: + // Ordering> o = + // Ordering.natural().lexicographical(); + public Ordering> lexicographical() { + /* + * Note that technically the returned ordering should be capable of + * handling not just {@code Iterable} instances, but also any {@code + * Iterable}. However, the need for this comes up so rarely + * that it doesn't justify making everyone else deal with the very ugly + * wildcard. + */ + return new LexicographicalOrdering(this); + } + + // Regular instance methods + + @Override + public abstract int compare(@ParametricNullness T left, @ParametricNullness T right); + + /** + * Returns the least of the specified values according to this ordering. If there are multiple + * least values, the first of those is returned. The iterator will be left exhausted: its {@code + * hasNext()} method will return {@code false}. + * + *

Java 8 users: Use {@code Streams.stream(iterator).min(thisComparator).get()} instead + * (but note that it does not guarantee which tied minimum element is returned). + * + * @param iterator the iterator whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + * @since 11.0 + */ + @ParametricNullness + public E min(Iterator iterator) { + // let this throw NoSuchElementException as necessary + E minSoFar = iterator.next(); + + while (iterator.hasNext()) { + minSoFar = min(minSoFar, iterator.next()); + } + + return minSoFar; + } + + /** + * Returns the least of the specified values according to this ordering. If there are multiple + * least values, the first of those is returned. + * + *

Java 8 users: If {@code iterable} is a {@link Collection}, use {@code + * Collections.min(collection, thisComparator)} instead. Otherwise, use {@code + * Streams.stream(iterable).min(thisComparator).get()} instead. Note that these alternatives do + * not guarantee which tied minimum element is returned. + * + * @param iterable the iterable whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + */ + @ParametricNullness + public E min(Iterable iterable) { + return min(iterable.iterator()); + } + + /** + * Returns the lesser of the two values according to this ordering. If the values compare as 0, + * the first is returned. + * + *

Implementation note: this method is invoked by the default implementations of the + * other {@code min} overloads, so overriding it will affect their behavior. + * + *

Note: Consider using {@code Comparators.min(a, b, thisComparator)} instead. If {@code + * thisComparator} is {@link Ordering#natural}, then use {@code Comparators.min(a, b)}. + * + * @param a value to compare, returned if less than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + */ + @ParametricNullness + public E min(@ParametricNullness E a, @ParametricNullness E b) { + return (compare(a, b) <= 0) ? a : b; + } + + /** + * Returns the least of the specified values according to this ordering. If there are multiple + * least values, the first of those is returned. + * + *

Java 8 users: Use {@code Collections.min(Arrays.asList(a, b, c...), thisComparator)} + * instead (but note that it does not guarantee which tied minimum element is returned). + * + * @param a value to compare, returned if less than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + */ + @ParametricNullness + public E min( + @ParametricNullness E a, @ParametricNullness E b, @ParametricNullness E c, E... rest) { + E minSoFar = min(min(a, b), c); + + for (E r : rest) { + minSoFar = min(minSoFar, r); + } + + return minSoFar; + } + + /** + * Returns the greatest of the specified values according to this ordering. If there are multiple + * greatest values, the first of those is returned. The iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. + * + *

Java 8 users: Use {@code Streams.stream(iterator).max(thisComparator).get()} instead + * (but note that it does not guarantee which tied maximum element is returned). + * + * @param iterator the iterator whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + * @since 11.0 + */ + @ParametricNullness + public E max(Iterator iterator) { + // let this throw NoSuchElementException as necessary + E maxSoFar = iterator.next(); + + while (iterator.hasNext()) { + maxSoFar = max(maxSoFar, iterator.next()); + } + + return maxSoFar; + } + + /** + * Returns the greatest of the specified values according to this ordering. If there are multiple + * greatest values, the first of those is returned. + * + *

Java 8 users: If {@code iterable} is a {@link Collection}, use {@code + * Collections.max(collection, thisComparator)} instead. Otherwise, use {@code + * Streams.stream(iterable).max(thisComparator).get()} instead. Note that these alternatives do + * not guarantee which tied maximum element is returned. + * + * @param iterable the iterable whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + */ + @ParametricNullness + public E max(Iterable iterable) { + return max(iterable.iterator()); + } + + /** + * Returns the greater of the two values according to this ordering. If the values compare as 0, + * the first is returned. + * + *

Implementation note: this method is invoked by the default implementations of the + * other {@code max} overloads, so overriding it will affect their behavior. + * + *

Note: Consider using {@code Comparators.max(a, b, thisComparator)} instead. If {@code + * thisComparator} is {@link Ordering#natural}, then use {@code Comparators.max(a, b)}. + * + * @param a value to compare, returned if greater than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + */ + @ParametricNullness + public E max(@ParametricNullness E a, @ParametricNullness E b) { + return (compare(a, b) >= 0) ? a : b; + } + + /** + * Returns the greatest of the specified values according to this ordering. If there are multiple + * greatest values, the first of those is returned. + * + *

Java 8 users: Use {@code Collections.max(Arrays.asList(a, b, c...), thisComparator)} + * instead (but note that it does not guarantee which tied maximum element is returned). + * + * @param a value to compare, returned if greater than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not mutually comparable under this + * ordering. + */ + @ParametricNullness + public E max( + @ParametricNullness E a, @ParametricNullness E b, @ParametricNullness E c, E... rest) { + E maxSoFar = max(max(a, b), c); + + for (E r : rest) { + maxSoFar = max(maxSoFar, r); + } + + return maxSoFar; + } + + /** + * Returns the {@code k} least elements of the given iterable according to this ordering, in order + * from least to greatest. If there are fewer than {@code k} elements present, all will be + * included. + * + *

The implementation does not necessarily use a stable sorting algorithm; when multiple + * elements are equivalent, it is undefined which will come first. + * + *

Java 8 users: Use {@code Streams.stream(iterable).collect(Comparators.least(k, + * thisComparator))} instead. + * + * @return an immutable {@code RandomAccess} list of the {@code k} least elements in ascending + * order + * @throws IllegalArgumentException if {@code k} is negative + * @since 8.0 + */ + public List leastOf(Iterable iterable, int k) { + if (iterable instanceof Collection) { + Collection collection = (Collection) iterable; + if (collection.size() <= 2L * k) { + // In this case, just dumping the collection to an array and sorting is + // faster than using the implementation for Iterator, which is + // specialized for k much smaller than n. + + @SuppressWarnings("unchecked") // c only contains E's and doesn't escape + E[] array = (E[]) collection.toArray(); + Arrays.sort(array, this); + if (array.length > k) { + array = Arrays.copyOf(array, k); + } + return Collections.unmodifiableList(Arrays.asList(array)); + } + } + return leastOf(iterable.iterator(), k); + } + + /** + * Returns the {@code k} least elements from the given iterator according to this ordering, in + * order from least to greatest. If there are fewer than {@code k} elements present, all will be + * included. + * + *

The implementation does not necessarily use a stable sorting algorithm; when multiple + * elements are equivalent, it is undefined which will come first. + * + *

Java 8 users: Use {@code Streams.stream(iterator).collect(Comparators.least(k, + * thisComparator))} instead. + * + * @return an immutable {@code RandomAccess} list of the {@code k} least elements in ascending + * order + * @throws IllegalArgumentException if {@code k} is negative + * @since 14.0 + */ + public List leastOf(Iterator iterator, int k) { + checkNotNull(iterator); + checkNonnegative(k, "k"); + + if (k == 0 || !iterator.hasNext()) { + return Collections.emptyList(); + } else if (k >= Integer.MAX_VALUE / 2) { + // k is really large; just do a straightforward sorted-copy-and-sublist + ArrayList list = Lists.newArrayList(iterator); + Collections.sort(list, this); + if (list.size() > k) { + list.subList(k, list.size()).clear(); + } + list.trimToSize(); + return Collections.unmodifiableList(list); + } else { + TopKSelector selector = TopKSelector.least(k, this); + selector.offerAll(iterator); + return selector.topK(); + } + } + + /** + * Returns the {@code k} greatest elements of the given iterable according to this ordering, in + * order from greatest to least. If there are fewer than {@code k} elements present, all will be + * included. + * + *

The implementation does not necessarily use a stable sorting algorithm; when multiple + * elements are equivalent, it is undefined which will come first. + * + *

Java 8 users: Use {@code Streams.stream(iterable).collect(Comparators.greatest(k, + * thisComparator))} instead. + * + * @return an immutable {@code RandomAccess} list of the {@code k} greatest elements in + * descending order + * @throws IllegalArgumentException if {@code k} is negative + * @since 8.0 + */ + public List greatestOf(Iterable iterable, int k) { + // TODO(kevinb): see if delegation is hurting performance noticeably + // TODO(kevinb): if we change this implementation, add full unit tests. + return this.reverse().leastOf(iterable, k); + } + + /** + * Returns the {@code k} greatest elements from the given iterator according to this ordering, in + * order from greatest to least. If there are fewer than {@code k} elements present, all will be + * included. + * + *

The implementation does not necessarily use a stable sorting algorithm; when multiple + * elements are equivalent, it is undefined which will come first. + * + *

Java 8 users: Use {@code Streams.stream(iterator).collect(Comparators.greatest(k, + * thisComparator))} instead. + * + * @return an immutable {@code RandomAccess} list of the {@code k} greatest elements in + * descending order + * @throws IllegalArgumentException if {@code k} is negative + * @since 14.0 + */ + public List greatestOf(Iterator iterator, int k) { + return this.reverse().leastOf(iterator, k); + } + + /** + * Returns a mutable list containing {@code elements} sorted by this ordering; use this + * only when the resulting list may need further modification, or may contain {@code null}. The + * input is not modified. The returned list is serializable and has random access. + * + *

Unlike {@link Sets#newTreeSet(Iterable)}, this method does not discard elements that are + * duplicates according to the comparator. The sort performed is stable, meaning that such + * elements will appear in the returned list in the same order they appeared in {@code elements}. + * + *

Performance note: According to our benchmarking on Open JDK 7, {@link + * #immutableSortedCopy} generally performs better (in both time and space) than this method, and + * this method in turn generally performs better than copying the list and calling {@link + * Collections#sort(List)}. + */ + // TODO(kevinb): rerun benchmarks including new options + public List sortedCopy(Iterable elements) { + @SuppressWarnings("unchecked") // does not escape, and contains only E's + E[] array = (E[]) Iterables.toArray(elements); + Arrays.sort(array, this); + return Lists.newArrayList(Arrays.asList(array)); + } + + /** + * Returns an immutable list containing {@code elements} sorted by this ordering. The input + * is not modified. + * + *

Unlike {@link Sets#newTreeSet(Iterable)}, this method does not discard elements that are + * duplicates according to the comparator. The sort performed is stable, meaning that such + * elements will appear in the returned list in the same order they appeared in {@code elements}. + * + *

Performance note: According to our benchmarking on Open JDK 7, this method is the + * most efficient way to make a sorted copy of a collection. + * + * @throws NullPointerException if any element of {@code elements} is {@code null} + * @since 3.0 + */ + // TODO(kevinb): rerun benchmarks including new options + public ImmutableList immutableSortedCopy(Iterable elements) { + return ImmutableList.sortedCopyOf(this, elements); + } + + /** + * Returns {@code true} if each element in {@code iterable} after the first is greater than or + * equal to the element that preceded it, according to this ordering. Note that this is always + * true when the iterable has fewer than two elements. + * + *

Java 8 users: Use the equivalent {@link Comparators#isInOrder(Iterable, Comparator)} + * instead, since the rest of {@code Ordering} is mostly obsolete (as explained in the class + * documentation). + */ + public boolean isOrdered(Iterable iterable) { + Iterator it = iterable.iterator(); + if (it.hasNext()) { + T prev = it.next(); + while (it.hasNext()) { + T next = it.next(); + if (compare(prev, next) > 0) { + return false; + } + prev = next; + } + } + return true; + } + + /** + * Returns {@code true} if each element in {@code iterable} after the first is strictly + * greater than the element that preceded it, according to this ordering. Note that this is always + * true when the iterable has fewer than two elements. + * + *

Java 8 users: Use the equivalent {@link Comparators#isInStrictOrder(Iterable, + * Comparator)} instead, since the rest of {@code Ordering} is mostly obsolete (as explained in + * the class documentation). + */ + public boolean isStrictlyOrdered(Iterable iterable) { + Iterator it = iterable.iterator(); + if (it.hasNext()) { + T prev = it.next(); + while (it.hasNext()) { + T next = it.next(); + if (compare(prev, next) >= 0) { + return false; + } + prev = next; + } + } + return true; + } + + /** + * {@link Collections#binarySearch(List, Object, Comparator) Searches} {@code sortedList} for + * {@code key} using the binary search algorithm. The list must be sorted using this ordering. + * + * @param sortedList the list to be searched + * @param key the key to be searched for + * @deprecated Use {@link Collections#binarySearch(List, Object, Comparator)} directly. + */ + @Deprecated + public int binarySearch(List sortedList, @ParametricNullness T key) { + return Collections.binarySearch(sortedList, key, this); + } + + /** + * Exception thrown by a {@link Ordering#explicit(List)} or {@link Ordering#explicit(Object, + * Object[])} comparator when comparing a value outside the set of values it can compare. + * Extending {@link ClassCastException} may seem odd, but it is required. + */ + @VisibleForTesting + static class IncomparableValueException extends ClassCastException { + final Object value; + + IncomparableValueException(Object value) { + super("Cannot compare value: " + value); + this.value = value; + } + + private static final long serialVersionUID = 0; + } + + // Never make these public + static final int LEFT_IS_GREATER = 1; + static final int RIGHT_IS_GREATER = -1; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ParametricNullness.java new file mode 100644 index 0000000000000..517202a39cff8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/PeekingIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/PeekingIterator.java new file mode 100644 index 0000000000000..f33efd32e7f1f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/PeekingIterator.java @@ -0,0 +1,74 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * An iterator that supports a one-element lookahead while iterating. + * + *

See the Guava User Guide article on {@code + * PeekingIterator}. + * + * @author Mick Killianey + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface PeekingIterator extends Iterator { + /** + * Returns the next element in the iteration, without advancing the iteration. + * + *

Calls to {@code peek()} should not change the state of the iteration, except that it + * may prevent removal of the most recent element via {@link #remove()}. + * + * @throws NoSuchElementException if the iteration has no more elements according to {@link + * #hasNext()} + */ + @ParametricNullness + E peek(); + + /** + * {@inheritDoc} + * + *

The objects returned by consecutive calls to {@link #peek()} then {@link #next()} are + * guaranteed to be equal to each other. + */ + @Override + @ParametricNullness + E next(); + + /** + * {@inheritDoc} + * + *

Implementations may or may not support removal when a call to {@link #peek()} has occurred + * since the most recent call to {@link #next()}. + * + * @throws IllegalStateException if there has been a call to {@link #peek()} since the most recent + * call to {@link #next()} and this implementation does not support this sequence of calls + * (optional) + */ + @Override + void remove(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Platform.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Platform.java new file mode 100644 index 0000000000000..6a64bf836dd6e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Platform.java @@ -0,0 +1,144 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Hayward Chan + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class Platform { + private static final java.util.logging.Logger logger = + java.util.logging.Logger.getLogger(Platform.class.getName()); + + /** Returns the platform preferred implementation of a map based on a hash table. */ + static Map newHashMapWithExpectedSize( + int expectedSize) { + return Maps.newHashMapWithExpectedSize(expectedSize); + } + + /** + * Returns the platform preferred implementation of an insertion ordered map based on a hash + * table. + */ + static Map newLinkedHashMapWithExpectedSize( + int expectedSize) { + return Maps.newLinkedHashMapWithExpectedSize(expectedSize); + } + + /** Returns the platform preferred implementation of a set based on a hash table. */ + static Set newHashSetWithExpectedSize(int expectedSize) { + return Sets.newHashSetWithExpectedSize(expectedSize); + } + + /** Returns the platform preferred implementation of a thread-safe hash set. */ + static Set newConcurrentHashSet() { + return ConcurrentHashMap.newKeySet(); + } + + /** + * Returns the platform preferred implementation of an insertion ordered set based on a hash + * table. + */ + static Set newLinkedHashSetWithExpectedSize(int expectedSize) { + return Sets.newLinkedHashSetWithExpectedSize(expectedSize); + } + + /** + * Returns the platform preferred map implementation that preserves insertion order when used only + * for insertions. + */ + static Map preservesInsertionOrderOnPutsMap() { + return Maps.newLinkedHashMap(); + } + + /** + * Returns the platform preferred set implementation that preserves insertion order when used only + * for insertions. + */ + static Set preservesInsertionOrderOnAddsSet() { + return Sets.newLinkedHashSet(); + } + + /** + * Returns a new array of the given length with the same type as a reference array. + * + * @param reference any array of the desired type + * @param length the length of the new array + */ + /* + * The new array contains nulls, even if the old array did not. If we wanted to be accurate, we + * would declare a return type of `@Nullable T[]`. However, we've decided not to think too hard + * about arrays for now, as they're a mess. (We previously discussed this in the review of + * ObjectArrays, which is the main caller of this method.) + */ + static T[] newArray(T[] reference, int length) { + T[] empty = reference.length == 0 ? reference : Arrays.copyOf(reference, 0); + return Arrays.copyOf(empty, length); + } + + /** Equivalent to Arrays.copyOfRange(source, from, to, arrayOfType.getClass()). */ + /* + * Arrays are a mess from a nullness perspective, and Class instances for object-array types are + * even worse. For now, we just suppress and move on with our lives. + * + * - https://github.com/jspecify/jspecify/issues/65 + * + * - https://github.com/jspecify/jdk/commit/71d826792b8c7ef95d492c50a274deab938f2552 + */ + @SuppressWarnings("nullness") + static T[] copy(Object[] source, int from, int to, T[] arrayOfType) { + return Arrays.copyOfRange(source, from, to, (Class) arrayOfType.getClass()); + } + + /** + * Configures the given map maker to use weak keys, if possible; does nothing otherwise (i.e., in + * GWT). This is sometimes acceptable, when only server-side code could generate enough volume + * that reclamation becomes important. + */ + @J2ktIncompatible + static MapMaker tryWeakKeys(MapMaker mapMaker) { + return mapMaker.weakKeys(); + } + + static > Class getDeclaringClassOrObjectForJ2cl(E e) { + return e.getDeclaringClass(); + } + + static int reduceIterationsIfGwt(int iterations) { + return iterations; + } + + static int reduceExponentIfGwt(int exponent) { + return exponent; + } + + private Platform() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Queues.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Queues.java new file mode 100644 index 0000000000000..56edc6634473b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Queues.java @@ -0,0 +1,489 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + +/** + * Static utility methods pertaining to {@link Queue} and {@link Deque} instances. Also see this + * class's counterparts {@link Lists}, {@link Sets}, and {@link Maps}. + * + * @author Kurt Alfred Kluever + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Queues { + private Queues() {} + + // ArrayBlockingQueue + + /** + * Creates an empty {@code ArrayBlockingQueue} with the given (fixed) capacity and nonfair access + * policy. + */ + @J2ktIncompatible + @GwtIncompatible // ArrayBlockingQueue + public static ArrayBlockingQueue newArrayBlockingQueue(int capacity) { + return new ArrayBlockingQueue(capacity); + } + + // ArrayDeque + + /** + * Creates an empty {@code ArrayDeque}. + * + * @since 12.0 + */ + public static ArrayDeque newArrayDeque() { + return new ArrayDeque(); + } + + /** + * Creates an {@code ArrayDeque} containing the elements of the specified iterable, in the order + * they are returned by the iterable's iterator. + * + * @since 12.0 + */ + public static ArrayDeque newArrayDeque(Iterable elements) { + if (elements instanceof Collection) { + return new ArrayDeque((Collection) elements); + } + ArrayDeque deque = new ArrayDeque(); + Iterables.addAll(deque, elements); + return deque; + } + + // ConcurrentLinkedQueue + + /** Creates an empty {@code ConcurrentLinkedQueue}. */ + @J2ktIncompatible + @GwtIncompatible // ConcurrentLinkedQueue + public static ConcurrentLinkedQueue newConcurrentLinkedQueue() { + return new ConcurrentLinkedQueue(); + } + + /** + * Creates a {@code ConcurrentLinkedQueue} containing the elements of the specified iterable, in + * the order they are returned by the iterable's iterator. + */ + @J2ktIncompatible + @GwtIncompatible // ConcurrentLinkedQueue + public static ConcurrentLinkedQueue newConcurrentLinkedQueue( + Iterable elements) { + if (elements instanceof Collection) { + return new ConcurrentLinkedQueue((Collection) elements); + } + ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + Iterables.addAll(queue, elements); + return queue; + } + + // LinkedBlockingDeque + + /** + * Creates an empty {@code LinkedBlockingDeque} with a capacity of {@link Integer#MAX_VALUE}. + * + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // LinkedBlockingDeque + public static LinkedBlockingDeque newLinkedBlockingDeque() { + return new LinkedBlockingDeque(); + } + + /** + * Creates an empty {@code LinkedBlockingDeque} with the given (fixed) capacity. + * + * @throws IllegalArgumentException if {@code capacity} is less than 1 + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // LinkedBlockingDeque + public static LinkedBlockingDeque newLinkedBlockingDeque(int capacity) { + return new LinkedBlockingDeque(capacity); + } + + /** + * Creates a {@code LinkedBlockingDeque} with a capacity of {@link Integer#MAX_VALUE}, containing + * the elements of the specified iterable, in the order they are returned by the iterable's + * iterator. + * + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // LinkedBlockingDeque + public static LinkedBlockingDeque newLinkedBlockingDeque(Iterable elements) { + if (elements instanceof Collection) { + return new LinkedBlockingDeque((Collection) elements); + } + LinkedBlockingDeque deque = new LinkedBlockingDeque(); + Iterables.addAll(deque, elements); + return deque; + } + + // LinkedBlockingQueue + + /** Creates an empty {@code LinkedBlockingQueue} with a capacity of {@link Integer#MAX_VALUE}. */ + @J2ktIncompatible + @GwtIncompatible // LinkedBlockingQueue + public static LinkedBlockingQueue newLinkedBlockingQueue() { + return new LinkedBlockingQueue(); + } + + /** + * Creates an empty {@code LinkedBlockingQueue} with the given (fixed) capacity. + * + * @throws IllegalArgumentException if {@code capacity} is less than 1 + */ + @J2ktIncompatible + @GwtIncompatible // LinkedBlockingQueue + public static LinkedBlockingQueue newLinkedBlockingQueue(int capacity) { + return new LinkedBlockingQueue(capacity); + } + + /** + * Creates a {@code LinkedBlockingQueue} with a capacity of {@link Integer#MAX_VALUE}, containing + * the elements of the specified iterable, in the order they are returned by the iterable's + * iterator. + * + * @param elements the elements that the queue should contain, in order + * @return a new {@code LinkedBlockingQueue} containing those elements + */ + @J2ktIncompatible + @GwtIncompatible // LinkedBlockingQueue + public static LinkedBlockingQueue newLinkedBlockingQueue(Iterable elements) { + if (elements instanceof Collection) { + return new LinkedBlockingQueue((Collection) elements); + } + LinkedBlockingQueue queue = new LinkedBlockingQueue(); + Iterables.addAll(queue, elements); + return queue; + } + + // LinkedList: see {@link org.apache.iotdb.google.common.collect.Lists} + + // PriorityBlockingQueue + + /** + * Creates an empty {@code PriorityBlockingQueue} with the ordering given by its elements' natural + * ordering. + * + * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} + * in 15.0) + */ + @J2ktIncompatible + @GwtIncompatible // PriorityBlockingQueue + public static PriorityBlockingQueue newPriorityBlockingQueue() { + return new PriorityBlockingQueue(); + } + + /** + * Creates a {@code PriorityBlockingQueue} containing the given elements. + * + *

Note: If the specified iterable is a {@code SortedSet} or a {@code PriorityQueue}, + * this priority queue will be ordered according to the same ordering. + * + * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} + * in 15.0) + */ + @J2ktIncompatible + @GwtIncompatible // PriorityBlockingQueue + public static PriorityBlockingQueue newPriorityBlockingQueue( + Iterable elements) { + if (elements instanceof Collection) { + return new PriorityBlockingQueue((Collection) elements); + } + PriorityBlockingQueue queue = new PriorityBlockingQueue(); + Iterables.addAll(queue, elements); + return queue; + } + + // PriorityQueue + + /** + * Creates an empty {@code PriorityQueue} with the ordering given by its elements' natural + * ordering. + * + * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} + * in 15.0) + */ + public static PriorityQueue newPriorityQueue() { + return new PriorityQueue(); + } + + /** + * Creates a {@code PriorityQueue} containing the given elements. + * + *

Note: If the specified iterable is a {@code SortedSet} or a {@code PriorityQueue}, + * this priority queue will be ordered according to the same ordering. + * + * @since 11.0 (but the bound of {@code E} was changed from {@code Object} to {@code Comparable} + * in 15.0) + */ + public static PriorityQueue newPriorityQueue( + Iterable elements) { + if (elements instanceof Collection) { + return new PriorityQueue((Collection) elements); + } + PriorityQueue queue = new PriorityQueue(); + Iterables.addAll(queue, elements); + return queue; + } + + // SynchronousQueue + + /** Creates an empty {@code SynchronousQueue} with nonfair access policy. */ + @J2ktIncompatible + @GwtIncompatible // SynchronousQueue + public static SynchronousQueue newSynchronousQueue() { + return new SynchronousQueue(); + } + + /** + * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested {@code + * numElements} elements are not available, it will wait for them up to the specified timeout. + * + * @param q the blocking queue to be drained + * @param buffer where to add the transferred elements + * @param numElements the number of elements to be waited for + * @param timeout how long to wait before giving up + * @return the number of elements transferred + * @throws InterruptedException if interrupted while waiting + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // BlockingQueue + public static int drain( + BlockingQueue q, Collection buffer, int numElements, java.time.Duration timeout) + throws InterruptedException { + // TODO(b/126049426): Consider using saturateToNanos(timeout) instead. + return drain(q, buffer, numElements, timeout.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested {@code + * numElements} elements are not available, it will wait for them up to the specified timeout. + * + * @param q the blocking queue to be drained + * @param buffer where to add the transferred elements + * @param numElements the number of elements to be waited for + * @param timeout how long to wait before giving up, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the timeout parameter + * @return the number of elements transferred + * @throws InterruptedException if interrupted while waiting + */ + @J2ktIncompatible + @GwtIncompatible // BlockingQueue + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static int drain( + BlockingQueue q, + Collection buffer, + int numElements, + long timeout, + TimeUnit unit) + throws InterruptedException { + Preconditions.checkNotNull(buffer); + /* + * This code performs one System.nanoTime() more than necessary, and in return, the time to + * execute Queue#drainTo is not added *on top* of waiting for the timeout (which could make + * the timeout arbitrarily inaccurate, given a queue that is slow to drain). + */ + long deadline = System.nanoTime() + unit.toNanos(timeout); + int added = 0; + while (added < numElements) { + // we could rely solely on #poll, but #drainTo might be more efficient when there are multiple + // elements already available (e.g. LinkedBlockingQueue#drainTo locks only once) + added += q.drainTo(buffer, numElements - added); + if (added < numElements) { // not enough elements immediately available; will have to poll + E e = q.poll(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + if (e == null) { + break; // we already waited enough, and there are no more elements in sight + } + buffer.add(e); + added++; + } + } + return added; + } + + /** + * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, Duration)}, but with a + * different behavior in case it is interrupted while waiting. In that case, the operation will + * continue as usual, and in the end the thread's interruption status will be set (no {@code + * InterruptedException} is thrown). + * + * @param q the blocking queue to be drained + * @param buffer where to add the transferred elements + * @param numElements the number of elements to be waited for + * @param timeout how long to wait before giving up + * @return the number of elements transferred + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // BlockingQueue + public static int drainUninterruptibly( + BlockingQueue q, + Collection buffer, + int numElements, + java.time.Duration timeout) { + // TODO(b/126049426): Consider using saturateToNanos(timeout) instead. + return drainUninterruptibly(q, buffer, numElements, timeout.toNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, but + * with a different behavior in case it is interrupted while waiting. In that case, the operation + * will continue as usual, and in the end the thread's interruption status will be set (no {@code + * InterruptedException} is thrown). + * + * @param q the blocking queue to be drained + * @param buffer where to add the transferred elements + * @param numElements the number of elements to be waited for + * @param timeout how long to wait before giving up, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the timeout parameter + * @return the number of elements transferred + */ + @J2ktIncompatible + @GwtIncompatible // BlockingQueue + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static int drainUninterruptibly( + BlockingQueue q, + Collection buffer, + int numElements, + long timeout, + TimeUnit unit) { + Preconditions.checkNotNull(buffer); + long deadline = System.nanoTime() + unit.toNanos(timeout); + int added = 0; + boolean interrupted = false; + try { + while (added < numElements) { + // we could rely solely on #poll, but #drainTo might be more efficient when there are + // multiple elements already available (e.g. LinkedBlockingQueue#drainTo locks only once) + added += q.drainTo(buffer, numElements - added); + if (added < numElements) { // not enough elements immediately available; will have to poll + E e; // written exactly once, by a successful (uninterrupted) invocation of #poll + while (true) { + try { + e = q.poll(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + break; + } catch (InterruptedException ex) { + interrupted = true; // note interruption and retry + } + } + if (e == null) { + break; // we already waited enough, and there are no more elements in sight + } + buffer.add(e); + added++; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + return added; + } + + /** + * Returns a synchronized (thread-safe) queue backed by the specified queue. In order to guarantee + * serial access, it is critical that all access to the backing queue is accomplished + * through the returned queue. + * + *

It is imperative that the user manually synchronize on the returned queue when accessing the + * queue's iterator: + * + *

{@code
+   * Queue queue = Queues.synchronizedQueue(MinMaxPriorityQueue.create());
+   * ...
+   * queue.add(element);  // Needn't be in synchronized block
+   * ...
+   * synchronized (queue) {  // Must synchronize on queue!
+   *   Iterator i = queue.iterator(); // Must be in synchronized block
+   *   while (i.hasNext()) {
+   *     foo(i.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned queue will be serializable if the specified queue is serializable. + * + * @param queue the queue to be wrapped in a synchronized view + * @return a synchronized view of the specified queue + * @since 14.0 + */ + public static Queue synchronizedQueue(Queue queue) { + return Synchronized.queue(queue, null); + } + + /** + * Returns a synchronized (thread-safe) deque backed by the specified deque. In order to guarantee + * serial access, it is critical that all access to the backing deque is accomplished + * through the returned deque. + * + *

It is imperative that the user manually synchronize on the returned deque when accessing any + * of the deque's iterators: + * + *

{@code
+   * Deque deque = Queues.synchronizedDeque(Queues.newArrayDeque());
+   * ...
+   * deque.add(element);  // Needn't be in synchronized block
+   * ...
+   * synchronized (deque) {  // Must synchronize on deque!
+   *   Iterator i = deque.iterator(); // Must be in synchronized block
+   *   while (i.hasNext()) {
+   *     foo(i.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned deque will be serializable if the specified deque is serializable. + * + * @param deque the deque to be wrapped in a synchronized view + * @return a synchronized view of the specified deque + * @since 15.0 + */ + public static Deque synchronizedDeque(Deque deque) { + return Synchronized.deque(deque, null); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Range.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Range.java new file mode 100644 index 0000000000000..69769099e59a6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Range.java @@ -0,0 +1,741 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Equivalence; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Predicate; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A range (or "interval") defines the boundaries around a contiguous span of values of some + * {@code Comparable} type; for example, "integers from 1 to 100 inclusive." Note that it is not + * possible to iterate over these contained values. To do so, pass this range instance and an + * appropriate {@link DiscreteDomain} to {@link ContiguousSet#create}. + * + *

Types of ranges

+ * + *

Each end of the range may be bounded or unbounded. If bounded, there is an associated + * endpoint value, and the range is considered to be either open (does not include the + * endpoint) or closed (includes the endpoint) on that side. With three possibilities on each + * side, this yields nine basic types of ranges, enumerated below. (Notation: a square bracket + * ({@code [ ]}) indicates that the range is closed on that side; a parenthesis ({@code ( )}) means + * it is either open or unbounded. The construct {@code {x | statement}} is read "the set of all + * x such that statement.") + * + *

+ * + * + * + *
Range Types
Notation Definition Factory method + *
{@code (a..b)} {@code {x | a < x < b}} {@link Range#open open} + *
{@code [a..b]} {@code {x | a <= x <= b}}{@link Range#closed closed} + *
{@code (a..b]} {@code {x | a < x <= b}} {@link Range#openClosed openClosed} + *
{@code [a..b)} {@code {x | a <= x < b}} {@link Range#closedOpen closedOpen} + *
{@code (a..+∞)} {@code {x | x > a}} {@link Range#greaterThan greaterThan} + *
{@code [a..+∞)} {@code {x | x >= a}} {@link Range#atLeast atLeast} + *
{@code (-∞..b)} {@code {x | x < b}} {@link Range#lessThan lessThan} + *
{@code (-∞..b]} {@code {x | x <= b}} {@link Range#atMost atMost} + *
{@code (-∞..+∞)}{@code {x}} {@link Range#all all} + *
+ * + *
+ * + *

When both endpoints exist, the upper endpoint may not be less than the lower. The endpoints + * may be equal only if at least one of the bounds is closed: + * + *

    + *
  • {@code [a..a]} : a singleton range + *
  • {@code [a..a); (a..a]} : {@linkplain #isEmpty empty} ranges; also valid + *
  • {@code (a..a)} : invalid; an exception will be thrown + *
+ * + *

Warnings

+ * + *
    + *
  • Use immutable value types only, if at all possible. If you must use a mutable type, do + * not allow the endpoint instances to mutate after the range is created! + *
  • Your value type's comparison method should be {@linkplain Comparable consistent with + * equals} if at all possible. Otherwise, be aware that concepts used throughout this + * documentation such as "equal", "same", "unique" and so on actually refer to whether {@link + * Comparable#compareTo compareTo} returns zero, not whether {@link Object#equals equals} + * returns {@code true}. + *
  • A class which implements {@code Comparable} is very broken, and will cause + * undefined horrible things to happen in {@code Range}. For now, the Range API does not + * prevent its use, because this would also rule out all ungenerified (pre-JDK1.5) data types. + * This may change in the future. + *
+ * + *

Other notes

+ * + *
    + *
  • All ranges are shallow-immutable. + *
  • Instances of this type are obtained using the static factory methods in this class. + *
  • Ranges are convex: whenever two values are contained, all values in between them + * must also be contained. More formally, for any {@code c1 <= c2 <= c3} of type {@code C}, + * {@code r.contains(c1) && r.contains(c3)} implies {@code r.contains(c2)}). This means that a + * {@code Range} can never be used to represent, say, "all prime numbers from + * 1 to 100." + *
  • When evaluated as a {@link Predicate}, a range yields the same result as invoking {@link + * #contains}. + *
  • Terminology note: a range {@code a} is said to be the maximal range having property + * P if, for all ranges {@code b} also having property P, {@code a.encloses(b)}. + * Likewise, {@code a} is minimal when {@code b.encloses(a)} for all {@code b} having + * property P. See, for example, the definition of {@link #intersection intersection}. + *
  • A {@code Range} is serializable if it has no bounds, or if each bound is serializable. + *
+ * + *

Further reading

+ * + *

See the Guava User Guide article on {@code Range}. + * + * @author Kevin Bourrillion + * @author Gregory Kick + * @since 10.0 + */ +@GwtCompatible +@SuppressWarnings("rawtypes") +@ElementTypesAreNonnullByDefault +public final class Range extends RangeGwtSerializationDependencies + implements Predicate, Serializable { + + static class LowerBoundFn implements Function { + static final LowerBoundFn INSTANCE = new LowerBoundFn(); + + @Override + public Cut apply(Range range) { + return range.lowerBound; + } + } + + static class UpperBoundFn implements Function { + static final UpperBoundFn INSTANCE = new UpperBoundFn(); + + @Override + public Cut apply(Range range) { + return range.upperBound; + } + } + + @SuppressWarnings("unchecked") + static > Function, Cut> lowerBoundFn() { + return (Function) LowerBoundFn.INSTANCE; + } + + @SuppressWarnings("unchecked") + static > Function, Cut> upperBoundFn() { + return (Function) UpperBoundFn.INSTANCE; + } + + static > Ordering> rangeLexOrdering() { + return (Ordering>) (Ordering) RangeLexOrdering.INSTANCE; + } + + static > Range create(Cut lowerBound, Cut upperBound) { + return new Range<>(lowerBound, upperBound); + } + + /** + * Returns a range that contains all values strictly greater than {@code lower} and strictly less + * than {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than or equal to {@code + * upper} + * @throws ClassCastException if {@code lower} and {@code upper} are not mutually comparable + * @since 14.0 + */ + public static > Range open(C lower, C upper) { + return create(Cut.aboveValue(lower), Cut.belowValue(upper)); + } + + /** + * Returns a range that contains all values greater than or equal to {@code lower} and less than + * or equal to {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @throws ClassCastException if {@code lower} and {@code upper} are not mutually comparable + * @since 14.0 + */ + public static > Range closed(C lower, C upper) { + return create(Cut.belowValue(lower), Cut.aboveValue(upper)); + } + + /** + * Returns a range that contains all values greater than or equal to {@code lower} and strictly + * less than {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @throws ClassCastException if {@code lower} and {@code upper} are not mutually comparable + * @since 14.0 + */ + public static > Range closedOpen(C lower, C upper) { + return create(Cut.belowValue(lower), Cut.belowValue(upper)); + } + + /** + * Returns a range that contains all values strictly greater than {@code lower} and less than or + * equal to {@code upper}. + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @throws ClassCastException if {@code lower} and {@code upper} are not mutually comparable + * @since 14.0 + */ + public static > Range openClosed(C lower, C upper) { + return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); + } + + /** + * Returns a range that contains any value from {@code lower} to {@code upper}, where each + * endpoint may be either inclusive (closed) or exclusive (open). + * + * @throws IllegalArgumentException if {@code lower} is greater than {@code upper} + * @throws ClassCastException if {@code lower} and {@code upper} are not mutually comparable + * @since 14.0 + */ + public static > Range range( + C lower, BoundType lowerType, C upper, BoundType upperType) { + checkNotNull(lowerType); + checkNotNull(upperType); + + Cut lowerBound = + (lowerType == BoundType.OPEN) ? Cut.aboveValue(lower) : Cut.belowValue(lower); + Cut upperBound = + (upperType == BoundType.OPEN) ? Cut.belowValue(upper) : Cut.aboveValue(upper); + return create(lowerBound, upperBound); + } + + /** + * Returns a range that contains all values strictly less than {@code endpoint}. + * + * @since 14.0 + */ + public static > Range lessThan(C endpoint) { + return create(Cut.belowAll(), Cut.belowValue(endpoint)); + } + + /** + * Returns a range that contains all values less than or equal to {@code endpoint}. + * + * @since 14.0 + */ + public static > Range atMost(C endpoint) { + return create(Cut.belowAll(), Cut.aboveValue(endpoint)); + } + + /** + * Returns a range with no lower bound up to the given endpoint, which may be either inclusive + * (closed) or exclusive (open). + * + * @since 14.0 + */ + public static > Range upTo(C endpoint, BoundType boundType) { + switch (boundType) { + case OPEN: + return lessThan(endpoint); + case CLOSED: + return atMost(endpoint); + default: + throw new AssertionError(); + } + } + + /** + * Returns a range that contains all values strictly greater than {@code endpoint}. + * + * @since 14.0 + */ + public static > Range greaterThan(C endpoint) { + return create(Cut.aboveValue(endpoint), Cut.aboveAll()); + } + + /** + * Returns a range that contains all values greater than or equal to {@code endpoint}. + * + * @since 14.0 + */ + public static > Range atLeast(C endpoint) { + return create(Cut.belowValue(endpoint), Cut.aboveAll()); + } + + /** + * Returns a range from the given endpoint, which may be either inclusive (closed) or exclusive + * (open), with no upper bound. + * + * @since 14.0 + */ + public static > Range downTo(C endpoint, BoundType boundType) { + switch (boundType) { + case OPEN: + return greaterThan(endpoint); + case CLOSED: + return atLeast(endpoint); + default: + throw new AssertionError(); + } + } + + private static final Range ALL = new Range<>(Cut.belowAll(), Cut.aboveAll()); + + /** + * Returns a range that contains every value of type {@code C}. + * + * @since 14.0 + */ + @SuppressWarnings("unchecked") + public static > Range all() { + return (Range) ALL; + } + + /** + * Returns a range that {@linkplain Range#contains(Comparable) contains} only the given value. The + * returned range is {@linkplain BoundType#CLOSED closed} on both ends. + * + * @since 14.0 + */ + public static > Range singleton(C value) { + return closed(value, value); + } + + /** + * Returns the minimal range that {@linkplain Range#contains(Comparable) contains} all of the + * given values. The returned range is {@linkplain BoundType#CLOSED closed} on both ends. + * + * @throws ClassCastException if the values are not mutually comparable + * @throws NoSuchElementException if {@code values} is empty + * @throws NullPointerException if any of {@code values} is null + * @since 14.0 + */ + public static > Range encloseAll(Iterable values) { + checkNotNull(values); + if (values instanceof SortedSet) { + SortedSet set = (SortedSet) values; + Comparator comparator = set.comparator(); + if (Ordering.natural().equals(comparator) || comparator == null) { + return closed(set.first(), set.last()); + } + } + Iterator valueIterator = values.iterator(); + C min = checkNotNull(valueIterator.next()); + C max = min; + while (valueIterator.hasNext()) { + C value = checkNotNull(valueIterator.next()); + min = Ordering.natural().min(min, value); + max = Ordering.natural().max(max, value); + } + return closed(min, max); + } + + final Cut lowerBound; + final Cut upperBound; + + private Range(Cut lowerBound, Cut upperBound) { + this.lowerBound = checkNotNull(lowerBound); + this.upperBound = checkNotNull(upperBound); + if (lowerBound.compareTo(upperBound) > 0 + || lowerBound == Cut.aboveAll() + || upperBound == Cut.belowAll()) { + throw new IllegalArgumentException("Invalid range: " + toString(lowerBound, upperBound)); + } + } + + /** Returns {@code true} if this range has a lower endpoint. */ + public boolean hasLowerBound() { + return lowerBound != Cut.belowAll(); + } + + /** + * Returns the lower endpoint of this range. + * + * @throws IllegalStateException if this range is unbounded below (that is, {@link + * #hasLowerBound()} returns {@code false}) + */ + public C lowerEndpoint() { + return lowerBound.endpoint(); + } + + /** + * Returns the type of this range's lower bound: {@link BoundType#CLOSED} if the range includes + * its lower endpoint, {@link BoundType#OPEN} if it does not. + * + * @throws IllegalStateException if this range is unbounded below (that is, {@link + * #hasLowerBound()} returns {@code false}) + */ + public BoundType lowerBoundType() { + return lowerBound.typeAsLowerBound(); + } + + /** Returns {@code true} if this range has an upper endpoint. */ + public boolean hasUpperBound() { + return upperBound != Cut.aboveAll(); + } + + /** + * Returns the upper endpoint of this range. + * + * @throws IllegalStateException if this range is unbounded above (that is, {@link + * #hasUpperBound()} returns {@code false}) + */ + public C upperEndpoint() { + return upperBound.endpoint(); + } + + /** + * Returns the type of this range's upper bound: {@link BoundType#CLOSED} if the range includes + * its upper endpoint, {@link BoundType#OPEN} if it does not. + * + * @throws IllegalStateException if this range is unbounded above (that is, {@link + * #hasUpperBound()} returns {@code false}) + */ + public BoundType upperBoundType() { + return upperBound.typeAsUpperBound(); + } + + /** + * Returns {@code true} if this range is of the form {@code [v..v)} or {@code (v..v]}. (This does + * not encompass ranges of the form {@code (v..v)}, because such ranges are invalid and + * can't be constructed at all.) + * + *

Note that certain discrete ranges such as the integer range {@code (3..4)} are not + * considered empty, even though they contain no actual values. In these cases, it may be helpful + * to preprocess ranges with {@link #canonical(DiscreteDomain)}. + */ + public boolean isEmpty() { + return lowerBound.equals(upperBound); + } + + /** + * Returns {@code true} if {@code value} is within the bounds of this range. For example, on the + * range {@code [0..2)}, {@code contains(1)} returns {@code true}, while {@code contains(2)} + * returns {@code false}. + */ + public boolean contains(C value) { + checkNotNull(value); + // let this throw CCE if there is some trickery going on + return lowerBound.isLessThan(value) && !upperBound.isLessThan(value); + } + + /** + * @deprecated Provided only to satisfy the {@link Predicate} interface; use {@link #contains} + * instead. + */ + @Deprecated + @Override + public boolean apply(C input) { + return contains(input); + } + + /** + * Returns {@code true} if every element in {@code values} is {@linkplain #contains contained} in + * this range. + */ + public boolean containsAll(Iterable values) { + if (Iterables.isEmpty(values)) { + return true; + } + + // this optimizes testing equality of two range-backed sets + if (values instanceof SortedSet) { + SortedSet set = (SortedSet) values; + Comparator comparator = set.comparator(); + if (Ordering.natural().equals(comparator) || comparator == null) { + return contains(set.first()) && contains(set.last()); + } + } + + for (C value : values) { + if (!contains(value)) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if the bounds of {@code other} do not extend outside the bounds of this + * range. Examples: + * + *

    + *
  • {@code [3..6]} encloses {@code [4..5]} + *
  • {@code (3..6)} encloses {@code (3..6)} + *
  • {@code [3..6]} encloses {@code [4..4)} (even though the latter is empty) + *
  • {@code (3..6]} does not enclose {@code [3..6]} + *
  • {@code [4..5]} does not enclose {@code (3..6)} (even though it contains every value + * contained by the latter range) + *
  • {@code [3..6]} does not enclose {@code (1..1]} (even though it contains every value + * contained by the latter range) + *
+ * + *

Note that if {@code a.encloses(b)}, then {@code b.contains(v)} implies {@code + * a.contains(v)}, but as the last two examples illustrate, the converse is not always true. + * + *

Being reflexive, antisymmetric and transitive, the {@code encloses} relation defines a + * partial order over ranges. There exists a unique {@linkplain Range#all maximal} range + * according to this relation, and also numerous {@linkplain #isEmpty minimal} ranges. Enclosure + * also implies {@linkplain #isConnected connectedness}. + */ + public boolean encloses(Range other) { + return lowerBound.compareTo(other.lowerBound) <= 0 + && upperBound.compareTo(other.upperBound) >= 0; + } + + /** + * Returns {@code true} if there exists a (possibly empty) range which is {@linkplain #encloses + * enclosed} by both this range and {@code other}. + * + *

For example, + * + *

    + *
  • {@code [2, 4)} and {@code [5, 7)} are not connected + *
  • {@code [2, 4)} and {@code [3, 5)} are connected, because both enclose {@code [3, 4)} + *
  • {@code [2, 4)} and {@code [4, 6)} are connected, because both enclose the empty range + * {@code [4, 4)} + *
+ * + *

Note that this range and {@code other} have a well-defined {@linkplain #span union} and + * {@linkplain #intersection intersection} (as a single, possibly-empty range) if and only if this + * method returns {@code true}. + * + *

The connectedness relation is both reflexive and symmetric, but does not form an {@linkplain + * Equivalence equivalence relation} as it is not transitive. + * + *

Note that certain discrete ranges are not considered connected, even though there are no + * elements "between them." For example, {@code [3, 5]} is not considered connected to {@code [6, + * 10]}. In these cases, it may be desirable for both input ranges to be preprocessed with {@link + * #canonical(DiscreteDomain)} before testing for connectedness. + */ + public boolean isConnected(Range other) { + return lowerBound.compareTo(other.upperBound) <= 0 + && other.lowerBound.compareTo(upperBound) <= 0; + } + + /** + * Returns the maximal range {@linkplain #encloses enclosed} by both this range and {@code + * connectedRange}, if such a range exists. + * + *

For example, the intersection of {@code [1..5]} and {@code (3..7)} is {@code (3..5]}. The + * resulting range may be empty; for example, {@code [1..5)} intersected with {@code [5..7)} + * yields the empty range {@code [5..5)}. + * + *

The intersection exists if and only if the two ranges are {@linkplain #isConnected + * connected}. + * + *

The intersection operation is commutative, associative and idempotent, and its identity + * element is {@link Range#all}). + * + * @throws IllegalArgumentException if {@code isConnected(connectedRange)} is {@code false} + */ + public Range intersection(Range connectedRange) { + int lowerCmp = lowerBound.compareTo(connectedRange.lowerBound); + int upperCmp = upperBound.compareTo(connectedRange.upperBound); + if (lowerCmp >= 0 && upperCmp <= 0) { + return this; + } else if (lowerCmp <= 0 && upperCmp >= 0) { + return connectedRange; + } else { + Cut newLower = (lowerCmp >= 0) ? lowerBound : connectedRange.lowerBound; + Cut newUpper = (upperCmp <= 0) ? upperBound : connectedRange.upperBound; + + // create() would catch this, but give a confusing error message + checkArgument( + newLower.compareTo(newUpper) <= 0, + "intersection is undefined for disconnected ranges %s and %s", + this, + connectedRange); + + // TODO(kevinb): all the precondition checks in the constructor are redundant... + return create(newLower, newUpper); + } + } + + /** + * Returns the maximal range lying between this range and {@code otherRange}, if such a range + * exists. The resulting range may be empty if the two ranges are adjacent but non-overlapping. + * + *

For example, the gap of {@code [1..5]} and {@code (7..10)} is {@code (5..7]}. The resulting + * range may be empty; for example, the gap between {@code [1..5)} {@code [5..7)} yields the empty + * range {@code [5..5)}. + * + *

The gap exists if and only if the two ranges are either disconnected or immediately adjacent + * (any intersection must be an empty range). + * + *

The gap operation is commutative. + * + * @throws IllegalArgumentException if this range and {@code otherRange} have a nonempty + * intersection + * @since 27.0 + */ + public Range gap(Range otherRange) { + /* + * For an explanation of the basic principle behind this check, see + * https://stackoverflow.com/a/35754308/28465 + * + * In that explanation's notation, our `overlap` check would be `x1 < y2 && y1 < x2`. We've + * flipped one part of the check so that we're using "less than" in both cases (rather than a + * mix of "less than" and "greater than"). We've also switched to "strictly less than" rather + * than "less than or equal to" because of *handwave* the difference between "endpoints of + * inclusive ranges" and "Cuts." + */ + if (lowerBound.compareTo(otherRange.upperBound) < 0 + && otherRange.lowerBound.compareTo(upperBound) < 0) { + throw new IllegalArgumentException( + "Ranges have a nonempty intersection: " + this + ", " + otherRange); + } + + boolean isThisFirst = this.lowerBound.compareTo(otherRange.lowerBound) < 0; + Range firstRange = isThisFirst ? this : otherRange; + Range secondRange = isThisFirst ? otherRange : this; + return create(firstRange.upperBound, secondRange.lowerBound); + } + + /** + * Returns the minimal range that {@linkplain #encloses encloses} both this range and {@code + * other}. For example, the span of {@code [1..3]} and {@code (5..7)} is {@code [1..7)}. + * + *

If the input ranges are {@linkplain #isConnected connected}, the returned range can + * also be called their union. If they are not, note that the span might contain values + * that are not contained in either input range. + * + *

Like {@link #intersection(Range) intersection}, this operation is commutative, associative + * and idempotent. Unlike it, it is always well-defined for any two input ranges. + */ + public Range span(Range other) { + int lowerCmp = lowerBound.compareTo(other.lowerBound); + int upperCmp = upperBound.compareTo(other.upperBound); + if (lowerCmp <= 0 && upperCmp >= 0) { + return this; + } else if (lowerCmp >= 0 && upperCmp <= 0) { + return other; + } else { + Cut newLower = (lowerCmp <= 0) ? lowerBound : other.lowerBound; + Cut newUpper = (upperCmp >= 0) ? upperBound : other.upperBound; + return create(newLower, newUpper); + } + } + + /** + * Returns the canonical form of this range in the given domain. The canonical form has the + * following properties: + * + *

    + *
  • equivalence: {@code a.canonical().contains(v) == a.contains(v)} for all {@code v} (in + * other words, {@code ContiguousSet.create(a.canonical(domain), domain).equals( + * ContiguousSet.create(a, domain))} + *
  • uniqueness: unless {@code a.isEmpty()}, {@code ContiguousSet.create(a, + * domain).equals(ContiguousSet.create(b, domain))} implies {@code + * a.canonical(domain).equals(b.canonical(domain))} + *
  • idempotence: {@code a.canonical(domain).canonical(domain).equals(a.canonical(domain))} + *
+ * + *

Furthermore, this method guarantees that the range returned will be one of the following + * canonical forms: + * + *

    + *
  • [start..end) + *
  • [start..+∞) + *
  • (-∞..end) (only if type {@code C} is unbounded below) + *
  • (-∞..+∞) (only if type {@code C} is unbounded below) + *
+ */ + public Range canonical(DiscreteDomain domain) { + checkNotNull(domain); + Cut lower = lowerBound.canonical(domain); + Cut upper = upperBound.canonical(domain); + return (lower == lowerBound && upper == upperBound) ? this : create(lower, upper); + } + + /** + * Returns {@code true} if {@code object} is a range having the same endpoints and bound types as + * this range. Note that discrete ranges such as {@code (1..4)} and {@code [2..3]} are not + * equal to one another, despite the fact that they each contain precisely the same set of values. + * Similarly, empty ranges are not equal unless they have exactly the same representation, so + * {@code [3..3)}, {@code (3..3]}, {@code (4..4]} are all unequal. + */ + @Override + public boolean equals(Object object) { + if (object instanceof Range) { + Range other = (Range) object; + return lowerBound.equals(other.lowerBound) && upperBound.equals(other.upperBound); + } + return false; + } + + /** Returns a hash code for this range. */ + @Override + public int hashCode() { + return lowerBound.hashCode() * 31 + upperBound.hashCode(); + } + + /** + * Returns a string representation of this range, such as {@code "[3..5)"} (other examples are + * listed in the class documentation). + */ + @Override + public String toString() { + return toString(lowerBound, upperBound); + } + + private static String toString(Cut lowerBound, Cut upperBound) { + StringBuilder sb = new StringBuilder(16); + lowerBound.describeAsLowerBound(sb); + sb.append(".."); + upperBound.describeAsUpperBound(sb); + return sb.toString(); + } + + Object readResolve() { + if (this.equals(ALL)) { + return all(); + } else { + return this; + } + } + + @SuppressWarnings("unchecked") // this method may throw CCE + static int compareOrThrow(Comparable left, Comparable right) { + return left.compareTo(right); + } + + /** Needed to serialize sorted collections of Ranges. */ + private static class RangeLexOrdering extends Ordering> implements Serializable { + static final Ordering> INSTANCE = new RangeLexOrdering(); + + @Override + public int compare(Range left, Range right) { + return ComparisonChain.start() + .compare(left.lowerBound, right.lowerBound) + .compare(left.upperBound, right.upperBound) + .result(); + } + + private static final long serialVersionUID = 0; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeGwtSerializationDependencies.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeGwtSerializationDependencies.java new file mode 100644 index 0000000000000..26ef57b05c060 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeGwtSerializationDependencies.java @@ -0,0 +1,36 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** + * A dummy superclass to support GWT serialization of the element type of a {@link Range}. The GWT + * supersource for this class contains a field of type {@code C}. + * + *

For details about this hack, see {@code GwtSerializationDependencies}, which takes the same + * approach but with a subclass rather than a superclass. + * + *

TODO(cpovirk): Consider applying this subclass approach to our other types. + */ +@GwtCompatible(emulated = true) +abstract class RangeGwtSerializationDependencies implements Serializable {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeMap.java new file mode 100644 index 0000000000000..9eecf2f8238f7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeMap.java @@ -0,0 +1,191 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.function.BiFunction; + +/** + * A mapping from disjoint nonempty ranges to non-null values. Queries look up the value associated + * with the range (if any) that contains a specified key. + * + *

In contrast to {@link RangeSet}, no "coalescing" is done of {@linkplain + * Range#isConnected(Range) connected} ranges, even if they are mapped to the same value. + * + * @author Louis Wasserman + * @since 14.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface RangeMap { + /* + * TODO(cpovirk): These docs sometimes say "map" and sometimes say "range map." Pick one, or at + * least decide on a policy for when to use which. + */ + + /** + * Returns the value associated with the specified key, or {@code null} if there is no such value. + * + *

Specifically, if any range in this range map contains the specified key, the value + * associated with that range is returned. + */ + V get(K key); + + /** + * Returns the range containing this key and its associated value, if such a range is present in + * the range map, or {@code null} otherwise. + */ + Entry, V> getEntry(K key); + + /** + * Returns the minimal range {@linkplain Range#encloses(Range) enclosing} the ranges in this + * {@code RangeMap}. + * + * @throws NoSuchElementException if this range map is empty + */ + Range span(); + + /** + * Maps a range to a specified value (optional operation). + * + *

Specifically, after a call to {@code put(range, value)}, if {@link + * Range#contains(Comparable) range.contains(k)}, then {@link #get(Comparable) get(k)} will return + * {@code value}. + * + *

If {@code range} {@linkplain Range#isEmpty() is empty}, then this is a no-op. + */ + void put(Range range, V value); + + /** + * Maps a range to a specified value, coalescing this range with any existing ranges with the same + * value that are {@linkplain Range#isConnected connected} to this range. + * + *

The behavior of {@link #get(Comparable) get(k)} after calling this method is identical to + * the behavior described in {@link #put(Range, Object) put(range, value)}, however the ranges + * returned from {@link #asMapOfRanges} will be different if there were existing entries which + * connect to the given range and value. + * + *

Even if the input range is empty, if it is connected on both sides by ranges mapped to the + * same value those two ranges will be coalesced. + * + *

Note: coalescing requires calling {@code .equals()} on any connected values, which + * may be expensive depending on the value type. Using this method on range maps with large values + * such as {@link Collection} types is discouraged. + * + * @since 22.0 + */ + void putCoalescing(Range range, V value); + + /** Puts all the associations from {@code rangeMap} into this range map (optional operation). */ + void putAll(RangeMap rangeMap); + + /** Removes all associations from this range map (optional operation). */ + void clear(); + + /** + * Removes all associations from this range map in the specified range (optional operation). + * + *

If {@code !range.contains(k)}, {@link #get(Comparable) get(k)} will return the same result + * before and after a call to {@code remove(range)}. If {@code range.contains(k)}, then after a + * call to {@code remove(range)}, {@code get(k)} will return {@code null}. + */ + void remove(Range range); + + /** + * Merges a value into a part of the map by applying a remapping function. + * + *

If any parts of the range are already present in this map, those parts are mapped to new + * values by applying the remapping function. The remapping function accepts the map's existing + * value for that part of the range and the given value. It returns the value to be associated + * with that part of the map, or it returns {@code null} to clear that part of the map. + * + *

Any parts of the range not already present in this map are mapped to the specified value, + * unless the value is {@code null}. + * + *

Any existing entry spanning either range boundary may be split at the boundary, even if the + * merge does not affect its value. For example, if {@code rangeMap} had one entry {@code [1, 5] + * => 3} then {@code rangeMap.merge(Range.closed(0,2), 3, Math::max)} could yield a map with the + * entries {@code [0, 1) => 3, [1, 2] => 3, (2, 5] => 3}. + * + * @since 28.1 + */ + void merge( + Range range, V value, BiFunction remappingFunction); + + /** + * Returns a view of this range map as an unmodifiable {@code Map, V>}. Modifications to + * this range map are guaranteed to read through to the returned {@code Map}. + * + *

The returned {@code Map} iterates over entries in ascending order of the bounds of the + * {@code Range} entries. + * + *

It is guaranteed that no empty ranges will be in the returned {@code Map}. + */ + Map, V> asMapOfRanges(); + + /** + * Returns a view of this range map as an unmodifiable {@code Map, V>}. Modifications to + * this range map are guaranteed to read through to the returned {@code Map}. + * + *

The returned {@code Map} iterates over entries in descending order of the bounds of the + * {@code Range} entries. + * + *

It is guaranteed that no empty ranges will be in the returned {@code Map}. + * + * @since 19.0 + */ + Map, V> asDescendingMapOfRanges(); + + /** + * Returns a view of the part of this range map that intersects with {@code range}. + * + *

For example, if {@code rangeMap} had the entries {@code [1, 5] => "foo", (6, 8) => "bar", + * (10, ∞) => "baz"} then {@code rangeMap.subRangeMap(Range.open(3, 12))} would return a range map + * with the entries {@code (3, 5] => "foo", (6, 8) => "bar", (10, 12) => "baz"}. + * + *

The returned range map supports all optional operations that this range map supports, except + * for {@code asMapOfRanges().iterator().remove()}. + * + *

The returned range map will throw an {@link IllegalArgumentException} on an attempt to + * insert a range not {@linkplain Range#encloses(Range) enclosed} by {@code range}. + */ + // TODO(cpovirk): Consider documenting that IAE on the various methods that can throw it. + RangeMap subRangeMap(Range range); + + /** + * Returns {@code true} if {@code obj} is another {@code RangeMap} that has an equivalent {@link + * #asMapOfRanges()}. + */ + @Override + boolean equals(Object o); + + /** Returns {@code asMapOfRanges().hashCode()}. */ + @Override + int hashCode(); + + /** Returns a readable string representation of this range map. */ + @Override + String toString(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeSet.java new file mode 100644 index 0000000000000..ac4ec3c9570dc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RangeSet.java @@ -0,0 +1,280 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * A set comprising zero or more {@linkplain Range#isEmpty nonempty}, {@linkplain + * Range#isConnected(Range) disconnected} ranges of type {@code C}. + * + *

Implementations that choose to support the {@link #add(Range)} operation are required to + * ignore empty ranges and coalesce connected ranges. For example: + * + *

{@code
+ * RangeSet rangeSet = TreeRangeSet.create();
+ * rangeSet.add(Range.closed(1, 10)); // {[1, 10]}
+ * rangeSet.add(Range.closedOpen(11, 15)); // disconnected range; {[1, 10], [11, 15)}
+ * rangeSet.add(Range.closedOpen(15, 20)); // connected range; {[1, 10], [11, 20)}
+ * rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)}
+ * rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}
+ * }
+ * + *

Note that the behavior of {@link Range#isEmpty()} and {@link Range#isConnected(Range)} may not + * be as expected on discrete ranges. See the Javadoc of those methods for details. + * + *

For a {@link Set} whose contents are specified by a {@link Range}, see {@link ContiguousSet}. + * + *

See the Guava User Guide article on RangeSets. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 14.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface RangeSet { + // TODO(lowasser): consider adding default implementations of some of these methods + + // Query methods + + /** Determines whether any of this range set's member ranges contains {@code value}. */ + boolean contains(C value); + + /** + * Returns the unique range from this range set that {@linkplain Range#contains contains} {@code + * value}, or {@code null} if this range set does not contain {@code value}. + */ + Range rangeContaining(C value); + + /** + * Returns {@code true} if there exists a non-empty range enclosed by both a member range in this + * range set and the specified range. This is equivalent to calling {@code + * subRangeSet(otherRange)} and testing whether the resulting range set is non-empty. + * + * @since 20.0 + */ + boolean intersects(Range otherRange); + + /** + * Returns {@code true} if there exists a member range in this range set which {@linkplain + * Range#encloses encloses} the specified range. + */ + boolean encloses(Range otherRange); + + /** + * Returns {@code true} if for each member range in {@code other} there exists a member range in + * this range set which {@linkplain Range#encloses encloses} it. It follows that {@code + * this.contains(value)} whenever {@code other.contains(value)}. Returns {@code true} if {@code + * other} is empty. + * + *

This is equivalent to checking if this range set {@link #encloses} each of the ranges in + * {@code other}. + */ + boolean enclosesAll(RangeSet other); + + /** + * Returns {@code true} if for each range in {@code other} there exists a member range in this + * range set which {@linkplain Range#encloses encloses} it. Returns {@code true} if {@code other} + * is empty. + * + *

This is equivalent to checking if this range set {@link #encloses} each range in {@code + * other}. + * + * @since 21.0 + */ + default boolean enclosesAll(Iterable> other) { + for (Range range : other) { + if (!encloses(range)) { + return false; + } + } + return true; + } + + /** Returns {@code true} if this range set contains no ranges. */ + boolean isEmpty(); + + /** + * Returns the minimal range which {@linkplain Range#encloses(Range) encloses} all ranges in this + * range set. + * + * @throws NoSuchElementException if this range set is {@linkplain #isEmpty() empty} + */ + Range span(); + + // Views + + /** + * Returns a view of the {@linkplain Range#isConnected disconnected} ranges that make up this + * range set. The returned set may be empty. The iterators returned by its {@link + * Iterable#iterator} method return the ranges in increasing order of lower bound (equivalently, + * of upper bound). + */ + Set> asRanges(); + + /** + * Returns a descending view of the {@linkplain Range#isConnected disconnected} ranges that make + * up this range set. The returned set may be empty. The iterators returned by its {@link + * Iterable#iterator} method return the ranges in decreasing order of lower bound (equivalently, + * of upper bound). + * + * @since 19.0 + */ + Set> asDescendingSetOfRanges(); + + /** + * Returns a view of the complement of this {@code RangeSet}. + * + *

The returned view supports the {@link #add} operation if this {@code RangeSet} supports + * {@link #remove}, and vice versa. + */ + RangeSet complement(); + + /** + * Returns a view of the intersection of this {@code RangeSet} with the specified range. + * + *

The returned view supports all optional operations supported by this {@code RangeSet}, with + * the caveat that an {@link IllegalArgumentException} is thrown on an attempt to {@linkplain + * #add(Range) add} any range not {@linkplain Range#encloses(Range) enclosed} by {@code view}. + */ + RangeSet subRangeSet(Range view); + + // Modification + + /** + * Adds the specified range to this {@code RangeSet} (optional operation). That is, for equal + * range sets a and b, the result of {@code a.add(range)} is that {@code a} will be the minimal + * range set for which both {@code a.enclosesAll(b)} and {@code a.encloses(range)}. + * + *

Note that {@code range} will be {@linkplain Range#span(Range) coalesced} with any ranges in + * the range set that are {@linkplain Range#isConnected(Range) connected} with it. Moreover, if + * {@code range} is empty, this is a no-op. + * + * @throws UnsupportedOperationException if this range set does not support the {@code add} + * operation + */ + void add(Range range); + + /** + * Removes the specified range from this {@code RangeSet} (optional operation). After this + * operation, if {@code range.contains(c)}, {@code this.contains(c)} will return {@code false}. + * + *

If {@code range} is empty, this is a no-op. + * + * @throws UnsupportedOperationException if this range set does not support the {@code remove} + * operation + */ + void remove(Range range); + + /** + * Removes all ranges from this {@code RangeSet} (optional operation). After this operation, + * {@code this.contains(c)} will return false for all {@code c}. + * + *

This is equivalent to {@code remove(Range.all())}. + * + * @throws UnsupportedOperationException if this range set does not support the {@code clear} + * operation + */ + void clear(); + + /** + * Adds all of the ranges from the specified range set to this range set (optional operation). + * After this operation, this range set is the minimal range set that {@linkplain + * #enclosesAll(RangeSet) encloses} both the original range set and {@code other}. + * + *

This is equivalent to calling {@link #add} on each of the ranges in {@code other} in turn. + * + * @throws UnsupportedOperationException if this range set does not support the {@code addAll} + * operation + */ + void addAll(RangeSet other); + + /** + * Adds all of the specified ranges to this range set (optional operation). After this operation, + * this range set is the minimal range set that {@linkplain #enclosesAll(RangeSet) encloses} both + * the original range set and each range in {@code other}. + * + *

This is equivalent to calling {@link #add} on each of the ranges in {@code other} in turn. + * + * @throws UnsupportedOperationException if this range set does not support the {@code addAll} + * operation + * @since 21.0 + */ + default void addAll(Iterable> ranges) { + for (Range range : ranges) { + add(range); + } + } + + /** + * Removes all of the ranges from the specified range set from this range set (optional + * operation). After this operation, if {@code other.contains(c)}, {@code this.contains(c)} will + * return {@code false}. + * + *

This is equivalent to calling {@link #remove} on each of the ranges in {@code other} in + * turn. + * + * @throws UnsupportedOperationException if this range set does not support the {@code removeAll} + * operation + */ + void removeAll(RangeSet other); + + /** + * Removes all of the specified ranges from this range set (optional operation). + * + *

This is equivalent to calling {@link #remove} on each of the ranges in {@code other} in + * turn. + * + * @throws UnsupportedOperationException if this range set does not support the {@code removeAll} + * operation + * @since 21.0 + */ + default void removeAll(Iterable> ranges) { + for (Range range : ranges) { + remove(range); + } + } + + // Object methods + + /** + * Returns {@code true} if {@code obj} is another {@code RangeSet} that contains the same ranges + * according to {@link Range#equals(Object)}. + */ + @Override + boolean equals(Object obj); + + /** Returns {@code asRanges().hashCode()}. */ + @Override + int hashCode(); + + /** + * Returns a readable string representation of this range set. For example, if this {@code + * RangeSet} consisted of {@code Range.closed(1, 3)} and {@code Range.greaterThan(4)}, this might + * return {@code " [1..3](4..+∞)}"}. + */ + @Override + String toString(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularContiguousSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularContiguousSet.java new file mode 100644 index 0000000000000..263c4e584e97f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularContiguousSet.java @@ -0,0 +1,259 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Collection; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.BoundType.CLOSED; + +/** + * An implementation of {@link ContiguousSet} that contains one or more elements. + * + * @author Gregory Kick + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("unchecked") // allow ungenerified Comparable types +@ElementTypesAreNonnullByDefault +final class RegularContiguousSet extends ContiguousSet { + private final Range range; + + RegularContiguousSet(Range range, DiscreteDomain domain) { + super(domain); + this.range = range; + } + + private ContiguousSet intersectionInCurrentDomain(Range other) { + return range.isConnected(other) + ? ContiguousSet.create(range.intersection(other), domain) + : new EmptyContiguousSet(domain); + } + + @Override + ContiguousSet headSetImpl(C toElement, boolean inclusive) { + return intersectionInCurrentDomain(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + } + + @Override + ContiguousSet subSetImpl( + C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + if (fromElement.compareTo(toElement) == 0 && !fromInclusive && !toInclusive) { + // Range would reject our attempt to create (x, x). + return new EmptyContiguousSet<>(domain); + } + return intersectionInCurrentDomain( + Range.range( + fromElement, BoundType.forBoolean(fromInclusive), + toElement, BoundType.forBoolean(toInclusive))); + } + + @Override + ContiguousSet tailSetImpl(C fromElement, boolean inclusive) { + return intersectionInCurrentDomain(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + } + + @GwtIncompatible // not used by GWT emulation + @Override + int indexOf(Object target) { + // requireNonNull is safe because of the contains check. + return contains(target) ? (int) domain.distance(first(), (C) requireNonNull(target)) : -1; + } + + @Override + public UnmodifiableIterator iterator() { + return new AbstractSequentialIterator(first()) { + final C last = last(); + + @Override + protected C computeNext(C previous) { + return equalsOrThrow(previous, last) ? null : domain.next(previous); + } + }; + } + + @GwtIncompatible // NavigableSet + @Override + public UnmodifiableIterator descendingIterator() { + return new AbstractSequentialIterator(last()) { + final C first = first(); + + @Override + protected C computeNext(C previous) { + return equalsOrThrow(previous, first) ? null : domain.previous(previous); + } + }; + } + + private static boolean equalsOrThrow(Comparable left, Comparable right) { + return right != null && Range.compareOrThrow(left, right) == 0; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public C first() { + // requireNonNull is safe because we checked the range is not empty in ContiguousSet.create. + return requireNonNull(range.lowerBound.leastValueAbove(domain)); + } + + @Override + public C last() { + // requireNonNull is safe because we checked the range is not empty in ContiguousSet.create. + return requireNonNull(range.upperBound.greatestValueBelow(domain)); + } + + @Override + ImmutableList createAsList() { + if (domain.supportsFastOffset) { + return new ImmutableAsList() { + @Override + ImmutableSortedSet delegateCollection() { + return RegularContiguousSet.this; + } + + @Override + public C get(int i) { + checkElementIndex(i, size()); + return domain.offset(first(), i); + } + }; + } else { + return super.createAsList(); + } + } + + @Override + public int size() { + long distance = domain.distance(first(), last()); + return (distance >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) distance + 1; + } + + @Override + public boolean contains(Object object) { + if (object == null) { + return false; + } + try { + return range.contains((C) object); + } catch (ClassCastException e) { + return false; + } + } + + @Override + public boolean containsAll(Collection targets) { + return Collections2.containsAllImpl(this, targets); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public ContiguousSet intersection(ContiguousSet other) { + checkNotNull(other); + checkArgument(this.domain.equals(other.domain)); + if (other.isEmpty()) { + return other; + } else { + C lowerEndpoint = Ordering.natural().max(this.first(), other.first()); + C upperEndpoint = Ordering.natural().min(this.last(), other.last()); + return (lowerEndpoint.compareTo(upperEndpoint) <= 0) + ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), domain) + : new EmptyContiguousSet(domain); + } + } + + @Override + public Range range() { + return range(CLOSED, CLOSED); + } + + @Override + public Range range(BoundType lowerBoundType, BoundType upperBoundType) { + return Range.create( + range.lowerBound.withLowerBoundType(lowerBoundType, domain), + range.upperBound.withUpperBoundType(upperBoundType, domain)); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof RegularContiguousSet) { + RegularContiguousSet that = (RegularContiguousSet) object; + if (this.domain.equals(that.domain)) { + return this.first().equals(that.first()) && this.last().equals(that.last()); + } + } + return super.equals(object); + } + + // copied to make sure not to use the GWT-emulated version + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private static final class SerializedForm implements Serializable { + final Range range; + final DiscreteDomain domain; + + private SerializedForm(Range range, DiscreteDomain domain) { + this.range = range; + this.domain = domain; + } + + private Object readResolve() { + return new RegularContiguousSet<>(range, domain); + } + } + + @GwtIncompatible // serialization + @J2ktIncompatible + @Override + Object writeReplace() { + return new SerializedForm<>(range, domain); + } + + @GwtIncompatible // serialization + @J2ktIncompatible + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableAsList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableAsList.java new file mode 100644 index 0000000000000..c30727b8ca8d5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableAsList.java @@ -0,0 +1,95 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.function.Consumer; + +/** + * An {@link ImmutableAsList} implementation specialized for when the delegate collection is already + * backed by an {@code ImmutableList} or array. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@SuppressWarnings("serial") // uses writeReplace, not default serialization +@ElementTypesAreNonnullByDefault +class RegularImmutableAsList extends ImmutableAsList { + private final ImmutableCollection delegate; + private final ImmutableList delegateList; + + RegularImmutableAsList(ImmutableCollection delegate, ImmutableList delegateList) { + this.delegate = delegate; + this.delegateList = delegateList; + } + + RegularImmutableAsList(ImmutableCollection delegate, Object[] array) { + this(delegate, ImmutableList.asImmutableList(array)); + } + + @Override + ImmutableCollection delegateCollection() { + return delegate; + } + + ImmutableList delegateList() { + return delegateList; + } + + @SuppressWarnings("unchecked") // safe covariant cast! + @Override + public UnmodifiableListIterator listIterator(int index) { + return (UnmodifiableListIterator) delegateList.listIterator(index); + } + + @GwtIncompatible // not present in emulated superclass + @Override + public void forEach(Consumer action) { + delegateList.forEach(action); + } + + @GwtIncompatible // not present in emulated superclass + @Override + int copyIntoArray(Object[] dst, int offset) { + return delegateList.copyIntoArray(dst, offset); + } + + @Override + Object[] internalArray() { + return delegateList.internalArray(); + } + + @Override + int internalArrayStart() { + return delegateList.internalArrayStart(); + } + + @Override + int internalArrayEnd() { + return delegateList.internalArrayEnd(); + } + + @Override + public E get(int index) { + return delegateList.get(index); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableBiMap.java new file mode 100644 index 0000000000000..30ec7681d4ee1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableBiMap.java @@ -0,0 +1,321 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.collect.ImmutableMapEntry.NonTerminalImmutableBiMapEntry; +import org.apache.iotdb.google.common.collect.RegularImmutableMap.BucketOverflowException; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static org.apache.iotdb.google.common.collect.ImmutableMapEntry.createEntryArray; +import static org.apache.iotdb.google.common.collect.RegularImmutableMap.MAX_HASH_BUCKET_LENGTH; +import static org.apache.iotdb.google.common.collect.RegularImmutableMap.checkNoConflictInKeyBucket; + +/** + * Bimap with zero or more mappings. + * + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +class RegularImmutableBiMap extends ImmutableBiMap { + static final RegularImmutableBiMap EMPTY = + new RegularImmutableBiMap<>( + null, null, (Entry[]) ImmutableMap.EMPTY_ENTRY_ARRAY, 0, 0); + + static final double MAX_LOAD_FACTOR = 1.2; + + private final transient ImmutableMapEntry[] keyTable; + private final transient ImmutableMapEntry[] valueTable; + @VisibleForTesting final transient Entry[] entries; + private final transient int mask; + private final transient int hashCode; + + static ImmutableBiMap fromEntries(Entry... entries) { + return fromEntryArray(entries.length, entries); + } + + static ImmutableBiMap fromEntryArray(int n, Entry[] entryArray) { + checkPositionIndex(n, entryArray.length); + int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); + int mask = tableSize - 1; + ImmutableMapEntry[] keyTable = createEntryArray(tableSize); + ImmutableMapEntry[] valueTable = createEntryArray(tableSize); + /* + * The cast is safe: n==entryArray.length means that we have filled the whole array with Entry + * instances, in which case it is safe to cast it from an array of nullable entries to an array + * of non-null entries. + */ + @SuppressWarnings("nullness") + Entry[] entries = + (n == entryArray.length) ? (Entry[]) entryArray : createEntryArray(n); + int hashCode = 0; + + for (int i = 0; i < n; i++) { + // requireNonNull is safe because the first `n` elements have been filled in. + Entry entry = requireNonNull(entryArray[i]); + K key = entry.getKey(); + V value = entry.getValue(); + checkEntryNotNull(key, value); + int keyHash = key.hashCode(); + int valueHash = value.hashCode(); + int keyBucket = Hashing.smear(keyHash) & mask; + int valueBucket = Hashing.smear(valueHash) & mask; + + ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; + ImmutableMapEntry nextInValueBucket = valueTable[valueBucket]; + try { + checkNoConflictInKeyBucket(key, value, nextInKeyBucket, /* throwIfDuplicateKeys= */ true); + checkNoConflictInValueBucket(value, entry, nextInValueBucket); + } catch (BucketOverflowException e) { + return JdkBackedImmutableBiMap.create(n, entryArray); + } + ImmutableMapEntry newEntry = + (nextInValueBucket == null && nextInKeyBucket == null) + ? RegularImmutableMap.makeImmutable(entry, key, value) + : new NonTerminalImmutableBiMapEntry<>( + key, value, nextInKeyBucket, nextInValueBucket); + keyTable[keyBucket] = newEntry; + valueTable[valueBucket] = newEntry; + entries[i] = newEntry; + hashCode += keyHash ^ valueHash; + } + return new RegularImmutableBiMap<>(keyTable, valueTable, entries, mask, hashCode); + } + + private RegularImmutableBiMap( + ImmutableMapEntry[] keyTable, + ImmutableMapEntry[] valueTable, + Entry[] entries, + int mask, + int hashCode) { + this.keyTable = keyTable; + this.valueTable = valueTable; + this.entries = entries; + this.mask = mask; + this.hashCode = hashCode; + } + + // checkNoConflictInKeyBucket is static imported from RegularImmutableMap + + /** + * @throws IllegalArgumentException if another entry in the bucket has the same key + * @throws BucketOverflowException if this bucket has too many entries, which may indicate a hash + * flooding attack + */ + private static void checkNoConflictInValueBucket( + Object value, Entry entry, ImmutableMapEntry valueBucketHead) + throws BucketOverflowException { + int bucketSize = 0; + for (; valueBucketHead != null; valueBucketHead = valueBucketHead.getNextInValueBucket()) { + checkNoConflict(!value.equals(valueBucketHead.getValue()), "value", entry, valueBucketHead); + if (++bucketSize > MAX_HASH_BUCKET_LENGTH) { + throw new BucketOverflowException(); + } + } + } + + @Override + public V get(Object key) { + return RegularImmutableMap.get(key, keyTable, mask); + } + + @Override + ImmutableSet> createEntrySet() { + return isEmpty() + ? ImmutableSet.>of() + : new ImmutableMapEntrySet.RegularEntrySet(this, entries); + } + + @Override + ImmutableSet createKeySet() { + return new ImmutableMapKeySet<>(this); + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + for (Entry entry : entries) { + action.accept(entry.getKey(), entry.getValue()); + } + } + + @Override + boolean isHashCodeFast() { + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int size() { + return entries.length; + } + + private transient ImmutableBiMap inverse; + + @Override + public ImmutableBiMap inverse() { + if (isEmpty()) { + return ImmutableBiMap.of(); + } + ImmutableBiMap result = inverse; + return (result == null) ? inverse = new Inverse() : result; + } + + private final class Inverse extends ImmutableBiMap { + + @Override + public int size() { + return inverse().size(); + } + + @Override + public ImmutableBiMap inverse() { + return RegularImmutableBiMap.this; + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + RegularImmutableBiMap.this.forEach((k, v) -> action.accept(v, k)); + } + + @Override + public K get(Object value) { + if (value == null || valueTable == null) { + return null; + } + int bucket = Hashing.smear(value.hashCode()) & mask; + for (ImmutableMapEntry entry = valueTable[bucket]; + entry != null; + entry = entry.getNextInValueBucket()) { + if (value.equals(entry.getValue())) { + return entry.getKey(); + } + } + return null; + } + + @Override + ImmutableSet createKeySet() { + return new ImmutableMapKeySet<>(this); + } + + @Override + ImmutableSet> createEntrySet() { + return new InverseEntrySet(); + } + + final class InverseEntrySet extends ImmutableMapEntrySet { + @Override + ImmutableMap map() { + return Inverse.this; + } + + @Override + boolean isHashCodeFast() { + return true; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + public void forEach(Consumer> action) { + asList().forEach(action); + } + + @Override + ImmutableList> createAsList() { + return new ImmutableAsList>() { + @Override + public Entry get(int index) { + Entry entry = entries[index]; + return Maps.immutableEntry(entry.getValue(), entry.getKey()); + } + + @Override + ImmutableCollection> delegateCollection() { + return InverseEntrySet.this; + } + }; + } + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + @J2ktIncompatible // serialization + Object writeReplace() { + return new InverseSerializedForm<>(RegularImmutableBiMap.this); + } + + @J2ktIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use InverseSerializedForm"); + } + } + + @J2ktIncompatible // serialization + private static class InverseSerializedForm implements Serializable { + private final ImmutableBiMap forward; + + InverseSerializedForm(ImmutableBiMap forward) { + this.forward = forward; + } + + Object readResolve() { + return forward.inverse(); + } + + private static final long serialVersionUID = 1; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableList.java new file mode 100644 index 0000000000000..a9c89ff030f0a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableList.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.Spliterator; +import java.util.Spliterators; + +/** + * Implementation of {@link ImmutableList} backed by a simple array. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +class RegularImmutableList extends ImmutableList { + static final ImmutableList EMPTY = new RegularImmutableList<>(new Object[0]); + + @VisibleForTesting final transient Object[] array; + + RegularImmutableList(Object[] array) { + this.array = array; + } + + @Override + public int size() { + return array.length; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + Object[] internalArray() { + return array; + } + + @Override + int internalArrayStart() { + return 0; + } + + @Override + int internalArrayEnd() { + return array.length; + } + + @Override + int copyIntoArray(Object[] dst, int dstOff) { + System.arraycopy(array, 0, dst, dstOff, array.length); + return dstOff + array.length; + } + + // The fake cast to E is safe because the creation methods only allow E's + @Override + @SuppressWarnings("unchecked") + public E get(int index) { + return (E) array[index]; + } + + @SuppressWarnings("unchecked") + @Override + public UnmodifiableListIterator listIterator(int index) { + // for performance + // The fake cast to E is safe because the creation methods only allow E's + return (UnmodifiableListIterator) Iterators.forArray(array, 0, array.length, index); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(array, SPLITERATOR_CHARACTERISTICS); + } + + // TODO(lowasser): benchmark optimizations for equals() and see if they're worthwhile +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableMap.java new file mode 100644 index 0000000000000..b5361df2d7460 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableMap.java @@ -0,0 +1,414 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.collect.ImmutableMapEntry.NonTerminalImmutableMapEntry; + +import java.io.Serializable; +import java.util.IdentityHashMap; +import java.util.function.BiConsumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; +import static org.apache.iotdb.google.common.collect.ImmutableMapEntry.createEntryArray; + +/** + * Implementation of {@link ImmutableMap} with two or more entries. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @author Gregory Kick + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +final class RegularImmutableMap extends ImmutableMap { + @SuppressWarnings("unchecked") + static final ImmutableMap EMPTY = + new RegularImmutableMap<>((Entry[]) ImmutableMap.EMPTY_ENTRY_ARRAY, null, 0); + + /** + * Closed addressing tends to perform well even with high load factors. Being conservative here + * ensures that the table is still likely to be relatively sparse (hence it misses fast) while + * saving space. + */ + @VisibleForTesting static final double MAX_LOAD_FACTOR = 1.2; + + /** + * Maximum allowed false positive probability of detecting a hash flooding attack given random + * input. + */ + @VisibleForTesting static final double HASH_FLOODING_FPP = 0.001; + + /** + * Maximum allowed length of a hash table bucket before falling back to a j.u.HashMap based + * implementation. Experimentally determined. + */ + @VisibleForTesting static final int MAX_HASH_BUCKET_LENGTH = 8; + + // entries in insertion order + @VisibleForTesting final transient Entry[] entries; + // array of linked lists of entries + private final transient ImmutableMapEntry[] table; + // 'and' with an int to get a table index + private final transient int mask; + + static ImmutableMap fromEntries(Entry... entries) { + return fromEntryArray(entries.length, entries, /* throwIfDuplicateKeys= */ true); + } + + /** + * Creates an ImmutableMap from the first n entries in entryArray. This implementation may replace + * the entries in entryArray with its own entry objects (though they will have the same key/value + * contents), and may take ownership of entryArray. + */ + static ImmutableMap fromEntryArray( + int n, Entry[] entryArray, boolean throwIfDuplicateKeys) { + checkPositionIndex(n, entryArray.length); + if (n == 0) { + @SuppressWarnings("unchecked") // it has no entries so the type variables don't matter + ImmutableMap empty = (ImmutableMap) EMPTY; + return empty; + } + try { + return fromEntryArrayCheckingBucketOverflow(n, entryArray, throwIfDuplicateKeys); + } catch (BucketOverflowException e) { + // probable hash flooding attack, fall back to j.u.HM based implementation and use its + // implementation of hash flooding protection + return JdkBackedImmutableMap.create(n, entryArray, throwIfDuplicateKeys); + } + } + + private static ImmutableMap fromEntryArrayCheckingBucketOverflow( + int n, Entry[] entryArray, boolean throwIfDuplicateKeys) + throws BucketOverflowException { + /* + * The cast is safe: n==entryArray.length means that we have filled the whole array with Entry + * instances, in which case it is safe to cast it from an array of nullable entries to an array + * of non-null entries. + */ + @SuppressWarnings("nullness") + Entry[] entries = + (n == entryArray.length) ? (Entry[]) entryArray : createEntryArray(n); + int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); + ImmutableMapEntry[] table = createEntryArray(tableSize); + int mask = tableSize - 1; + // If duplicates are allowed, this IdentityHashMap will record the final Entry for each + // duplicated key. We will use this final Entry to overwrite earlier slots in the entries array + // that have the same key. Then a second pass will remove all but the first of the slots that + // have this Entry. The value in the map becomes false when this first entry has been copied, so + // we know not to copy the remaining ones. + IdentityHashMap, Boolean> duplicates = null; + int dupCount = 0; + for (int entryIndex = n - 1; entryIndex >= 0; entryIndex--) { + // requireNonNull is safe because the first `n` elements have been filled in. + Entry entry = requireNonNull(entryArray[entryIndex]); + K key = entry.getKey(); + V value = entry.getValue(); + checkEntryNotNull(key, value); + int tableIndex = Hashing.smear(key.hashCode()) & mask; + ImmutableMapEntry keyBucketHead = table[tableIndex]; + ImmutableMapEntry effectiveEntry = + checkNoConflictInKeyBucket(key, value, keyBucketHead, throwIfDuplicateKeys); + if (effectiveEntry == null) { + // prepend, not append, so the entries can be immutable + effectiveEntry = + (keyBucketHead == null) + ? makeImmutable(entry, key, value) + : new NonTerminalImmutableMapEntry(key, value, keyBucketHead); + table[tableIndex] = effectiveEntry; + } else { + // We already saw this key, and the first value we saw (going backwards) is the one we are + // keeping. So we won't touch table[], but we do still want to add the existing entry that + // we found to entries[] so that we will see this key in the right place when iterating. + if (duplicates == null) { + duplicates = new IdentityHashMap<>(); + } + duplicates.put(effectiveEntry, true); + dupCount++; + // Make sure we are not overwriting the original entries array, in case we later do + // buildOrThrow(). We would want an exception to include two values for the duplicate key. + if (entries == entryArray) { + // Temporary variable is necessary to defeat bad smartcast (entries adopting the type of + // entryArray) in the Kotlin translation. + Entry[] originalEntries = entries; + entries = originalEntries.clone(); + } + } + entries[entryIndex] = effectiveEntry; + } + if (duplicates != null) { + // Explicit type parameters needed here to avoid a problem with nullness inference. + entries = RegularImmutableMap.removeDuplicates(entries, n, n - dupCount, duplicates); + int newTableSize = Hashing.closedTableSize(entries.length, MAX_LOAD_FACTOR); + if (newTableSize != tableSize) { + return fromEntryArrayCheckingBucketOverflow( + entries.length, entries, /* throwIfDuplicateKeys= */ true); + } + } + return new RegularImmutableMap<>(entries, table, mask); + } + + /** + * Constructs a new entry array where each duplicated key from the original appears only once, at + * its first position but with its final value. The {@code duplicates} map is modified. + * + * @param entries the original array of entries including duplicates + * @param n the number of valid entries in {@code entries} + * @param newN the expected number of entries once duplicates are removed + * @param duplicates a map of canonical {@link Entry} objects for each duplicate key. This map + * will be updated by the method, setting each value to false as soon as the {@link Entry} has + * been included in the new entry array. + * @return an array of {@code newN} entries where no key appears more than once. + */ + static Entry[] removeDuplicates( + Entry[] entries, int n, int newN, IdentityHashMap, Boolean> duplicates) { + Entry[] newEntries = createEntryArray(newN); + for (int in = 0, out = 0; in < n; in++) { + Entry entry = entries[in]; + Boolean status = duplicates.get(entry); + // null=>not dup'd; true=>dup'd, first; false=>dup'd, not first + if (status != null) { + if (status) { + duplicates.put(entry, false); + } else { + continue; // delete this entry; we already copied an earlier one for the same key + } + } + newEntries[out++] = entry; + } + return newEntries; + } + + /** Makes an entry usable internally by a new ImmutableMap without rereading its contents. */ + static ImmutableMapEntry makeImmutable(Entry entry, K key, V value) { + boolean reusable = + entry instanceof ImmutableMapEntry && ((ImmutableMapEntry) entry).isReusable(); + return reusable ? (ImmutableMapEntry) entry : new ImmutableMapEntry(key, value); + } + + /** Makes an entry usable internally by a new ImmutableMap. */ + static ImmutableMapEntry makeImmutable(Entry entry) { + return makeImmutable(entry, entry.getKey(), entry.getValue()); + } + + private RegularImmutableMap(Entry[] entries, ImmutableMapEntry[] table, int mask) { + this.entries = entries; + this.table = table; + this.mask = mask; + } + + /** + * Checks if the given key already appears in the hash chain starting at {@code keyBucketHead}. If + * it does not, then null is returned. If it does, then if {@code throwIfDuplicateKeys} is true an + * {@code IllegalArgumentException} is thrown, and otherwise the existing {@link Entry} is + * returned. + * + * @throws IllegalArgumentException if another entry in the bucket has the same key and {@code + * throwIfDuplicateKeys} is true + * @throws BucketOverflowException if this bucket has too many entries, which may indicate a hash + * flooding attack + */ + static ImmutableMapEntry checkNoConflictInKeyBucket( + Object key, + Object newValue, + ImmutableMapEntry keyBucketHead, + boolean throwIfDuplicateKeys) + throws BucketOverflowException { + int bucketSize = 0; + for (; keyBucketHead != null; keyBucketHead = keyBucketHead.getNextInKeyBucket()) { + if (keyBucketHead.getKey().equals(key)) { + if (throwIfDuplicateKeys) { + checkNoConflict(/* safe= */ false, "key", keyBucketHead, key + "=" + newValue); + } else { + return keyBucketHead; + } + } + if (++bucketSize > MAX_HASH_BUCKET_LENGTH) { + throw new BucketOverflowException(); + } + } + return null; + } + + static class BucketOverflowException extends Exception {} + + @Override + public V get(Object key) { + return get(key, table, mask); + } + + static V get(Object key, ImmutableMapEntry[] keyTable, int mask) { + if (key == null || keyTable == null) { + return null; + } + int index = Hashing.smear(key.hashCode()) & mask; + for (ImmutableMapEntry entry = keyTable[index]; + entry != null; + entry = entry.getNextInKeyBucket()) { + Object candidateKey = entry.getKey(); + + /* + * Assume that equals uses the == optimization when appropriate, and that + * it would check hash codes as an optimization when appropriate. If we + * did these things, it would just make things worse for the most + * performance-conscious users. + */ + if (key.equals(candidateKey)) { + return entry.getValue(); + } + } + return null; + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action); + for (Entry entry : entries) { + action.accept(entry.getKey(), entry.getValue()); + } + } + + @Override + public int size() { + return entries.length; + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet.RegularEntrySet<>(this, entries); + } + + @Override + ImmutableSet createKeySet() { + return new KeySet<>(this); + } + + @GwtCompatible(emulated = true) + private static final class KeySet extends IndexedImmutableSet { + private final RegularImmutableMap map; + + KeySet(RegularImmutableMap map) { + this.map = map; + } + + @Override + K get(int index) { + return map.entries[index].getKey(); + } + + @Override + public boolean contains(Object object) { + return map.containsKey(object); + } + + @Override + boolean isPartialView() { + return true; + } + + @Override + public int size() { + return map.size(); + } + + // No longer used for new writes, but kept so that old data can still be read. + @GwtIncompatible // serialization + @J2ktIncompatible + @SuppressWarnings("unused") + private static class SerializedForm implements Serializable { + final ImmutableMap map; + + SerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return map.keySet(); + } + + @J2ktIncompatible // serialization + private static final long serialVersionUID = 0; + } + } + + @Override + ImmutableCollection createValues() { + return new Values<>(this); + } + + @GwtCompatible(emulated = true) + private static final class Values extends ImmutableList { + final RegularImmutableMap map; + + Values(RegularImmutableMap map) { + this.map = map; + } + + @Override + public V get(int index) { + return map.entries[index].getValue(); + } + + @Override + public int size() { + return map.size(); + } + + @Override + boolean isPartialView() { + return true; + } + + // No longer used for new writes, but kept so that old data can still be read. + @GwtIncompatible // serialization + @J2ktIncompatible + @SuppressWarnings("unused") + private static class SerializedForm implements Serializable { + final ImmutableMap map; + + SerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return map.values(); + } + + @J2ktIncompatible // serialization + private static final long serialVersionUID = 0; + } + } + + // This class is never actually serialized directly, but we have to make the + // warning go away (and suppressing would suppress for all nested classes too) + @J2ktIncompatible // serialization + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableMultiset.java new file mode 100644 index 0000000000000..22fcf78e98c66 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableMultiset.java @@ -0,0 +1,199 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Multisets.ImmutableEntry; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.Arrays; +import java.util.Collection; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@link ImmutableMultiset} with zero or more elements. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true, serializable = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +class RegularImmutableMultiset extends ImmutableMultiset { + private static final ImmutableEntry[] EMPTY_ARRAY = new ImmutableEntry[0]; + static final ImmutableMultiset EMPTY = create(ImmutableList.>of()); + + static ImmutableMultiset create(Collection> entries) { + int distinct = entries.size(); + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableEntry[] entryArray = new ImmutableEntry[distinct]; + if (distinct == 0) { + return new RegularImmutableMultiset<>(entryArray, EMPTY_ARRAY, 0, 0, ImmutableSet.of()); + } + int tableSize = Hashing.closedTableSize(distinct, MAX_LOAD_FACTOR); + int mask = tableSize - 1; + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableEntry[] hashTable = new ImmutableEntry[tableSize]; + + int index = 0; + int hashCode = 0; + long size = 0; + for (Entry entryWithWildcard : entries) { + @SuppressWarnings("unchecked") // safe because we only read from it + Entry entry = (Entry) entryWithWildcard; + E element = checkNotNull(entry.getElement()); + int count = entry.getCount(); + int hash = element.hashCode(); + int bucket = Hashing.smear(hash) & mask; + ImmutableEntry bucketHead = hashTable[bucket]; + ImmutableEntry newEntry; + if (bucketHead == null) { + boolean canReuseEntry = + entry instanceof ImmutableEntry && !(entry instanceof NonTerminalEntry); + newEntry = + canReuseEntry ? (ImmutableEntry) entry : new ImmutableEntry(element, count); + } else { + newEntry = new NonTerminalEntry(element, count, bucketHead); + } + hashCode += hash ^ count; + entryArray[index++] = newEntry; + hashTable[bucket] = newEntry; + size += count; + } + + return hashFloodingDetected(hashTable) + ? JdkBackedImmutableMultiset.create(ImmutableList.asImmutableList(entryArray)) + : new RegularImmutableMultiset( + entryArray, hashTable, Ints.saturatedCast(size), hashCode, null); + } + + private static boolean hashFloodingDetected(ImmutableEntry[] hashTable) { + for (int i = 0; i < hashTable.length; i++) { + int bucketLength = 0; + for (ImmutableEntry entry = hashTable[i]; entry != null; entry = entry.nextInBucket()) { + bucketLength++; + if (bucketLength > MAX_HASH_BUCKET_LENGTH) { + return true; + } + } + } + return false; + } + + /** + * Closed addressing tends to perform well even with high load factors. Being conservative here + * ensures that the table is still likely to be relatively sparse (hence it misses fast) while + * saving space. + */ + @VisibleForTesting static final double MAX_LOAD_FACTOR = 1.0; + + /** + * Maximum allowed false positive probability of detecting a hash flooding attack given random + * input. + */ + @VisibleForTesting static final double HASH_FLOODING_FPP = 0.001; + + /** + * Maximum allowed length of a hash table bucket before falling back to a j.u.HashMap based + * implementation. Experimentally determined. + */ + @VisibleForTesting static final int MAX_HASH_BUCKET_LENGTH = 9; + + private final transient ImmutableEntry[] entries; + private final transient ImmutableEntry[] hashTable; + private final transient int size; + private final transient int hashCode; + + private transient ImmutableSet elementSet; + + private RegularImmutableMultiset( + ImmutableEntry[] entries, + ImmutableEntry[] hashTable, + int size, + int hashCode, + ImmutableSet elementSet) { + this.entries = entries; + this.hashTable = hashTable; + this.size = size; + this.hashCode = hashCode; + this.elementSet = elementSet; + } + + private static final class NonTerminalEntry extends ImmutableEntry { + private final ImmutableEntry nextInBucket; + + NonTerminalEntry(E element, int count, ImmutableEntry nextInBucket) { + super(element, count); + this.nextInBucket = nextInBucket; + } + + @Override + public ImmutableEntry nextInBucket() { + return nextInBucket; + } + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int count(Object element) { + ImmutableEntry[] hashTable = this.hashTable; + if (element == null || hashTable.length == 0) { + return 0; + } + int hash = Hashing.smearedHash(element); + int mask = hashTable.length - 1; + for (ImmutableEntry entry = hashTable[hash & mask]; + entry != null; + entry = entry.nextInBucket()) { + if (Objects.equal(element, entry.getElement())) { + return entry.getCount(); + } + } + return 0; + } + + @Override + public int size() { + return size; + } + + @Override + public ImmutableSet elementSet() { + ImmutableSet result = elementSet; + return (result == null) ? elementSet = new ElementSet(Arrays.asList(entries), this) : result; + } + + @Override + Entry getEntry(int index) { + return entries[index]; + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSet.java new file mode 100644 index 0000000000000..f58e66d8b4684 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSet.java @@ -0,0 +1,129 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.Spliterator; +import java.util.Spliterators; + +/** + * Implementation of {@link ImmutableSet} with two or more elements. + * + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +final class RegularImmutableSet extends ImmutableSet.CachingAsList { + private static final Object[] EMPTY_ARRAY = new Object[0]; + static final RegularImmutableSet EMPTY = + new RegularImmutableSet<>(EMPTY_ARRAY, 0, EMPTY_ARRAY, 0); + + private final transient Object[] elements; + private final transient int hashCode; + // the same values as `elements` in hashed positions (plus nulls) + @VisibleForTesting final transient Object[] table; + // 'and' with an int to get a valid table index. + private final transient int mask; + + RegularImmutableSet(Object[] elements, int hashCode, Object[] table, int mask) { + this.elements = elements; + this.hashCode = hashCode; + this.table = table; + this.mask = mask; + } + + @Override + public boolean contains(Object target) { + Object[] table = this.table; + if (target == null || table.length == 0) { + return false; + } + for (int i = Hashing.smearedHash(target); ; i++) { + i &= mask; + Object candidate = table[i]; + if (candidate == null) { + return false; + } else if (candidate.equals(target)) { + return true; + } + } + } + + @Override + public int size() { + return elements.length; + } + + @Override + public UnmodifiableIterator iterator() { + return (UnmodifiableIterator) Iterators.forArray(elements); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(elements, SPLITERATOR_CHARACTERISTICS); + } + + @Override + Object[] internalArray() { + return elements; + } + + @Override + int internalArrayStart() { + return 0; + } + + @Override + int internalArrayEnd() { + return elements.length; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + System.arraycopy(elements, 0, dst, offset, elements.length); + return offset + elements.length; + } + + @Override + ImmutableList createAsList() { + return (table.length == 0) + ? ImmutableList.of() + : new RegularImmutableAsList(this, elements); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + boolean isHashCodeFast() { + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSortedMultiset.java new file mode 100644 index 0000000000000..5d790079d245a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSortedMultiset.java @@ -0,0 +1,139 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.Comparator; +import java.util.function.ObjIntConsumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.collect.BoundType.CLOSED; + +/** + * An immutable sorted multiset with one or more distinct elements. + * + * @author Louis Wasserman + */ +@SuppressWarnings("serial") // uses writeReplace, not default serialization +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class RegularImmutableSortedMultiset extends ImmutableSortedMultiset { + private static final long[] ZERO_CUMULATIVE_COUNTS = {0}; + + static final ImmutableSortedMultiset NATURAL_EMPTY_MULTISET = + new RegularImmutableSortedMultiset<>(Ordering.natural()); + + @VisibleForTesting final transient RegularImmutableSortedSet elementSet; + private final transient long[] cumulativeCounts; + private final transient int offset; + private final transient int length; + + RegularImmutableSortedMultiset(Comparator comparator) { + this.elementSet = ImmutableSortedSet.emptySet(comparator); + this.cumulativeCounts = ZERO_CUMULATIVE_COUNTS; + this.offset = 0; + this.length = 0; + } + + RegularImmutableSortedMultiset( + RegularImmutableSortedSet elementSet, long[] cumulativeCounts, int offset, int length) { + this.elementSet = elementSet; + this.cumulativeCounts = cumulativeCounts; + this.offset = offset; + this.length = length; + } + + private int getCount(int index) { + return (int) (cumulativeCounts[offset + index + 1] - cumulativeCounts[offset + index]); + } + + @Override + Entry getEntry(int index) { + return Multisets.immutableEntry(elementSet.asList().get(index), getCount(index)); + } + + @Override + public void forEachEntry(ObjIntConsumer action) { + checkNotNull(action); + for (int i = 0; i < length; i++) { + action.accept(elementSet.asList().get(i), getCount(i)); + } + } + + @Override + public Entry firstEntry() { + return isEmpty() ? null : getEntry(0); + } + + @Override + public Entry lastEntry() { + return isEmpty() ? null : getEntry(length - 1); + } + + @Override + public int count(Object element) { + int index = elementSet.indexOf(element); + return (index >= 0) ? getCount(index) : 0; + } + + @Override + public int size() { + long size = cumulativeCounts[offset + length] - cumulativeCounts[offset]; + return Ints.saturatedCast(size); + } + + @Override + public ImmutableSortedSet elementSet() { + return elementSet; + } + + @Override + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + return getSubMultiset(0, elementSet.headIndex(upperBound, checkNotNull(boundType) == CLOSED)); + } + + @Override + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return getSubMultiset( + elementSet.tailIndex(lowerBound, checkNotNull(boundType) == CLOSED), length); + } + + ImmutableSortedMultiset getSubMultiset(int from, int to) { + checkPositionIndexes(from, to, length); + if (from == to) { + return emptyMultiset(comparator()); + } else if (from == 0 && to == length) { + return this; + } else { + RegularImmutableSortedSet subElementSet = elementSet.getSubSet(from, to); + return new RegularImmutableSortedMultiset( + subElementSet, cumulativeCounts, offset + from, to - from); + } + } + + @Override + boolean isPartialView() { + return offset > 0 || length < cumulativeCounts.length - 1; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSortedSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSortedSet.java new file mode 100644 index 0000000000000..c3bdd5f4cedeb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableSortedSet.java @@ -0,0 +1,329 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An immutable sorted set with one or more elements. TODO(jlevy): Consider separate class for a + * single-element sorted set. + * + * @author Jared Levy + * @author Louis Wasserman + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings({"serial", "rawtypes"}) +@ElementTypesAreNonnullByDefault +final class RegularImmutableSortedSet extends ImmutableSortedSet { + static final RegularImmutableSortedSet NATURAL_EMPTY_SET = + new RegularImmutableSortedSet<>(ImmutableList.of(), Ordering.natural()); + + private final transient ImmutableList elements; + + RegularImmutableSortedSet(ImmutableList elements, Comparator comparator) { + super(comparator); + this.elements = elements; + } + + @Override + Object[] internalArray() { + return elements.internalArray(); + } + + @Override + int internalArrayStart() { + return elements.internalArrayStart(); + } + + @Override + int internalArrayEnd() { + return elements.internalArrayEnd(); + } + + @Override + public UnmodifiableIterator iterator() { + return elements.iterator(); + } + + @GwtIncompatible // NavigableSet + @Override + public UnmodifiableIterator descendingIterator() { + return elements.reverse().iterator(); + } + + @Override + public Spliterator spliterator() { + return asList().spliterator(); + } + + @Override + public void forEach(Consumer action) { + elements.forEach(action); + } + + @Override + public int size() { + return elements.size(); + } + + @Override + public boolean contains(Object o) { + try { + return o != null && unsafeBinarySearch(o) >= 0; + } catch (ClassCastException e) { + return false; + } + } + + @Override + public boolean containsAll(Collection targets) { + // TODO(jlevy): For optimal performance, use a binary search when + // targets.size() < size() / log(size()) + // TODO(kevinb): see if we can share code with OrderedIterator after it + // graduates from labs. + if (targets instanceof Multiset) { + targets = ((Multiset) targets).elementSet(); + } + if (!SortedIterables.hasSameComparator(comparator(), targets) || (targets.size() <= 1)) { + return super.containsAll(targets); + } + + /* + * If targets is a sorted set with the same comparator, containsAll can run + * in O(n) time stepping through the two collections. + */ + Iterator thisIterator = iterator(); + + Iterator thatIterator = targets.iterator(); + // known nonempty since we checked targets.size() > 1 + + if (!thisIterator.hasNext()) { + return false; + } + + Object target = thatIterator.next(); + E current = thisIterator.next(); + try { + while (true) { + int cmp = unsafeCompare(current, target); + + if (cmp < 0) { + if (!thisIterator.hasNext()) { + return false; + } + current = thisIterator.next(); + } else if (cmp == 0) { + if (!thatIterator.hasNext()) { + return true; + } + target = thatIterator.next(); + + } else if (cmp > 0) { + return false; + } + } + } catch (NullPointerException | ClassCastException e) { + return false; + } + } + + private int unsafeBinarySearch(Object key) throws ClassCastException { + return Collections.binarySearch(elements, key, unsafeComparator()); + } + + @Override + boolean isPartialView() { + return elements.isPartialView(); + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + return elements.copyIntoArray(dst, offset); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof Set)) { + return false; + } + + Set that = (Set) object; + if (size() != that.size()) { + return false; + } else if (isEmpty()) { + return true; + } + + if (SortedIterables.hasSameComparator(comparator, that)) { + Iterator otherIterator = that.iterator(); + try { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + Object element = iterator.next(); + Object otherElement = otherIterator.next(); + if (otherElement == null || unsafeCompare(element, otherElement) != 0) { + return false; + } + } + return true; + } catch (ClassCastException e) { + return false; + } catch (NoSuchElementException e) { + return false; // concurrent change to other set + } + } + return containsAll(that); + } + + @Override + public E first() { + if (isEmpty()) { + throw new NoSuchElementException(); + } + return elements.get(0); + } + + @Override + public E last() { + if (isEmpty()) { + throw new NoSuchElementException(); + } + return elements.get(size() - 1); + } + + @Override + public E lower(E element) { + int index = headIndex(element, false) - 1; + return (index == -1) ? null : elements.get(index); + } + + @Override + public E floor(E element) { + int index = headIndex(element, true) - 1; + return (index == -1) ? null : elements.get(index); + } + + @Override + public E ceiling(E element) { + int index = tailIndex(element, true); + return (index == size()) ? null : elements.get(index); + } + + @Override + public E higher(E element) { + int index = tailIndex(element, false); + return (index == size()) ? null : elements.get(index); + } + + @Override + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return getSubSet(0, headIndex(toElement, inclusive)); + } + + int headIndex(E toElement, boolean inclusive) { + int index = Collections.binarySearch(elements, checkNotNull(toElement), comparator()); + if (index >= 0) { + return inclusive ? index + 1 : index; + } else { + return ~index; + } + } + + @Override + ImmutableSortedSet subSetImpl( + E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return tailSetImpl(fromElement, fromInclusive).headSetImpl(toElement, toInclusive); + } + + @Override + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return getSubSet(tailIndex(fromElement, inclusive), size()); + } + + int tailIndex(E fromElement, boolean inclusive) { + int index = Collections.binarySearch(elements, checkNotNull(fromElement), comparator()); + if (index >= 0) { + return inclusive ? index : index + 1; + } else { + return ~index; + } + } + + // Pretend the comparator can compare anything. If it turns out it can't + // compare two elements, it'll throw a CCE. Only methods that are specified to + // throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator unsafeComparator() { + return (Comparator) comparator; + } + + RegularImmutableSortedSet getSubSet(int newFromIndex, int newToIndex) { + if (newFromIndex == 0 && newToIndex == size()) { + return this; + } else if (newFromIndex < newToIndex) { + return new RegularImmutableSortedSet( + elements.subList(newFromIndex, newToIndex), comparator); + } else { + return emptySet(comparator); + } + } + + @Override + int indexOf(Object target) { + if (target == null) { + return -1; + } + int position; + try { + position = Collections.binarySearch(elements, target, unsafeComparator()); + } catch (ClassCastException e) { + return -1; + } + return (position >= 0) ? position : -1; + } + + @Override + ImmutableList createAsList() { + return (size() <= 1) ? elements : new ImmutableSortedAsList(this, elements); + } + + @Override + ImmutableSortedSet createDescendingSet() { + Comparator reversedOrder = Collections.reverseOrder(comparator); + return isEmpty() + ? emptySet(reversedOrder) + : new RegularImmutableSortedSet(elements.reverse(), reversedOrder); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableTable.java new file mode 100644 index 0000000000000..2715bd90b1613 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RegularImmutableTable.java @@ -0,0 +1,188 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation of {@link ImmutableTable} holding an arbitrary number of cells. + * + * @author Gregory Kick + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class RegularImmutableTable extends ImmutableTable { + RegularImmutableTable() {} + + abstract Cell getCell(int iterationIndex); + + @Override + final ImmutableSet> createCellSet() { + return isEmpty() ? ImmutableSet.>of() : new CellSet(); + } + + private final class CellSet extends IndexedImmutableSet> { + @Override + public int size() { + return RegularImmutableTable.this.size(); + } + + @Override + Cell get(int index) { + return getCell(index); + } + + @Override + public boolean contains(Object object) { + if (object instanceof Cell) { + Cell cell = (Cell) object; + Object value = RegularImmutableTable.this.get(cell.getRowKey(), cell.getColumnKey()); + return value != null && value.equals(cell.getValue()); + } + return false; + } + + @Override + boolean isPartialView() { + return false; + } + } + + abstract V getValue(int iterationIndex); + + @Override + final ImmutableCollection createValues() { + return isEmpty() ? ImmutableList.of() : new Values(); + } + + private final class Values extends ImmutableList { + @Override + public int size() { + return RegularImmutableTable.this.size(); + } + + @Override + public V get(int index) { + return getValue(index); + } + + @Override + boolean isPartialView() { + return true; + } + } + + static RegularImmutableTable forCells( + List> cells, + Comparator rowComparator, + Comparator columnComparator) { + checkNotNull(cells); + if (rowComparator != null || columnComparator != null) { + /* + * This sorting logic leads to a cellSet() ordering that may not be expected and that isn't + * documented in the Javadoc. If a row Comparator is provided, cellSet() iterates across the + * columns in the first row, the columns in the second row, etc. If a column Comparator is + * provided but a row Comparator isn't, cellSet() iterates across the rows in the first + * column, the rows in the second column, etc. + */ + Comparator> comparator = + (Cell cell1, Cell cell2) -> { + int rowCompare = + (rowComparator == null) + ? 0 + : rowComparator.compare(cell1.getRowKey(), cell2.getRowKey()); + if (rowCompare != 0) { + return rowCompare; + } + return (columnComparator == null) + ? 0 + : columnComparator.compare(cell1.getColumnKey(), cell2.getColumnKey()); + }; + Collections.sort(cells, comparator); + } + return forCellsInternal(cells, rowComparator, columnComparator); + } + + static RegularImmutableTable forCells(Iterable> cells) { + return forCellsInternal(cells, null, null); + } + + private static RegularImmutableTable forCellsInternal( + Iterable> cells, + Comparator rowComparator, + Comparator columnComparator) { + Set rowSpaceBuilder = new LinkedHashSet<>(); + Set columnSpaceBuilder = new LinkedHashSet<>(); + ImmutableList> cellList = ImmutableList.copyOf(cells); + for (Cell cell : cells) { + rowSpaceBuilder.add(cell.getRowKey()); + columnSpaceBuilder.add(cell.getColumnKey()); + } + + ImmutableSet rowSpace = + (rowComparator == null) + ? ImmutableSet.copyOf(rowSpaceBuilder) + : ImmutableSet.copyOf(ImmutableList.sortedCopyOf(rowComparator, rowSpaceBuilder)); + ImmutableSet columnSpace = + (columnComparator == null) + ? ImmutableSet.copyOf(columnSpaceBuilder) + : ImmutableSet.copyOf(ImmutableList.sortedCopyOf(columnComparator, columnSpaceBuilder)); + + return forOrderedComponents(cellList, rowSpace, columnSpace); + } + + /** A factory that chooses the most space-efficient representation of the table. */ + static RegularImmutableTable forOrderedComponents( + ImmutableList> cellList, + ImmutableSet rowSpace, + ImmutableSet columnSpace) { + // use a dense table if more than half of the cells have values + // TODO(gak): tune this condition based on empirical evidence + return (cellList.size() > (((long) rowSpace.size() * columnSpace.size()) / 2)) + ? new DenseImmutableTable(cellList, rowSpace, columnSpace) + : new SparseImmutableTable(cellList, rowSpace, columnSpace); + } + + /** + * @throws IllegalArgumentException if {@code existingValue} is not null. + */ + /* + * We could have declared this method 'static' but the additional compile-time checks achieved by + * referencing the type variables seem worthwhile. + */ + final void checkNoDuplicate(R rowKey, C columnKey, V existingValue, V newValue) { + checkArgument( + existingValue == null, + "Duplicate key: (row=%s, column=%s), values: [%s, %s].", + rowKey, + columnKey, + newValue, + existingValue); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ReverseNaturalOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ReverseNaturalOrdering.java new file mode 100644 index 0000000000000..7b0c18c6ea2fb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ReverseNaturalOrdering.java @@ -0,0 +1,106 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** An ordering that uses the reverse of the natural order of the values. */ +@GwtCompatible(serializable = true) +@SuppressWarnings({"unchecked", "rawtypes"}) // TODO(kevinb): the right way to explain this?? +@ElementTypesAreNonnullByDefault +final class ReverseNaturalOrdering extends Ordering> implements Serializable { + static final ReverseNaturalOrdering INSTANCE = new ReverseNaturalOrdering(); + + @Override + public int compare(Comparable left, Comparable right) { + checkNotNull(left); // right null is caught later + if (left == right) { + return 0; + } + + return ((Comparable) right).compareTo(left); + } + + @Override + public > Ordering reverse() { + return Ordering.natural(); + } + + // Override the min/max methods to "hoist" delegation outside loops + + @Override + public > E min(E a, E b) { + return NaturalOrdering.INSTANCE.max(a, b); + } + + @Override + public > E min(E a, E b, E c, E... rest) { + return NaturalOrdering.INSTANCE.max(a, b, c, rest); + } + + @Override + public > E min(Iterator iterator) { + return NaturalOrdering.INSTANCE.max(iterator); + } + + @Override + public > E min(Iterable iterable) { + return NaturalOrdering.INSTANCE.max(iterable); + } + + @Override + public > E max(E a, E b) { + return NaturalOrdering.INSTANCE.min(a, b); + } + + @Override + public > E max(E a, E b, E c, E... rest) { + return NaturalOrdering.INSTANCE.min(a, b, c, rest); + } + + @Override + public > E max(Iterator iterator) { + return NaturalOrdering.INSTANCE.min(iterator); + } + + @Override + public > E max(Iterable iterable) { + return NaturalOrdering.INSTANCE.min(iterable); + } + + // preserving singleton-ness gives equals()/hashCode() for free + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.natural().reverse()"; + } + + private ReverseNaturalOrdering() {} + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ReverseOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ReverseOrdering.java new file mode 100644 index 0000000000000..cadb08ff71f3f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/ReverseOrdering.java @@ -0,0 +1,117 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** An ordering that uses the reverse of a given order. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class ReverseOrdering extends Ordering implements Serializable { + final Ordering forwardOrder; + + ReverseOrdering(Ordering forwardOrder) { + this.forwardOrder = checkNotNull(forwardOrder); + } + + @Override + public int compare(@ParametricNullness T a, @ParametricNullness T b) { + return forwardOrder.compare(b, a); + } + + @SuppressWarnings("unchecked") // how to explain? + @Override + public Ordering reverse() { + return (Ordering) forwardOrder; + } + + // Override the min/max methods to "hoist" delegation outside loops + + @Override + public E min(@ParametricNullness E a, @ParametricNullness E b) { + return forwardOrder.max(a, b); + } + + @Override + public E min( + @ParametricNullness E a, @ParametricNullness E b, @ParametricNullness E c, E... rest) { + return forwardOrder.max(a, b, c, rest); + } + + @Override + public E min(Iterator iterator) { + return forwardOrder.max(iterator); + } + + @Override + public E min(Iterable iterable) { + return forwardOrder.max(iterable); + } + + @Override + public E max(@ParametricNullness E a, @ParametricNullness E b) { + return forwardOrder.min(a, b); + } + + @Override + public E max( + @ParametricNullness E a, @ParametricNullness E b, @ParametricNullness E c, E... rest) { + return forwardOrder.min(a, b, c, rest); + } + + @Override + public E max(Iterator iterator) { + return forwardOrder.min(iterator); + } + + @Override + public E max(Iterable iterable) { + return forwardOrder.min(iterable); + } + + @Override + public int hashCode() { + return -forwardOrder.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ReverseOrdering) { + ReverseOrdering that = (ReverseOrdering) object; + return this.forwardOrder.equals(that.forwardOrder); + } + return false; + } + + @Override + public String toString() { + return forwardOrder + ".reverse()"; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RowSortedTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RowSortedTable.java new file mode 100644 index 0000000000000..5cfedde68c676 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/RowSortedTable.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +/** + * Interface that extends {@code Table} and whose rows are sorted. + * + *

The {@link #rowKeySet} method returns a {@link SortedSet} and the {@link #rowMap} method + * returns a {@link SortedMap}, instead of the {@link Set} and {@link Map} specified by the {@link + * Table} interface. + * + * @author Warren Dukes + * @since 8.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface RowSortedTable + extends Table { + /** + * {@inheritDoc} + * + *

This method returns a {@link SortedSet}, instead of the {@code Set} specified in the {@link + * Table} interface. + */ + @Override + SortedSet rowKeySet(); + + /** + * {@inheritDoc} + * + *

This method returns a {@link SortedMap}, instead of the {@code Map} specified in the {@link + * Table} interface. + */ + @Override + SortedMap> rowMap(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Serialization.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Serialization.java new file mode 100644 index 0000000000000..2d2018ce49727 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Serialization.java @@ -0,0 +1,229 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; + +/** + * Provides static methods for serializing collection classes. + * + *

This class assists the implementation of collection classes. Do not use this class to + * serialize collections that are defined elsewhere. + * + * @author Jared Levy + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +final class Serialization { + private Serialization() {} + + /** + * Reads a count corresponding to a serialized map, multiset, or multimap. It returns the size of + * a map serialized by {@link #writeMap(Map, ObjectOutputStream)}, the number of distinct elements + * in a multiset serialized by {@link #writeMultiset(Multiset, ObjectOutputStream)}, or the number + * of distinct keys in a multimap serialized by {@link #writeMultimap(Multimap, + * ObjectOutputStream)}. + */ + static int readCount(ObjectInputStream stream) throws IOException { + return stream.readInt(); + } + + /** + * Stores the contents of a map in an output stream, as part of serialization. It does not support + * concurrent maps whose content may change while the method is running. + * + *

The serialized output consists of the number of entries, first key, first value, second key, + * second value, and so on. + */ + static void writeMap( + Map map, ObjectOutputStream stream) throws IOException { + stream.writeInt(map.size()); + for (Map.Entry entry : map.entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + /** + * Populates a map by reading an input stream, as part of deserialization. See {@link #writeMap} + * for the data format. + */ + static void populateMap( + Map map, ObjectInputStream stream) throws IOException, ClassNotFoundException { + int size = stream.readInt(); + populateMap(map, stream, size); + } + + /** + * Populates a map by reading an input stream, as part of deserialization. See {@link #writeMap} + * for the data format. The size is determined by a prior call to {@link #readCount}. + */ + static void populateMap( + Map map, ObjectInputStream stream, int size) + throws IOException, ClassNotFoundException { + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMap + K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") // reading data stored by writeMap + V value = (V) stream.readObject(); + map.put(key, value); + } + } + + /** + * Stores the contents of a multiset in an output stream, as part of serialization. It does not + * support concurrent multisets whose content may change while the method is running. + * + *

The serialized output consists of the number of distinct elements, the first element, its + * count, the second element, its count, and so on. + */ + static void writeMultiset(Multiset multiset, ObjectOutputStream stream) + throws IOException { + int entryCount = multiset.entrySet().size(); + stream.writeInt(entryCount); + for (Multiset.Entry entry : multiset.entrySet()) { + stream.writeObject(entry.getElement()); + stream.writeInt(entry.getCount()); + } + } + + /** + * Populates a multiset by reading an input stream, as part of deserialization. See {@link + * #writeMultiset} for the data format. + */ + static void populateMultiset(Multiset multiset, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctElements = stream.readInt(); + populateMultiset(multiset, stream, distinctElements); + } + + /** + * Populates a multiset by reading an input stream, as part of deserialization. See {@link + * #writeMultiset} for the data format. The number of distinct elements is determined by a prior + * call to {@link #readCount}. + */ + static void populateMultiset( + Multiset multiset, ObjectInputStream stream, int distinctElements) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctElements; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultiset + E element = (E) stream.readObject(); + int count = stream.readInt(); + multiset.add(element, count); + } + } + + /** + * Stores the contents of a multimap in an output stream, as part of serialization. It does not + * support concurrent multimaps whose content may change while the method is running. The {@link + * Multimap#asMap} view determines the ordering in which data is written to the stream. + * + *

The serialized output consists of the number of distinct keys, and then for each distinct + * key: the key, the number of values for that key, and the key's values. + */ + static void writeMultimap( + Multimap multimap, ObjectOutputStream stream) throws IOException { + stream.writeInt(multimap.asMap().size()); + for (Map.Entry> entry : multimap.asMap().entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeInt(entry.getValue().size()); + for (V value : entry.getValue()) { + stream.writeObject(value); + } + } + } + + /** + * Populates a multimap by reading an input stream, as part of deserialization. See {@link + * #writeMultimap} for the data format. + */ + static void populateMultimap( + Multimap multimap, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctKeys = stream.readInt(); + populateMultimap(multimap, stream, distinctKeys); + } + + /** + * Populates a multimap by reading an input stream, as part of deserialization. See {@link + * #writeMultimap} for the data format. The number of distinct keys is determined by a prior call + * to {@link #readCount}. + */ + static void populateMultimap( + Multimap multimap, ObjectInputStream stream, int distinctKeys) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctKeys; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + K key = (K) stream.readObject(); + Collection values = multimap.get(key); + int valueCount = stream.readInt(); + for (int j = 0; j < valueCount; j++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + V value = (V) stream.readObject(); + values.add(value); + } + } + } + + // Secret sauce for setting final fields; don't make it public. + static FieldSetter getFieldSetter(Class clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + return new FieldSetter<>(field); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); // programmer error + } + } + + // Secret sauce for setting final fields; don't make it public. + static final class FieldSetter { + private final Field field; + + private FieldSetter(Field field) { + this.field = field; + field.setAccessible(true); + } + + void set(T instance, Object value) { + try { + field.set(instance, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + + void set(T instance, int value) { + try { + field.set(instance, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SetMultimap.java new file mode 100644 index 0000000000000..b8b97a3138a77 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SetMultimap.java @@ -0,0 +1,121 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A {@code Multimap} that cannot hold duplicate key-value pairs. Adding a key-value pair that's + * already in the multimap has no effect. See the {@link Multimap} documentation for information + * common to all multimaps. + * + *

The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods each return a {@link + * Set} of values, while {@link #entries} returns a {@code Set} of map entries. Though the method + * signature doesn't say so explicitly, the map returned by {@link #asMap} has {@code Set} values. + * + *

If the values corresponding to a single key should be ordered according to a {@link + * java.util.Comparator} (or the natural order), see the {@link SortedSetMultimap} subinterface. + * + *

Since the value collections are sets, the behavior of a {@code SetMultimap} is not specified + * if key or value objects already present in the multimap change in a manner that affects + * {@code equals} comparisons. Use caution if mutable objects are used as keys or values in a {@code + * SetMultimap}. + * + *

Warning: Do not modify either a key or a value of a {@code SetMultimap} in a way + * that affects its {@link Object#equals} behavior. Undefined behavior and bugs will result. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface SetMultimap extends Multimap { + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link java.util.Collection} specified in the {@link Multimap} + * interface. + */ + @Override + Set get(@ParametricNullness K key); + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link java.util.Collection} specified in the {@link Multimap} + * interface. + */ + @Override + Set removeAll(Object key); + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link java.util.Collection} specified in the {@link Multimap} + * interface. + * + *

Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + Set replaceValues(@ParametricNullness K key, Iterable values); + + /** + * {@inheritDoc} + * + *

Because a {@code SetMultimap} has unique values for a given key, this method returns a + * {@link Set}, instead of the {@link java.util.Collection} specified in the {@link Multimap} + * interface. + */ + @Override + Set> entries(); + + /** + * {@inheritDoc} + * + *

Note: The returned map's values are guaranteed to be of type {@link Set}. To obtain + * this map with the more specific generic type {@code Map>}, call {@link + * Multimaps#asMap(SetMultimap)} instead. + */ + @Override + Map> asMap(); + + /** + * Compares the specified object to this multimap for equality. + * + *

Two {@code SetMultimap} instances are equal if, for each key, they contain the same values. + * Equality does not depend on the ordering of keys or values. + * + *

An empty {@code SetMultimap} is equal to any other empty {@code Multimap}, including an + * empty {@code ListMultimap}. + */ + @Override + boolean equals(Object obj); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Sets.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Sets.java new file mode 100644 index 0000000000000..386bbe46d1f51 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Sets.java @@ -0,0 +1,2142 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.collect.Collections2.FilteredCollection; +import org.apache.iotdb.google.common.math.IntMath; + +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Consumer; +import java.util.stream.Collector; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; + +/** + * Static utility methods pertaining to {@link Set} instances. Also see this class's counterparts + * {@link Lists}, {@link Maps} and {@link Queues}. + * + *

See the Guava User Guide article on {@code Sets}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @author Chris Povirk + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Sets { + private Sets() {} + + /** + * {@link AbstractSet} substitute without the potentially-quadratic {@code removeAll} + * implementation. + */ + abstract static class ImprovedAbstractSet extends AbstractSet { + @Override + public boolean removeAll(Collection c) { + return removeAllImpl(this, c); + } + + @Override + public boolean retainAll(Collection c) { + return super.retainAll(checkNotNull(c)); // GWT compatibility + } + } + + /** + * Returns an immutable set instance containing the given enum elements. Internally, the returned + * set will be backed by an {@link EnumSet}. + * + *

The iteration order of the returned set follows the enum's iteration order, not the order in + * which the elements are provided to the method. + * + * @param anElement one of the elements the set should contain + * @param otherElements the rest of the elements the set should contain + * @return an immutable set containing those elements, minus duplicates + */ + // http://code.google.com/p/google-web-toolkit/issues/detail?id=3028 + @GwtCompatible(serializable = true) + public static > ImmutableSet immutableEnumSet( + E anElement, E... otherElements) { + return ImmutableEnumSet.asImmutable(EnumSet.of(anElement, otherElements)); + } + + /** + * Returns an immutable set instance containing the given enum elements. Internally, the returned + * set will be backed by an {@link EnumSet}. + * + *

The iteration order of the returned set follows the enum's iteration order, not the order in + * which the elements appear in the given collection. + * + * @param elements the elements, all of the same {@code enum} type, that the set should contain + * @return an immutable set containing those elements, minus duplicates + */ + // http://code.google.com/p/google-web-toolkit/issues/detail?id=3028 + @GwtCompatible(serializable = true) + public static > ImmutableSet immutableEnumSet(Iterable elements) { + if (elements instanceof ImmutableEnumSet) { + return (ImmutableEnumSet) elements; + } else if (elements instanceof Collection) { + Collection collection = (Collection) elements; + if (collection.isEmpty()) { + return ImmutableSet.of(); + } else { + return ImmutableEnumSet.asImmutable(EnumSet.copyOf(collection)); + } + } else { + Iterator itr = elements.iterator(); + if (itr.hasNext()) { + EnumSet enumSet = EnumSet.of(itr.next()); + Iterators.addAll(enumSet, itr); + return ImmutableEnumSet.asImmutable(enumSet); + } else { + return ImmutableSet.of(); + } + } + } + + /** + * Returns a {@code Collector} that accumulates the input elements into a new {@code ImmutableSet} + * with an implementation specialized for enums. Unlike {@link ImmutableSet#toImmutableSet}, the + * resulting set will iterate over elements in their enum definition order, not encounter order. + * + * @since 21.0 + */ + public static > Collector> toImmutableEnumSet() { + return CollectCollectors.toImmutableEnumSet(); + } + + /** + * Returns a new, mutable {@code EnumSet} instance containing the given elements in their + * natural order. This method behaves identically to {@link EnumSet#copyOf(Collection)}, but also + * accepts non-{@code Collection} iterables and empty iterables. + */ + public static > EnumSet newEnumSet( + Iterable iterable, Class elementType) { + EnumSet set = EnumSet.noneOf(elementType); + Iterables.addAll(set, iterable); + return set; + } + + // HashSet + + /** + * Creates a mutable, initially empty {@code HashSet} instance. + * + *

Note: if mutability is not required, use {@link ImmutableSet#of()} instead. If {@code + * E} is an {@link Enum} type, use {@link EnumSet#noneOf} instead. Otherwise, strongly consider + * using a {@code LinkedHashSet} instead, at the cost of increased memory footprint, to get + * deterministic iteration behavior. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code HashSet} constructor directly, taking advantage of "diamond" syntax. + */ + public static HashSet newHashSet() { + return new HashSet(); + } + + /** + * Creates a mutable {@code HashSet} instance initially containing the given elements. + * + *

Note: if elements are non-null and won't be added or removed after this point, use + * {@link ImmutableSet#of()} or {@link ImmutableSet#copyOf(Object[])} instead. If {@code E} is an + * {@link Enum} type, use {@link EnumSet#of(Enum, Enum[])} instead. Otherwise, strongly consider + * using a {@code LinkedHashSet} instead, at the cost of increased memory footprint, to get + * deterministic iteration behavior. + * + *

This method is just a small convenience, either for {@code newHashSet(}{@link Arrays#asList + * asList}{@code (...))}, or for creating an empty set then calling {@link Collections#addAll}. + * This method is not actually very useful and will likely be deprecated in the future. + */ + public static HashSet newHashSet(E... elements) { + HashSet set = newHashSetWithExpectedSize(elements.length); + Collections.addAll(set, elements); + return set; + } + + /** + * Creates a mutable {@code HashSet} instance containing the given elements. A very thin + * convenience for creating an empty set then calling {@link Collection#addAll} or {@link + * Iterables#addAll}. + * + *

Note: if mutability is not required and the elements are non-null, use {@link + * ImmutableSet#copyOf(Iterable)} instead. (Or, change {@code elements} to be a {@link + * FluentIterable} and call {@code elements.toSet()}.) + * + *

Note: if {@code E} is an {@link Enum} type, use {@link #newEnumSet(Iterable, Class)} + * instead. + * + *

Note: if {@code elements} is a {@link Collection}, you don't need this method. + * Instead, use the {@code HashSet} constructor directly, taking advantage of "diamond" syntax. + * + *

Overall, this method is not very useful and will likely be deprecated in the future. + */ + public static HashSet newHashSet(Iterable elements) { + return (elements instanceof Collection) + ? new HashSet((Collection) elements) + : newHashSet(elements.iterator()); + } + + /** + * Creates a mutable {@code HashSet} instance containing the given elements. A very thin + * convenience for creating an empty set and then calling {@link Iterators#addAll}. + * + *

Note: if mutability is not required and the elements are non-null, use {@link + * ImmutableSet#copyOf(Iterator)} instead. + * + *

Note: if {@code E} is an {@link Enum} type, you should create an {@link EnumSet} + * instead. + * + *

Overall, this method is not very useful and will likely be deprecated in the future. + */ + public static HashSet newHashSet(Iterator elements) { + HashSet set = newHashSet(); + Iterators.addAll(set, elements); + return set; + } + + /** + * Returns a new hash set using the smallest initial table size that can hold {@code expectedSize} + * elements without resizing. Note that this is not what {@link HashSet#HashSet(int)} does, but it + * is what most users want and expect it to do. + * + *

This behavior can't be broadly guaranteed, but has been tested with OpenJDK 1.7 and 1.8. + * + * @param expectedSize the number of elements you expect to add to the returned set + * @return a new, empty hash set with enough capacity to hold {@code expectedSize} elements + * without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashSet newHashSetWithExpectedSize(int expectedSize) { + return new HashSet(Maps.capacity(expectedSize)); + } + + /** + * Creates a thread-safe set backed by a hash map. The set is backed by a {@link + * ConcurrentHashMap} instance, and thus carries the same concurrency guarantees. + * + *

Unlike {@code HashSet}, this class does NOT allow {@code null} to be used as an element. The + * set is serializable. + * + * @return a new, empty thread-safe {@code Set} + * @since 15.0 + */ + public static Set newConcurrentHashSet() { + return Platform.newConcurrentHashSet(); + } + + /** + * Creates a thread-safe set backed by a hash map and containing the given elements. The set is + * backed by a {@link ConcurrentHashMap} instance, and thus carries the same concurrency + * guarantees. + * + *

Unlike {@code HashSet}, this class does NOT allow {@code null} to be used as an element. The + * set is serializable. + * + * @param elements the elements that the set should contain + * @return a new thread-safe set containing those elements (minus duplicates) + * @throws NullPointerException if {@code elements} or any of its contents is null + * @since 15.0 + */ + public static Set newConcurrentHashSet(Iterable elements) { + Set set = newConcurrentHashSet(); + Iterables.addAll(set, elements); + return set; + } + + // LinkedHashSet + + /** + * Creates a mutable, empty {@code LinkedHashSet} instance. + * + *

Note: if mutability is not required, use {@link ImmutableSet#of()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code LinkedHashSet} constructor directly, taking advantage of "diamond" syntax. + * + * @return a new, empty {@code LinkedHashSet} + */ + public static LinkedHashSet newLinkedHashSet() { + return new LinkedHashSet(); + } + + /** + * Creates a mutable {@code LinkedHashSet} instance containing the given elements in order. + * + *

Note: if mutability is not required and the elements are non-null, use {@link + * ImmutableSet#copyOf(Iterable)} instead. + * + *

Note: if {@code elements} is a {@link Collection}, you don't need this method. + * Instead, use the {@code LinkedHashSet} constructor directly, taking advantage of "diamond" syntax. + * + *

Overall, this method is not very useful and will likely be deprecated in the future. + * + * @param elements the elements that the set should contain, in order + * @return a new {@code LinkedHashSet} containing those elements (minus duplicates) + */ + public static LinkedHashSet newLinkedHashSet( + Iterable elements) { + if (elements instanceof Collection) { + return new LinkedHashSet((Collection) elements); + } + LinkedHashSet set = newLinkedHashSet(); + Iterables.addAll(set, elements); + return set; + } + + /** + * Creates a {@code LinkedHashSet} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without growth. This behavior cannot be + * broadly guaranteed, but it is observed to be true for OpenJDK 1.7. It also can't be guaranteed + * that the method isn't inadvertently oversizing the returned set. + * + * @param expectedSize the number of elements you expect to add to the returned set + * @return a new, empty {@code LinkedHashSet} with enough capacity to hold {@code expectedSize} + * elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + * @since 11.0 + */ + public static LinkedHashSet newLinkedHashSetWithExpectedSize( + int expectedSize) { + return new LinkedHashSet(Maps.capacity(expectedSize)); + } + + // TreeSet + + /** + * Creates a mutable, empty {@code TreeSet} instance sorted by the natural sort ordering of + * its elements. + * + *

Note: if mutability is not required, use {@link ImmutableSortedSet#of()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeSet} constructor directly, taking advantage of "diamond" syntax. + * + * @return a new, empty {@code TreeSet} + */ + public static TreeSet newTreeSet() { + return new TreeSet(); + } + + /** + * Creates a mutable {@code TreeSet} instance containing the given elements sorted by their + * natural ordering. + * + *

Note: if mutability is not required, use {@link ImmutableSortedSet#copyOf(Iterable)} + * instead. + * + *

Note: If {@code elements} is a {@code SortedSet} with an explicit comparator, this + * method has different behavior than {@link TreeSet#TreeSet(SortedSet)}, which returns a {@code + * TreeSet} with that comparator. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeSet} constructor directly, taking advantage of "diamond" syntax. + * + *

This method is just a small convenience for creating an empty set and then calling {@link + * Iterables#addAll}. This method is not very useful and will likely be deprecated in the future. + * + * @param elements the elements that the set should contain + * @return a new {@code TreeSet} containing those elements (minus duplicates) + */ + public static TreeSet newTreeSet(Iterable elements) { + TreeSet set = newTreeSet(); + Iterables.addAll(set, elements); + return set; + } + + /** + * Creates a mutable, empty {@code TreeSet} instance with the given comparator. + * + *

Note: if mutability is not required, use {@code + * ImmutableSortedSet.orderedBy(comparator).build()} instead. + * + *

Note: this method is now unnecessary and should be treated as deprecated. Instead, + * use the {@code TreeSet} constructor directly, taking advantage of "diamond" syntax. One caveat to this is that the {@code TreeSet} + * constructor uses a null {@code Comparator} to mean "natural ordering," whereas this factory + * rejects null. Clean your code accordingly. + * + * @param comparator the comparator to use to sort the set + * @return a new, empty {@code TreeSet} + * @throws NullPointerException if {@code comparator} is null + */ + public static TreeSet newTreeSet(Comparator comparator) { + return new TreeSet(checkNotNull(comparator)); + } + + /** + * Creates an empty {@code Set} that uses identity to determine equality. It compares object + * references, instead of calling {@code equals}, to determine whether a provided object matches + * an element in the set. For example, {@code contains} returns {@code false} when passed an + * object that equals a set member, but isn't the same instance. This behavior is similar to the + * way {@code IdentityHashMap} handles key lookups. + * + * @since 8.0 + */ + public static Set newIdentityHashSet() { + return Collections.newSetFromMap(Maps.newIdentityHashMap()); + } + + /** + * Creates an empty {@code CopyOnWriteArraySet} instance. + * + *

Note: if you need an immutable empty {@link Set}, use {@link Collections#emptySet} + * instead. + * + * @return a new, empty {@code CopyOnWriteArraySet} + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // CopyOnWriteArraySet + public static CopyOnWriteArraySet newCopyOnWriteArraySet() { + return new CopyOnWriteArraySet(); + } + + /** + * Creates a {@code CopyOnWriteArraySet} instance containing the given elements. + * + * @param elements the elements that the set should contain, in order + * @return a new {@code CopyOnWriteArraySet} containing those elements + * @since 12.0 + */ + @J2ktIncompatible + @GwtIncompatible // CopyOnWriteArraySet + public static CopyOnWriteArraySet newCopyOnWriteArraySet( + Iterable elements) { + // We copy elements to an ArrayList first, rather than incurring the + // quadratic cost of adding them to the COWAS directly. + Collection elementsCollection = + (elements instanceof Collection) + ? (Collection) elements + : Lists.newArrayList(elements); + return new CopyOnWriteArraySet(elementsCollection); + } + + /** + * Creates an {@code EnumSet} consisting of all enum values that are not in the specified + * collection. If the collection is an {@link EnumSet}, this method has the same behavior as + * {@link EnumSet#complementOf}. Otherwise, the specified collection must contain at least one + * element, in order to determine the element type. If the collection could be empty, use {@link + * #complementOf(Collection, Class)} instead of this method. + * + * @param collection the collection whose complement should be stored in the enum set + * @return a new, modifiable {@code EnumSet} containing all values of the enum that aren't present + * in the given collection + * @throws IllegalArgumentException if {@code collection} is not an {@code EnumSet} instance and + * contains no elements + */ + @J2ktIncompatible + @GwtIncompatible + public static > EnumSet complementOf(Collection collection) { + if (collection instanceof EnumSet) { + return EnumSet.complementOf((EnumSet) collection); + } + checkArgument( + !collection.isEmpty(), "collection is empty; use the other version of this method"); + Class type = collection.iterator().next().getDeclaringClass(); + return makeComplementByHand(collection, type); + } + + /** + * Creates an {@code EnumSet} consisting of all enum values that are not in the specified + * collection. This is equivalent to {@link EnumSet#complementOf}, but can act on any input + * collection, as long as the elements are of enum type. + * + * @param collection the collection whose complement should be stored in the {@code EnumSet} + * @param type the type of the elements in the set + * @return a new, modifiable {@code EnumSet} initially containing all the values of the enum not + * present in the given collection + */ + @GwtIncompatible + public static > EnumSet complementOf( + Collection collection, Class type) { + checkNotNull(collection); + return (collection instanceof EnumSet) + ? EnumSet.complementOf((EnumSet) collection) + : makeComplementByHand(collection, type); + } + + @GwtIncompatible + private static > EnumSet makeComplementByHand( + Collection collection, Class type) { + EnumSet result = EnumSet.allOf(type); + result.removeAll(collection); + return result; + } + + /** + * Returns a set backed by the specified map. The resulting set displays the same ordering, + * concurrency, and performance characteristics as the backing map. In essence, this factory + * method provides a {@link Set} implementation corresponding to any {@link Map} implementation. + * There is no need to use this method on a {@link Map} implementation that already has a + * corresponding {@link Set} implementation (such as {@link java.util.HashMap} or {@link + * java.util.TreeMap}). + * + *

Each method invocation on the set returned by this method results in exactly one method + * invocation on the backing map or its {@code keySet} view, with one exception. The {@code + * addAll} method is implemented as a sequence of {@code put} invocations on the backing map. + * + *

The specified map must be empty at the time this method is invoked, and should not be + * accessed directly after this method returns. These conditions are ensured if the map is created + * empty, passed directly to this method, and no reference to the map is retained, as illustrated + * in the following code fragment: + * + *

{@code
+   * Set identityHashSet = Sets.newSetFromMap(
+   *     new IdentityHashMap());
+   * }
+   *
+   * 

The returned set is serializable if the backing map is. + * + * @param map the backing map + * @return the set backed by the map + * @throws IllegalArgumentException if {@code map} is not empty + * @deprecated Use {@link Collections#newSetFromMap} instead. + */ + @Deprecated + public static Set newSetFromMap(Map map) { + return Collections.newSetFromMap(map); + } + + /** + * An unmodifiable view of a set which may be backed by other sets; this view will change as the + * backing sets do. Contains methods to copy the data into a new set which will then remain + * stable. There is usually no reason to retain a reference of type {@code SetView}; typically, + * you either use it as a plain {@link Set}, or immediately invoke {@link #immutableCopy} or + * {@link #copyInto} and forget the {@code SetView} itself. + * + * @since 2.0 + */ + public abstract static class SetView extends AbstractSet { + private SetView() {} // no subclasses but our own + + /** + * Returns an immutable copy of the current contents of this set view. Does not support null + * elements. + * + *

Warning: this may have unexpected results if a backing set of this view uses a + * nonstandard notion of equivalence, for example if it is a {@link TreeSet} using a comparator + * that is inconsistent with {@link Object#equals(Object)}. + */ + @SuppressWarnings("nullness") // Unsafe, but we can't fix it now. + public ImmutableSet immutableCopy() { + return ImmutableSet.copyOf((SetView) this); + } + + /** + * Copies the current contents of this set view into an existing set. This method has equivalent + * behavior to {@code set.addAll(this)}, assuming that all the sets involved are based on the + * same notion of equivalence. + * + * @return a reference to {@code set}, for convenience + */ + // Note: S should logically extend Set but can't due to either + // some javac bug or some weirdness in the spec, not sure which. + public > S copyInto(S set) { + set.addAll(this); + return set; + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean add(@ParametricNullness E e) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean remove(Object object) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean addAll(Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean removeAll(Collection oldElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean removeIf(java.util.function.Predicate filter) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean retainAll(Collection elementsToKeep) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Scope the return type to {@link UnmodifiableIterator} to ensure this is an unmodifiable view. + * + * @since 20.0 (present with return type {@link Iterator} since 2.0) + */ + @Override + public abstract UnmodifiableIterator iterator(); + } + + /** + * Returns an unmodifiable view of the union of two sets. The returned set contains all + * elements that are contained in either backing set. Iterating over the returned set iterates + * first over all the elements of {@code set1}, then over each element of {@code set2}, in order, + * that is not contained in {@code set1}. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based on different + * equivalence relations, for example if {@code set1} is a {@link HashSet} and {@code set2} is a + * {@link TreeSet} or the {@link Map#keySet} of an {@code IdentityHashMap}. + */ + public static SetView union( + final Set set1, final Set set2) { + checkNotNull(set1, "set1"); + checkNotNull(set2, "set2"); + + return new SetView() { + @Override + public int size() { + int size = set1.size(); + for (E e : set2) { + if (!set1.contains(e)) { + size++; + } + } + return size; + } + + @Override + public boolean isEmpty() { + return set1.isEmpty() && set2.isEmpty(); + } + + @Override + public UnmodifiableIterator iterator() { + return new AbstractIterator() { + final Iterator itr1 = set1.iterator(); + final Iterator itr2 = set2.iterator(); + + @Override + protected E computeNext() { + if (itr1.hasNext()) { + return itr1.next(); + } + while (itr2.hasNext()) { + E e = itr2.next(); + if (!set1.contains(e)) { + return e; + } + } + return endOfData(); + } + }; + } + + @Override + public Stream stream() { + return Stream.concat(set1.stream(), set2.stream().filter((E e) -> !set1.contains(e))); + } + + @Override + public Stream parallelStream() { + return stream().parallel(); + } + + @Override + public boolean contains(Object object) { + return set1.contains(object) || set2.contains(object); + } + + @Override + public > S copyInto(S set) { + set.addAll(set1); + set.addAll(set2); + return set; + } + + @Override + @SuppressWarnings({"nullness", "unchecked"}) // see supertype + public ImmutableSet immutableCopy() { + ImmutableSet.Builder builder = + new ImmutableSet.Builder().addAll((Iterable) set1).addAll((Iterable) set2); + return (ImmutableSet) builder.build(); + } + }; + } + + /** + * Returns an unmodifiable view of the intersection of two sets. The returned set contains + * all elements that are contained by both backing sets. The iteration order of the returned set + * matches that of {@code set1}. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based on different + * equivalence relations, for example if {@code set1} is a {@link HashSet} and {@code set2} is a + * {@link TreeSet} or the {@link Map#keySet} of an {@code IdentityHashMap}. + * + *

Note: The returned view performs slightly better when {@code set1} is the smaller of + * the two sets. If you have reason to believe one of your sets will generally be smaller than the + * other, pass it first. Unfortunately, since this method sets the generic type of the returned + * set based on the type of the first set passed, this could in rare cases force you to make a + * cast, for example: + * + *

{@code
+   * Set aFewBadObjects = ...
+   * Set manyBadStrings = ...
+   *
+   * // impossible for a non-String to be in the intersection
+   * SuppressWarnings("unchecked")
+   * Set badStrings = (Set) Sets.intersection(
+   *     aFewBadObjects, manyBadStrings);
+   * }
+   *
+   * 

This is unfortunate, but should come up only very rarely. + */ + public static SetView intersection(final Set set1, final Set set2) { + checkNotNull(set1, "set1"); + checkNotNull(set2, "set2"); + + return new SetView() { + @Override + public UnmodifiableIterator iterator() { + return new AbstractIterator() { + final Iterator itr = set1.iterator(); + + @Override + protected E computeNext() { + while (itr.hasNext()) { + E e = itr.next(); + if (set2.contains(e)) { + return e; + } + } + return endOfData(); + } + }; + } + + @Override + public Stream stream() { + return set1.stream().filter(set2::contains); + } + + @Override + public Stream parallelStream() { + return set1.parallelStream().filter(set2::contains); + } + + @Override + public int size() { + int size = 0; + for (E e : set1) { + if (set2.contains(e)) { + size++; + } + } + return size; + } + + @Override + public boolean isEmpty() { + return Collections.disjoint(set2, set1); + } + + @Override + public boolean contains(Object object) { + return set1.contains(object) && set2.contains(object); + } + + @Override + public boolean containsAll(Collection collection) { + return set1.containsAll(collection) && set2.containsAll(collection); + } + }; + } + + /** + * Returns an unmodifiable view of the difference of two sets. The returned set contains + * all elements that are contained by {@code set1} and not contained by {@code set2}. {@code set2} + * may also contain elements not present in {@code set1}; these are simply ignored. The iteration + * order of the returned set matches that of {@code set1}. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based on different + * equivalence relations, for example if {@code set1} is a {@link HashSet} and {@code set2} is a + * {@link TreeSet} or the {@link Map#keySet} of an {@code IdentityHashMap}. + */ + public static SetView difference(final Set set1, final Set set2) { + checkNotNull(set1, "set1"); + checkNotNull(set2, "set2"); + + return new SetView() { + @Override + public UnmodifiableIterator iterator() { + return new AbstractIterator() { + final Iterator itr = set1.iterator(); + + @Override + protected E computeNext() { + while (itr.hasNext()) { + E e = itr.next(); + if (!set2.contains(e)) { + return e; + } + } + return endOfData(); + } + }; + } + + @Override + public Stream stream() { + return set1.stream().filter(e -> !set2.contains(e)); + } + + @Override + public Stream parallelStream() { + return set1.parallelStream().filter(e -> !set2.contains(e)); + } + + @Override + public int size() { + int size = 0; + for (E e : set1) { + if (!set2.contains(e)) { + size++; + } + } + return size; + } + + @Override + public boolean isEmpty() { + return set2.containsAll(set1); + } + + @Override + public boolean contains(Object element) { + return set1.contains(element) && !set2.contains(element); + } + }; + } + + /** + * Returns an unmodifiable view of the symmetric difference of two sets. The returned set + * contains all elements that are contained in either {@code set1} or {@code set2} but not in + * both. The iteration order of the returned set is undefined. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based on different + * equivalence relations, for example if {@code set1} is a {@link HashSet} and {@code set2} is a + * {@link TreeSet} or the {@link Map#keySet} of an {@code IdentityHashMap}. + * + * @since 3.0 + */ + public static SetView symmetricDifference( + final Set set1, final Set set2) { + checkNotNull(set1, "set1"); + checkNotNull(set2, "set2"); + + return new SetView() { + @Override + public UnmodifiableIterator iterator() { + final Iterator itr1 = set1.iterator(); + final Iterator itr2 = set2.iterator(); + return new AbstractIterator() { + @Override + public E computeNext() { + while (itr1.hasNext()) { + E elem1 = itr1.next(); + if (!set2.contains(elem1)) { + return elem1; + } + } + while (itr2.hasNext()) { + E elem2 = itr2.next(); + if (!set1.contains(elem2)) { + return elem2; + } + } + return endOfData(); + } + }; + } + + @Override + public int size() { + int size = 0; + for (E e : set1) { + if (!set2.contains(e)) { + size++; + } + } + for (E e : set2) { + if (!set1.contains(e)) { + size++; + } + } + return size; + } + + @Override + public boolean isEmpty() { + return set1.equals(set2); + } + + @Override + public boolean contains(Object element) { + return set1.contains(element) ^ set2.contains(element); + } + }; + } + + /** + * Returns the elements of {@code unfiltered} that satisfy a predicate. The returned set is a live + * view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting set's iterator does not support {@code remove()}, but all other set methods + * are supported. When given an element that doesn't satisfy the predicate, the set's {@code + * add()} and {@code addAll()} methods throw an {@link IllegalArgumentException}. When methods + * such as {@code removeAll()} and {@code clear()} are called on the filtered set, only elements + * that satisfy the filter will be removed from the underlying set. + * + *

The returned set isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered set's methods, such as {@code size()}, iterate across every element in + * the underlying set and determine which elements satisfy the filter. When a live view is + * not needed, it may be faster to copy {@code Iterables.filter(unfiltered, predicate)} and + * use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link + * Iterables#filter(Iterable, Class)} for related functionality.) + * + *

Java 8 users: many use cases for this method are better addressed by {@link + * java.util.stream.Stream#filter}. This method is not being deprecated, but we gently encourage + * you to migrate to streams. + */ + // TODO(kevinb): how to omit that last sentence when building GWT javadoc? + public static Set filter( + Set unfiltered, Predicate predicate) { + if (unfiltered instanceof SortedSet) { + return filter((SortedSet) unfiltered, predicate); + } + if (unfiltered instanceof FilteredSet) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + FilteredSet filtered = (FilteredSet) unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredSet((Set) filtered.unfiltered, combinedPredicate); + } + + return new FilteredSet(checkNotNull(unfiltered), checkNotNull(predicate)); + } + + /** + * Returns the elements of a {@code SortedSet}, {@code unfiltered}, that satisfy a predicate. The + * returned set is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting set's iterator does not support {@code remove()}, but all other set methods + * are supported. When given an element that doesn't satisfy the predicate, the set's {@code + * add()} and {@code addAll()} methods throw an {@link IllegalArgumentException}. When methods + * such as {@code removeAll()} and {@code clear()} are called on the filtered set, only elements + * that satisfy the filter will be removed from the underlying set. + * + *

The returned set isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered set's methods, such as {@code size()}, iterate across every element in + * the underlying set and determine which elements satisfy the filter. When a live view is + * not needed, it may be faster to copy {@code Iterables.filter(unfiltered, predicate)} and + * use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link + * Iterables#filter(Iterable, Class)} for related functionality.) + * + * @since 11.0 + */ + public static SortedSet filter( + SortedSet unfiltered, Predicate predicate) { + if (unfiltered instanceof FilteredSet) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + FilteredSet filtered = (FilteredSet) unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredSortedSet((SortedSet) filtered.unfiltered, combinedPredicate); + } + + return new FilteredSortedSet(checkNotNull(unfiltered), checkNotNull(predicate)); + } + + /** + * Returns the elements of a {@code NavigableSet}, {@code unfiltered}, that satisfy a predicate. + * The returned set is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting set's iterator does not support {@code remove()}, but all other set methods + * are supported. When given an element that doesn't satisfy the predicate, the set's {@code + * add()} and {@code addAll()} methods throw an {@link IllegalArgumentException}. When methods + * such as {@code removeAll()} and {@code clear()} are called on the filtered set, only elements + * that satisfy the filter will be removed from the underlying set. + * + *

The returned set isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered set's methods, such as {@code size()}, iterate across every element in + * the underlying set and determine which elements satisfy the filter. When a live view is + * not needed, it may be faster to copy {@code Iterables.filter(unfiltered, predicate)} and + * use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, as documented at + * {@link Predicate#apply}. Do not provide a predicate such as {@code + * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link + * Iterables#filter(Iterable, Class)} for related functionality.) + * + * @since 14.0 + */ + @GwtIncompatible // NavigableSet + @SuppressWarnings("unchecked") + public static NavigableSet filter( + NavigableSet unfiltered, Predicate predicate) { + if (unfiltered instanceof FilteredSet) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + FilteredSet filtered = (FilteredSet) unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new FilteredNavigableSet((NavigableSet) filtered.unfiltered, combinedPredicate); + } + + return new FilteredNavigableSet(checkNotNull(unfiltered), checkNotNull(predicate)); + } + + private static class FilteredSet extends FilteredCollection + implements Set { + FilteredSet(Set unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + @Override + public boolean equals(Object object) { + return equalsImpl(this, object); + } + + @Override + public int hashCode() { + return hashCodeImpl(this); + } + } + + private static class FilteredSortedSet extends FilteredSet + implements SortedSet { + + FilteredSortedSet(SortedSet unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + @Override + public Comparator comparator() { + return ((SortedSet) unfiltered).comparator(); + } + + @Override + public SortedSet subSet(@ParametricNullness E fromElement, @ParametricNullness E toElement) { + return new FilteredSortedSet( + ((SortedSet) unfiltered).subSet(fromElement, toElement), predicate); + } + + @Override + public SortedSet headSet(@ParametricNullness E toElement) { + return new FilteredSortedSet(((SortedSet) unfiltered).headSet(toElement), predicate); + } + + @Override + public SortedSet tailSet(@ParametricNullness E fromElement) { + return new FilteredSortedSet(((SortedSet) unfiltered).tailSet(fromElement), predicate); + } + + @Override + @ParametricNullness + public E first() { + return Iterators.find(unfiltered.iterator(), predicate); + } + + @Override + @ParametricNullness + public E last() { + SortedSet sortedUnfiltered = (SortedSet) unfiltered; + while (true) { + E element = sortedUnfiltered.last(); + if (predicate.apply(element)) { + return element; + } + sortedUnfiltered = sortedUnfiltered.headSet(element); + } + } + } + + @GwtIncompatible // NavigableSet + private static class FilteredNavigableSet extends FilteredSortedSet + implements NavigableSet { + FilteredNavigableSet(NavigableSet unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + NavigableSet unfiltered() { + return (NavigableSet) unfiltered; + } + + @Override + public E lower(@ParametricNullness E e) { + return Iterators.find(unfiltered().headSet(e, false).descendingIterator(), predicate, null); + } + + @Override + public E floor(@ParametricNullness E e) { + return Iterators.find(unfiltered().headSet(e, true).descendingIterator(), predicate, null); + } + + @Override + public E ceiling(@ParametricNullness E e) { + return Iterables.find(unfiltered().tailSet(e, true), predicate, null); + } + + @Override + public E higher(@ParametricNullness E e) { + return Iterables.find(unfiltered().tailSet(e, false), predicate, null); + } + + @Override + public E pollFirst() { + return Iterables.removeFirstMatching(unfiltered(), predicate); + } + + @Override + public E pollLast() { + return Iterables.removeFirstMatching(unfiltered().descendingSet(), predicate); + } + + @Override + public NavigableSet descendingSet() { + return Sets.filter(unfiltered().descendingSet(), predicate); + } + + @Override + public Iterator descendingIterator() { + return Iterators.filter(unfiltered().descendingIterator(), predicate); + } + + @Override + @ParametricNullness + public E last() { + return Iterators.find(unfiltered().descendingIterator(), predicate); + } + + @Override + public NavigableSet subSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return filter( + unfiltered().subSet(fromElement, fromInclusive, toElement, toInclusive), predicate); + } + + @Override + public NavigableSet headSet(@ParametricNullness E toElement, boolean inclusive) { + return filter(unfiltered().headSet(toElement, inclusive), predicate); + } + + @Override + public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclusive) { + return filter(unfiltered().tailSet(fromElement, inclusive), predicate); + } + } + + /** + * Returns every possible list that can be formed by choosing one element from each of the given + * sets in order; the "n-ary Cartesian + * product" of the sets. For example: + * + *

{@code
+   * Sets.cartesianProduct(ImmutableList.of(
+   *     ImmutableSet.of(1, 2),
+   *     ImmutableSet.of("A", "B", "C")))
+   * }
+ * + *

returns a set containing six lists: + * + *

    + *
  • {@code ImmutableList.of(1, "A")} + *
  • {@code ImmutableList.of(1, "B")} + *
  • {@code ImmutableList.of(1, "C")} + *
  • {@code ImmutableList.of(2, "A")} + *
  • {@code ImmutableList.of(2, "B")} + *
  • {@code ImmutableList.of(2, "C")} + *
+ * + *

The result is guaranteed to be in the "traditional", lexicographical order for Cartesian + * products that you would get from nesting for loops: + * + *

{@code
+   * for (B b0 : sets.get(0)) {
+   *   for (B b1 : sets.get(1)) {
+   *     ...
+   *     ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+   *     // operate on tuple
+   *   }
+   * }
+   * }
+ * + *

Note that if any input set is empty, the Cartesian product will also be empty. If no sets at + * all are provided (an empty list), the resulting Cartesian product has one element, an empty + * list (counter-intuitive, but mathematically consistent). + * + *

Performance notes: while the cartesian product of sets of size {@code m, n, p} is a + * set of size {@code m x n x p}, its actual memory consumption is much smaller. When the + * cartesian set is constructed, the input sets are merely copied. Only as the resulting set is + * iterated are the individual lists created, and these are not retained after iteration. + * + * @param sets the sets to choose elements from, in the order that the elements chosen from those + * sets should appear in the resulting lists + * @param any common base class shared by all axes (often just {@link Object}) + * @return the Cartesian product, as an immutable set containing immutable lists + * @throws NullPointerException if {@code sets}, any one of the {@code sets}, or any element of a + * provided set is null + * @throws IllegalArgumentException if the cartesian product size exceeds the {@code int} range + * @since 2.0 + */ + public static Set> cartesianProduct(List> sets) { + return CartesianSet.create(sets); + } + + /** + * Returns every possible list that can be formed by choosing one element from each of the given + * sets in order; the "n-ary Cartesian + * product" of the sets. For example: + * + *

{@code
+   * Sets.cartesianProduct(
+   *     ImmutableSet.of(1, 2),
+   *     ImmutableSet.of("A", "B", "C"))
+   * }
+ * + *

returns a set containing six lists: + * + *

    + *
  • {@code ImmutableList.of(1, "A")} + *
  • {@code ImmutableList.of(1, "B")} + *
  • {@code ImmutableList.of(1, "C")} + *
  • {@code ImmutableList.of(2, "A")} + *
  • {@code ImmutableList.of(2, "B")} + *
  • {@code ImmutableList.of(2, "C")} + *
+ * + *

The result is guaranteed to be in the "traditional", lexicographical order for Cartesian + * products that you would get from nesting for loops: + * + *

{@code
+   * for (B b0 : sets.get(0)) {
+   *   for (B b1 : sets.get(1)) {
+   *     ...
+   *     ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+   *     // operate on tuple
+   *   }
+   * }
+   * }
+ * + *

Note that if any input set is empty, the Cartesian product will also be empty. If no sets at + * all are provided (an empty list), the resulting Cartesian product has one element, an empty + * list (counter-intuitive, but mathematically consistent). + * + *

Performance notes: while the cartesian product of sets of size {@code m, n, p} is a + * set of size {@code m x n x p}, its actual memory consumption is much smaller. When the + * cartesian set is constructed, the input sets are merely copied. Only as the resulting set is + * iterated are the individual lists created, and these are not retained after iteration. + * + * @param sets the sets to choose elements from, in the order that the elements chosen from those + * sets should appear in the resulting lists + * @param any common base class shared by all axes (often just {@link Object}) + * @return the Cartesian product, as an immutable set containing immutable lists + * @throws NullPointerException if {@code sets}, any one of the {@code sets}, or any element of a + * provided set is null + * @throws IllegalArgumentException if the cartesian product size exceeds the {@code int} range + * @since 2.0 + */ + @SafeVarargs + public static Set> cartesianProduct(Set... sets) { + return cartesianProduct(Arrays.asList(sets)); + } + + private static final class CartesianSet extends ForwardingCollection> + implements Set> { + private final transient ImmutableList> axes; + private final transient CartesianList delegate; + + static Set> create(List> sets) { + ImmutableList.Builder> axesBuilder = new ImmutableList.Builder<>(sets.size()); + for (Set set : sets) { + ImmutableSet copy = ImmutableSet.copyOf(set); + if (copy.isEmpty()) { + return ImmutableSet.of(); + } + axesBuilder.add(copy); + } + final ImmutableList> axes = axesBuilder.build(); + ImmutableList> listAxes = + new ImmutableList>() { + @Override + public int size() { + return axes.size(); + } + + @Override + public List get(int index) { + return axes.get(index).asList(); + } + + @Override + boolean isPartialView() { + return true; + } + }; + return new CartesianSet(axes, new CartesianList(listAxes)); + } + + private CartesianSet(ImmutableList> axes, CartesianList delegate) { + this.axes = axes; + this.delegate = delegate; + } + + @Override + protected Collection> delegate() { + return delegate; + } + + @Override + public boolean contains(Object object) { + if (!(object instanceof List)) { + return false; + } + List list = (List) object; + if (list.size() != axes.size()) { + return false; + } + int i = 0; + for (Object o : list) { + if (!axes.get(i).contains(o)) { + return false; + } + i++; + } + return true; + } + + @Override + public boolean equals(Object object) { + // Warning: this is broken if size() == 0, so it is critical that we + // substitute an empty ImmutableSet to the user in place of this + if (object instanceof CartesianSet) { + CartesianSet that = (CartesianSet) object; + return this.axes.equals(that.axes); + } + return super.equals(object); + } + + @Override + public int hashCode() { + // Warning: this is broken if size() == 0, so it is critical that we + // substitute an empty ImmutableSet to the user in place of this + + // It's a weird formula, but tests prove it works. + int adjust = size() - 1; + for (int i = 0; i < axes.size(); i++) { + adjust *= 31; + adjust = ~~adjust; + // in GWT, we have to deal with integer overflow carefully + } + int hash = 1; + for (Set axis : axes) { + hash = 31 * hash + (size() / axis.size() * axis.hashCode()); + + hash = ~~hash; + } + hash += adjust; + return ~~hash; + } + } + + /** + * Returns the set of all possible subsets of {@code set}. For example, {@code + * powerSet(ImmutableSet.of(1, 2))} returns the set {@code {{}, {1}, {2}, {1, 2}}}. + * + *

Elements appear in these subsets in the same iteration order as they appeared in the input + * set. The order in which these subsets appear in the outer set is undefined. Note that the power + * set of the empty set is not the empty set, but a one-element set containing the empty set. + * + *

The returned set and its constituent sets use {@code equals} to decide whether two elements + * are identical, even if the input set uses a different concept of equivalence. + * + *

Performance notes: while the power set of a set with size {@code n} is of size {@code + * 2^n}, its memory usage is only {@code O(n)}. When the power set is constructed, the input set + * is merely copied. Only as the power set is iterated are the individual subsets created, and + * these subsets themselves occupy only a small constant amount of memory. + * + * @param set the set of elements to construct a power set from + * @return the power set, as an immutable set of immutable sets + * @throws IllegalArgumentException if {@code set} has more than 30 unique elements (causing the + * power set size to exceed the {@code int} range) + * @throws NullPointerException if {@code set} is or contains {@code null} + * @see Power set article at Wikipedia + * @since 4.0 + */ + @GwtCompatible(serializable = false) + public static Set> powerSet(Set set) { + return new PowerSet(set); + } + + private static final class SubSet extends AbstractSet { + private final ImmutableMap inputSet; + private final int mask; + + SubSet(ImmutableMap inputSet, int mask) { + this.inputSet = inputSet; + this.mask = mask; + } + + @Override + public Iterator iterator() { + return new UnmodifiableIterator() { + final ImmutableList elements = inputSet.keySet().asList(); + int remainingSetBits = mask; + + @Override + public boolean hasNext() { + return remainingSetBits != 0; + } + + @Override + public E next() { + int index = Integer.numberOfTrailingZeros(remainingSetBits); + if (index == 32) { + throw new NoSuchElementException(); + } + remainingSetBits &= ~(1 << index); + return elements.get(index); + } + }; + } + + @Override + public int size() { + return Integer.bitCount(mask); + } + + @Override + public boolean contains(Object o) { + Integer index = inputSet.get(o); + return index != null && (mask & (1 << index)) != 0; + } + } + + private static final class PowerSet extends AbstractSet> { + final ImmutableMap inputSet; + + PowerSet(Set input) { + checkArgument( + input.size() <= 30, "Too many elements to create power set: %s > 30", input.size()); + this.inputSet = Maps.indexMap(input); + } + + @Override + public int size() { + return 1 << inputSet.size(); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Iterator> iterator() { + return new AbstractIndexedListIterator>(size()) { + @Override + protected Set get(final int setBits) { + return new SubSet(inputSet, setBits); + } + }; + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof Set) { + Set set = (Set) obj; + return inputSet.keySet().containsAll(set); + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PowerSet) { + PowerSet that = (PowerSet) obj; + return inputSet.keySet().equals(that.inputSet.keySet()); + } + return super.equals(obj); + } + + @Override + public int hashCode() { + /* + * The sum of the sums of the hash codes in each subset is just the sum of + * each input element's hash code times the number of sets that element + * appears in. Each element appears in exactly half of the 2^n sets, so: + */ + return inputSet.keySet().hashCode() << (inputSet.size() - 1); + } + + @Override + public String toString() { + return "powerSet(" + inputSet + ")"; + } + } + + /** + * Returns the set of all subsets of {@code set} of size {@code size}. For example, {@code + * combinations(ImmutableSet.of(1, 2, 3), 2)} returns the set {@code {{1, 2}, {1, 3}, {2, 3}}}. + * + *

Elements appear in these subsets in the same iteration order as they appeared in the input + * set. The order in which these subsets appear in the outer set is undefined. + * + *

The returned set and its constituent sets use {@code equals} to decide whether two elements + * are identical, even if the input set uses a different concept of equivalence. + * + *

Performance notes: the memory usage of the returned set is only {@code O(n)}. When + * the result set is constructed, the input set is merely copied. Only as the result set is + * iterated are the individual subsets created. Each of these subsets occupies an additional O(n) + * memory but only for as long as the user retains a reference to it. That is, the set returned by + * {@code combinations} does not retain the individual subsets. + * + * @param set the set of elements to take combinations of + * @param size the number of elements per combination + * @return the set of all combinations of {@code size} elements from {@code set} + * @throws IllegalArgumentException if {@code size} is not between 0 and {@code set.size()} + * inclusive + * @throws NullPointerException if {@code set} is or contains {@code null} + * @since 23.0 + */ + public static Set> combinations(Set set, final int size) { + final ImmutableMap index = Maps.indexMap(set); + checkNonnegative(size, "size"); + checkArgument(size <= index.size(), "size (%s) must be <= set.size() (%s)", size, index.size()); + if (size == 0) { + return ImmutableSet.>of(ImmutableSet.of()); + } else if (size == index.size()) { + return ImmutableSet.>of(index.keySet()); + } + return new AbstractSet>() { + @Override + public boolean contains(Object o) { + if (o instanceof Set) { + Set s = (Set) o; + return s.size() == size && index.keySet().containsAll(s); + } + return false; + } + + @Override + public Iterator> iterator() { + return new AbstractIterator>() { + final BitSet bits = new BitSet(index.size()); + + @Override + protected Set computeNext() { + if (bits.isEmpty()) { + bits.set(0, size); + } else { + int firstSetBit = bits.nextSetBit(0); + int bitToFlip = bits.nextClearBit(firstSetBit); + + if (bitToFlip == index.size()) { + return endOfData(); + } + + /* + * The current set in sorted order looks like + * {firstSetBit, firstSetBit + 1, ..., bitToFlip - 1, ...} + * where it does *not* contain bitToFlip. + * + * The next combination is + * + * {0, 1, ..., bitToFlip - firstSetBit - 2, bitToFlip, ...} + * + * This is lexicographically next if you look at the combinations in descending order + * e.g. {2, 1, 0}, {3, 1, 0}, {3, 2, 0}, {3, 2, 1}, {4, 1, 0}... + */ + + bits.set(0, bitToFlip - firstSetBit - 1); + bits.clear(bitToFlip - firstSetBit - 1, bitToFlip); + bits.set(bitToFlip); + } + final BitSet copy = (BitSet) bits.clone(); + return new AbstractSet() { + @Override + public boolean contains(Object o) { + Integer i = index.get(o); + return i != null && copy.get(i); + } + + @Override + public Iterator iterator() { + return new AbstractIterator() { + int i = -1; + + @Override + protected E computeNext() { + i = copy.nextSetBit(i + 1); + if (i == -1) { + return endOfData(); + } + return index.keySet().asList().get(i); + } + }; + } + + @Override + public int size() { + return size; + } + }; + } + }; + } + + @Override + public int size() { + return IntMath.binomial(index.size(), size); + } + + @Override + public String toString() { + return "Sets.combinations(" + index.keySet() + ", " + size + ")"; + } + }; + } + + /** An implementation for {@link Set#hashCode()}. */ + static int hashCodeImpl(Set s) { + int hashCode = 0; + for (Object o : s) { + hashCode += o != null ? o.hashCode() : 0; + + hashCode = ~~hashCode; + // Needed to deal with unusual integer overflow in GWT. + } + return hashCode; + } + + /** An implementation for {@link Set#equals(Object)}. */ + static boolean equalsImpl(Set s, Object object) { + if (s == object) { + return true; + } + if (object instanceof Set) { + Set o = (Set) object; + + try { + return s.size() == o.size() && s.containsAll(o); + } catch (NullPointerException | ClassCastException ignored) { + return false; + } + } + return false; + } + + /** + * Returns an unmodifiable view of the specified navigable set. This method allows modules to + * provide users with "read-only" access to internal navigable sets. Query operations on the + * returned set "read through" to the specified set, and attempts to modify the returned set, + * whether direct or via its collection views, result in an {@code UnsupportedOperationException}. + * + *

The returned navigable set will be serializable if the specified navigable set is + * serializable. + * + *

Java 8 users and later: Prefer {@link Collections#unmodifiableNavigableSet}. + * + * @param set the navigable set for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified navigable set + * @since 12.0 + */ + public static NavigableSet unmodifiableNavigableSet(NavigableSet set) { + if (set instanceof ImmutableCollection || set instanceof UnmodifiableNavigableSet) { + return set; + } + return new UnmodifiableNavigableSet(set); + } + + static final class UnmodifiableNavigableSet extends ForwardingSortedSet + implements NavigableSet, Serializable { + private final NavigableSet delegate; + private final SortedSet unmodifiableDelegate; + + UnmodifiableNavigableSet(NavigableSet delegate) { + this.delegate = checkNotNull(delegate); + this.unmodifiableDelegate = Collections.unmodifiableSortedSet(delegate); + } + + @Override + protected SortedSet delegate() { + return unmodifiableDelegate; + } + + // default methods not forwarded by ForwardingSortedSet + + @Override + public boolean removeIf(java.util.function.Predicate filter) { + throw new UnsupportedOperationException(); + } + + @Override + public Stream stream() { + return delegate.stream(); + } + + @Override + public Stream parallelStream() { + return delegate.parallelStream(); + } + + @Override + public void forEach(Consumer action) { + delegate.forEach(action); + } + + @Override + public E lower(@ParametricNullness E e) { + return delegate.lower(e); + } + + @Override + public E floor(@ParametricNullness E e) { + return delegate.floor(e); + } + + @Override + public E ceiling(@ParametricNullness E e) { + return delegate.ceiling(e); + } + + @Override + public E higher(@ParametricNullness E e) { + return delegate.higher(e); + } + + @Override + public E pollFirst() { + throw new UnsupportedOperationException(); + } + + @Override + public E pollLast() { + throw new UnsupportedOperationException(); + } + + private transient UnmodifiableNavigableSet descendingSet; + + @Override + public NavigableSet descendingSet() { + UnmodifiableNavigableSet result = descendingSet; + if (result == null) { + result = descendingSet = new UnmodifiableNavigableSet(delegate.descendingSet()); + result.descendingSet = this; + } + return result; + } + + @Override + public Iterator descendingIterator() { + return Iterators.unmodifiableIterator(delegate.descendingIterator()); + } + + @Override + public NavigableSet subSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return unmodifiableNavigableSet( + delegate.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet headSet(@ParametricNullness E toElement, boolean inclusive) { + return unmodifiableNavigableSet(delegate.headSet(toElement, inclusive)); + } + + @Override + public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclusive) { + return unmodifiableNavigableSet(delegate.tailSet(fromElement, inclusive)); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns a synchronized (thread-safe) navigable set backed by the specified navigable set. In + * order to guarantee serial access, it is critical that all access to the backing + * navigable set is accomplished through the returned navigable set (or its views). + * + *

It is imperative that the user manually synchronize on the returned sorted set when + * iterating over it or any of its {@code descendingSet}, {@code subSet}, {@code headSet}, or + * {@code tailSet} views. + * + *

{@code
+   * NavigableSet set = synchronizedNavigableSet(new TreeSet());
+   *  ...
+   * synchronized (set) {
+   *   // Must be in the synchronized block
+   *   Iterator it = set.iterator();
+   *   while (it.hasNext()) {
+   *     foo(it.next());
+   *   }
+   * }
+   * }
+ * + *

or: + * + *

{@code
+   * NavigableSet set = synchronizedNavigableSet(new TreeSet());
+   * NavigableSet set2 = set.descendingSet().headSet(foo);
+   *  ...
+   * synchronized (set) { // Note: set, not set2!!!
+   *   // Must be in the synchronized block
+   *   Iterator it = set2.descendingIterator();
+   *   while (it.hasNext())
+   *     foo(it.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned navigable set will be serializable if the specified navigable set is + * serializable. + * + *

Java 8 users and later: Prefer {@link Collections#synchronizedNavigableSet}. + * + * @param navigableSet the navigable set to be "wrapped" in a synchronized navigable set. + * @return a synchronized view of the specified navigable set. + * @since 13.0 + */ + @GwtIncompatible // NavigableSet + public static NavigableSet synchronizedNavigableSet( + NavigableSet navigableSet) { + return Synchronized.navigableSet(navigableSet); + } + + /** Remove each element in an iterable from a set. */ + static boolean removeAllImpl(Set set, Iterator iterator) { + boolean changed = false; + while (iterator.hasNext()) { + changed |= set.remove(iterator.next()); + } + return changed; + } + + static boolean removeAllImpl(Set set, Collection collection) { + checkNotNull(collection); // for GWT + if (collection instanceof Multiset) { + collection = ((Multiset) collection).elementSet(); + } + /* + * AbstractSet.removeAll(List) has quadratic behavior if the list size + * is just more than the set's size. We augment the test by + * assuming that sets have fast contains() performance, and other + * collections don't. See + * http://code.google.com/p/guava-libraries/issues/detail?id=1013 + */ + if (collection instanceof Set && collection.size() > set.size()) { + return Iterators.removeAll(set.iterator(), collection); + } else { + return removeAllImpl(set, collection.iterator()); + } + } + + @GwtIncompatible // NavigableSet + static class DescendingSet extends ForwardingNavigableSet { + private final NavigableSet forward; + + DescendingSet(NavigableSet forward) { + this.forward = forward; + } + + @Override + protected NavigableSet delegate() { + return forward; + } + + @Override + public E lower(@ParametricNullness E e) { + return forward.higher(e); + } + + @Override + public E floor(@ParametricNullness E e) { + return forward.ceiling(e); + } + + @Override + public E ceiling(@ParametricNullness E e) { + return forward.floor(e); + } + + @Override + public E higher(@ParametricNullness E e) { + return forward.lower(e); + } + + @Override + public E pollFirst() { + return forward.pollLast(); + } + + @Override + public E pollLast() { + return forward.pollFirst(); + } + + @Override + public NavigableSet descendingSet() { + return forward; + } + + @Override + public Iterator descendingIterator() { + return forward.iterator(); + } + + @Override + public NavigableSet subSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); + } + + @Override + public SortedSet subSet(@ParametricNullness E fromElement, @ParametricNullness E toElement) { + return standardSubSet(fromElement, toElement); + } + + @Override + public NavigableSet headSet(@ParametricNullness E toElement, boolean inclusive) { + return forward.tailSet(toElement, inclusive).descendingSet(); + } + + @Override + public SortedSet headSet(@ParametricNullness E toElement) { + return standardHeadSet(toElement); + } + + @Override + public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclusive) { + return forward.headSet(fromElement, inclusive).descendingSet(); + } + + @Override + public SortedSet tailSet(@ParametricNullness E fromElement) { + return standardTailSet(fromElement); + } + + @SuppressWarnings("unchecked") + @Override + public Comparator comparator() { + Comparator forwardComparator = forward.comparator(); + if (forwardComparator == null) { + return (Comparator) Ordering.natural().reverse(); + } else { + return reverse(forwardComparator); + } + } + + // If we inline this, we get a javac error. + private static Ordering reverse(Comparator forward) { + return Ordering.from(forward).reverse(); + } + + @Override + @ParametricNullness + public E first() { + return forward.last(); + } + + @Override + @ParametricNullness + public E last() { + return forward.first(); + } + + @Override + public Iterator iterator() { + return forward.descendingIterator(); + } + + @Override + public Object[] toArray() { + return standardToArray(); + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return standardToArray(array); + } + + @Override + public String toString() { + return standardToString(); + } + } + + /** + * Returns a view of the portion of {@code set} whose elements are contained by {@code range}. + * + *

This method delegates to the appropriate methods of {@link NavigableSet} (namely {@link + * NavigableSet#subSet(Object, boolean, Object, boolean) subSet()}, {@link + * NavigableSet#tailSet(Object, boolean) tailSet()}, and {@link NavigableSet#headSet(Object, + * boolean) headSet()}) to actually construct the view. Consult these methods for a full + * description of the returned view's behavior. + * + *

Warning: {@code Range}s always represent a range of values using the values' natural + * ordering. {@code NavigableSet} on the other hand can specify a custom ordering via a {@link + * Comparator}, which can violate the natural ordering. Using this method (or in general using + * {@code Range}) with unnaturally-ordered sets can lead to unexpected and undefined behavior. + * + * @since 20.0 + */ + @GwtIncompatible // NavigableSet + public static > NavigableSet subSet( + NavigableSet set, Range range) { + if (set.comparator() != null + && set.comparator() != Ordering.natural() + && range.hasLowerBound() + && range.hasUpperBound()) { + checkArgument( + set.comparator().compare(range.lowerEndpoint(), range.upperEndpoint()) <= 0, + "set is using a custom comparator which is inconsistent with the natural ordering."); + } + if (range.hasLowerBound() && range.hasUpperBound()) { + return set.subSet( + range.lowerEndpoint(), + range.lowerBoundType() == BoundType.CLOSED, + range.upperEndpoint(), + range.upperBoundType() == BoundType.CLOSED); + } else if (range.hasLowerBound()) { + return set.tailSet(range.lowerEndpoint(), range.lowerBoundType() == BoundType.CLOSED); + } else if (range.hasUpperBound()) { + return set.headSet(range.upperEndpoint(), range.upperBoundType() == BoundType.CLOSED); + } + return checkNotNull(set); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableBiMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableBiMap.java new file mode 100644 index 0000000000000..2dbfd4c148a14 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableBiMap.java @@ -0,0 +1,113 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.function.BiConsumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkEntryNotNull; + +/** + * Implementation of {@link ImmutableMap} with exactly one entry. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +final class SingletonImmutableBiMap extends ImmutableBiMap { + + final transient K singleKey; + final transient V singleValue; + + SingletonImmutableBiMap(K singleKey, V singleValue) { + checkEntryNotNull(singleKey, singleValue); + this.singleKey = singleKey; + this.singleValue = singleValue; + this.inverse = null; + } + + private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap inverse) { + this.singleKey = singleKey; + this.singleValue = singleValue; + this.inverse = inverse; + } + + @Override + public V get(Object key) { + return singleKey.equals(key) ? singleValue : null; + } + + @Override + public int size() { + return 1; + } + + @Override + public void forEach(BiConsumer action) { + checkNotNull(action).accept(singleKey, singleValue); + } + + @Override + public boolean containsKey(Object key) { + return singleKey.equals(key); + } + + @Override + public boolean containsValue(Object value) { + return singleValue.equals(value); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + ImmutableSet> createEntrySet() { + return ImmutableSet.of(Maps.immutableEntry(singleKey, singleValue)); + } + + @Override + ImmutableSet createKeySet() { + return ImmutableSet.of(singleKey); + } + + private final transient ImmutableBiMap inverse; + private transient ImmutableBiMap lazyInverse; + + @Override + public ImmutableBiMap inverse() { + if (inverse != null) { + return inverse; + } else { + // racy single-check idiom + ImmutableBiMap result = lazyInverse; + if (result == null) { + return lazyInverse = new SingletonImmutableBiMap<>(singleValue, singleKey, this); + } else { + return result; + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableList.java new file mode 100644 index 0000000000000..7dff152225037 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableList.java @@ -0,0 +1,82 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.util.Collections; +import java.util.Spliterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@link ImmutableList} with exactly one element. + * + * @author Hayward Chan + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +final class SingletonImmutableList extends ImmutableList { + + final transient E element; + + SingletonImmutableList(E element) { + this.element = checkNotNull(element); + } + + @Override + public E get(int index) { + Preconditions.checkElementIndex(index, 1); + return element; + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.singletonIterator(element); + } + + @Override + public Spliterator spliterator() { + return Collections.singleton(element).spliterator(); + } + + @Override + public int size() { + return 1; + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, 1); + return (fromIndex == toIndex) ? ImmutableList.of() : this; + } + + @Override + public String toString() { + return '[' + element.toString() + ']'; + } + + @Override + boolean isPartialView() { + return false; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableSet.java new file mode 100644 index 0000000000000..dba7e71230fc5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableSet.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +/** + * Implementation of {@link ImmutableSet} with exactly one element. + * + * @author Kevin Bourrillion + * @author Nick Kralevich + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +@ElementTypesAreNonnullByDefault +final class SingletonImmutableSet extends ImmutableSet { + // We deliberately avoid caching the asList and hashCode here, to ensure that with + // compressed oops, a SingletonImmutableSet packs all the way down to the optimal 16 bytes. + + final transient E element; + + SingletonImmutableSet(E element) { + this.element = Preconditions.checkNotNull(element); + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean contains(Object target) { + return element.equals(target); + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.singletonIterator(element); + } + + @Override + public ImmutableList asList() { + return ImmutableList.of(element); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + dst[offset] = element; + return offset + 1; + } + + @Override + public final int hashCode() { + return element.hashCode(); + } + + @Override + public String toString() { + return '[' + element.toString() + ']'; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableTable.java new file mode 100644 index 0000000000000..d9eddbe9f47f4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SingletonImmutableTable.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation of {@link ImmutableTable} that holds a single cell. + * + * @author Gregory Kick + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +class SingletonImmutableTable extends ImmutableTable { + final R singleRowKey; + final C singleColumnKey; + final V singleValue; + + SingletonImmutableTable(R rowKey, C columnKey, V value) { + this.singleRowKey = checkNotNull(rowKey); + this.singleColumnKey = checkNotNull(columnKey); + this.singleValue = checkNotNull(value); + } + + SingletonImmutableTable(Cell cell) { + this(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + + @Override + public ImmutableMap column(C columnKey) { + checkNotNull(columnKey); + return containsColumn(columnKey) + ? ImmutableMap.of(singleRowKey, singleValue) + : ImmutableMap.of(); + } + + @Override + public ImmutableMap> columnMap() { + return ImmutableMap.of(singleColumnKey, (Map) ImmutableMap.of(singleRowKey, singleValue)); + } + + @Override + public ImmutableMap> rowMap() { + return ImmutableMap.of(singleRowKey, (Map) ImmutableMap.of(singleColumnKey, singleValue)); + } + + @Override + public int size() { + return 1; + } + + @Override + ImmutableSet> createCellSet() { + return ImmutableSet.of(cellOf(singleRowKey, singleColumnKey, singleValue)); + } + + @Override + ImmutableCollection createValues() { + return ImmutableSet.of(singleValue); + } + + @Override + SerializedForm createSerializedForm() { + return SerializedForm.create(this, new int[] {0}, new int[] {0}); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedIterable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedIterable.java new file mode 100644 index 0000000000000..fcd022b2da66f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedIterable.java @@ -0,0 +1,48 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.Iterator; + +/** + * An {@code Iterable} whose elements are sorted relative to a {@code Comparator}, typically + * provided at creation time. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +interface SortedIterable extends Iterable { + /** + * Returns the {@code Comparator} by which the elements of this iterable are ordered, or {@code + * Ordering.natural()} if the elements are ordered by their natural ordering. + */ + Comparator comparator(); + + /** + * Returns an iterator over elements of type {@code T}. The elements are returned in nondecreasing + * order according to the associated {@link #comparator}. + */ + @Override + Iterator iterator(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedIterables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedIterables.java new file mode 100644 index 0000000000000..203b9b262a388 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedIterables.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Comparator; +import java.util.SortedSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Utilities for dealing with sorted collections of all types. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class SortedIterables { + private SortedIterables() {} + + /** + * Returns {@code true} if {@code elements} is a sorted collection using an ordering equivalent to + * {@code comparator}. + */ + public static boolean hasSameComparator(Comparator comparator, Iterable elements) { + checkNotNull(comparator); + checkNotNull(elements); + Comparator comparator2; + if (elements instanceof SortedSet) { + comparator2 = comparator((SortedSet) elements); + } else if (elements instanceof SortedIterable) { + comparator2 = ((SortedIterable) elements).comparator(); + } else { + return false; + } + return comparator.equals(comparator2); + } + + @SuppressWarnings("unchecked") + // if sortedSet.comparator() is null, the set must be naturally ordered + public static Comparator comparator(SortedSet sortedSet) { + Comparator result = sortedSet.comparator(); + if (result == null) { + result = (Comparator) Ordering.natural(); + } + return result; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedLists.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedLists.java new file mode 100644 index 0000000000000..e3da2796e28b6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedLists.java @@ -0,0 +1,306 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static methods pertaining to sorted {@link List} instances. + * + *

In this documentation, the terms greatest, greater, least, and + * lesser are considered to refer to the comparator on the elements, and the terms + * first and last are considered to refer to the elements' ordering in a list. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class SortedLists { + private SortedLists() {} + + /** + * A specification for which index to return if the list contains at least one element that + * compares as equal to the key. + */ + enum KeyPresentBehavior { + /** + * Return the index of any list element that compares as equal to the key. No guarantees are + * made as to which index is returned, if more than one element compares as equal to the key. + */ + ANY_PRESENT { + @Override + int resultIndex( + Comparator comparator, + @ParametricNullness E key, + List list, + int foundIndex) { + return foundIndex; + } + }, + /** Return the index of the last list element that compares as equal to the key. */ + LAST_PRESENT { + @Override + int resultIndex( + Comparator comparator, + @ParametricNullness E key, + List list, + int foundIndex) { + // Of course, we have to use binary search to find the precise + // breakpoint... + int lower = foundIndex; + int upper = list.size() - 1; + // Everything between lower and upper inclusive compares at >= 0. + while (lower < upper) { + int middle = (lower + upper + 1) >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c > 0) { + upper = middle - 1; + } else { // c == 0 + lower = middle; + } + } + return lower; + } + }, + /** Return the index of the first list element that compares as equal to the key. */ + FIRST_PRESENT { + @Override + int resultIndex( + Comparator comparator, + @ParametricNullness E key, + List list, + int foundIndex) { + // Of course, we have to use binary search to find the precise + // breakpoint... + int lower = 0; + int upper = foundIndex; + // Of course, we have to use binary search to find the precise breakpoint... + // Everything between lower and upper inclusive compares at <= 0. + while (lower < upper) { + int middle = (lower + upper) >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c < 0) { + lower = middle + 1; + } else { // c == 0 + upper = middle; + } + } + return lower; + } + }, + /** + * Return the index of the first list element that compares as greater than the key, or {@code + * list.size()} if there is no such element. + */ + FIRST_AFTER { + @Override + public int resultIndex( + Comparator comparator, + @ParametricNullness E key, + List list, + int foundIndex) { + return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1; + } + }, + /** + * Return the index of the last list element that compares as less than the key, or {@code -1} + * if there is no such element. + */ + LAST_BEFORE { + @Override + public int resultIndex( + Comparator comparator, + @ParametricNullness E key, + List list, + int foundIndex) { + return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1; + } + }; + + abstract int resultIndex( + Comparator comparator, + @ParametricNullness E key, + List list, + int foundIndex); + } + + /** + * A specification for which index to return if the list contains no elements that compare as + * equal to the key. + */ + enum KeyAbsentBehavior { + /** + * Return the index of the next lower element in the list, or {@code -1} if there is no such + * element. + */ + NEXT_LOWER { + @Override + int resultIndex(int higherIndex) { + return higherIndex - 1; + } + }, + /** + * Return the index of the next higher element in the list, or {@code list.size()} if there is + * no such element. + */ + NEXT_HIGHER { + @Override + public int resultIndex(int higherIndex) { + return higherIndex; + } + }, + /** + * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at which + * the key would be inserted into the list: the index of the next higher element in the list, or + * {@code list.size()} if there is no such element. + * + *

Note that the return value will be {@code >= 0} if and only if there is an element of the + * list that compares as equal to the key. + * + *

This is equivalent to the behavior of {@link java.util.Collections#binarySearch(List, + * Object)} when the key isn't present, since {@code ~insertionIndex} is equal to {@code -1 - + * insertionIndex}. + */ + INVERTED_INSERTION_INDEX { + @Override + public int resultIndex(int higherIndex) { + return ~higherIndex; + } + }; + + abstract int resultIndex(int higherIndex); + } + + /** + * Searches the specified naturally ordered list for the specified object using the binary search + * algorithm. + * + *

Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, + * KeyAbsentBehavior)} using {@link Ordering#natural}. + */ + public static int binarySearch( + List list, + E e, + KeyPresentBehavior presentBehavior, + KeyAbsentBehavior absentBehavior) { + checkNotNull(e); + return binarySearch(list, e, Ordering.natural(), presentBehavior, absentBehavior); + } + + /** + * Binary searches the list for the specified key, using the specified key function. + * + *

Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior, + * KeyAbsentBehavior)} using {@link Ordering#natural}. + */ + public static int binarySearch( + List list, + Function keyFunction, + K key, + KeyPresentBehavior presentBehavior, + KeyAbsentBehavior absentBehavior) { + checkNotNull(key); + return binarySearch( + list, keyFunction, key, Ordering.natural(), presentBehavior, absentBehavior); + } + + /** + * Binary searches the list for the specified key, using the specified key function. + * + *

Equivalent to {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, + * KeyAbsentBehavior)} using {@link Lists#transform(List, Function) Lists.transform(list, + * keyFunction)}. + */ + public static int binarySearch( + List list, + Function keyFunction, + @ParametricNullness K key, + Comparator keyComparator, + KeyPresentBehavior presentBehavior, + KeyAbsentBehavior absentBehavior) { + return binarySearch( + Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior); + } + + /** + * Searches the specified list for the specified object using the binary search algorithm. The + * list must be sorted into ascending order according to the specified comparator (as by the + * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior to + * making this call. If it is not sorted, the results are undefined. + * + *

If there are elements in the list which compare as equal to the key, the choice of {@link + * KeyPresentBehavior} decides which index is returned. If no elements compare as equal to the + * key, the choice of {@link KeyAbsentBehavior} decides which index is returned. + * + *

This method runs in log(n) time on random-access lists, which offer near-constant-time + * access to each list element. + * + * @param list the list to be searched. + * @param key the value to be searched for. + * @param comparator the comparator by which the list is ordered. + * @param presentBehavior the specification for what to do if at least one element of the list + * compares as equal to the key. + * @param absentBehavior the specification for what to do if no elements of the list compare as + * equal to the key. + * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list; + * otherwise the index determined by the {@code KeyAbsentBehavior}. + */ + public static int binarySearch( + List list, + @ParametricNullness E key, + Comparator comparator, + KeyPresentBehavior presentBehavior, + KeyAbsentBehavior absentBehavior) { + checkNotNull(comparator); + checkNotNull(list); + checkNotNull(presentBehavior); + checkNotNull(absentBehavior); + if (!(list instanceof RandomAccess)) { + list = Lists.newArrayList(list); + } + // TODO(lowasser): benchmark when it's best to do a linear search + + int lower = 0; + int upper = list.size() - 1; + + while (lower <= upper) { + int middle = (lower + upper) >>> 1; + int c = comparator.compare(key, list.get(middle)); + if (c < 0) { + upper = middle - 1; + } else if (c > 0) { + lower = middle + 1; + } else { + return lower + + presentBehavior.resultIndex( + comparator, key, list.subList(lower, upper + 1), middle - lower); + } + } + return absentBehavior.resultIndex(lower); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMapDifference.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMapDifference.java new file mode 100644 index 0000000000000..e2af356d4fced --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMapDifference.java @@ -0,0 +1,48 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.SortedMap; + +/** + * An object representing the differences between two sorted maps. + * + * @author Louis Wasserman + * @since 8.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface SortedMapDifference + extends MapDifference { + + @Override + SortedMap entriesOnlyOnLeft(); + + @Override + SortedMap entriesOnlyOnRight(); + + @Override + SortedMap entriesInCommon(); + + @Override + SortedMap> entriesDiffering(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultiset.java new file mode 100644 index 0000000000000..c0488a658f837 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultiset.java @@ -0,0 +1,153 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; + +/** + * A {@link Multiset} which maintains the ordering of its elements, according to either their + * natural order or an explicit {@link Comparator}. This order is reflected when iterating over the + * sorted multiset, either directly, or through its {@code elementSet} or {@code entrySet} views. In + * all cases, this implementation uses {@link Comparable#compareTo} or {@link Comparator#compare} + * instead of {@link Object#equals} to determine equivalence of instances. + * + *

Warning: The comparison must be consistent with equals as explained by the + * {@link Comparable} class specification. Otherwise, the resulting multiset will violate the {@link + * Collection} contract, which is specified in terms of {@link Object#equals}. + * + *

See the Guava User Guide article on {@code Multiset}. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public interface SortedMultiset + extends SortedMultisetBridge, SortedIterable { + /** + * Returns the comparator that orders this multiset, or {@link Ordering#natural()} if the natural + * ordering of the elements is used. + */ + @Override + Comparator comparator(); + + /** + * Returns the entry of the first element in this multiset, or {@code null} if this multiset is + * empty. + */ + Entry firstEntry(); + + /** + * Returns the entry of the last element in this multiset, or {@code null} if this multiset is + * empty. + */ + Entry lastEntry(); + + /** + * Returns and removes the entry associated with the lowest element in this multiset, or returns + * {@code null} if this multiset is empty. + */ + Entry pollFirstEntry(); + + /** + * Returns and removes the entry associated with the greatest element in this multiset, or returns + * {@code null} if this multiset is empty. + */ + Entry pollLastEntry(); + + /** + * Returns a {@link NavigableSet} view of the distinct elements in this multiset. + * + * @since 14.0 (present with return type {@code SortedSet} since 11.0) + */ + @Override + NavigableSet elementSet(); + + /** + * {@inheritDoc} + * + *

The {@code entrySet}'s iterator returns entries in ascending element order according to this + * multiset's comparator. + */ + @Override + Set> entrySet(); + + /** + * {@inheritDoc} + * + *

The iterator returns the elements in ascending order according to this multiset's + * comparator. + */ + @Override + Iterator iterator(); + + /** + * Returns a descending view of this multiset. Modifications made to either map will be reflected + * in the other. + */ + SortedMultiset descendingMultiset(); + + /** + * Returns a view of this multiset restricted to the elements less than {@code upperBound}, + * optionally including {@code upperBound} itself. The returned multiset is a view of this + * multiset, so changes to one will be reflected in the other. The returned multiset supports all + * operations that this multiset supports. + * + *

The returned multiset will throw an {@link IllegalArgumentException} on attempts to add + * elements outside its range. + */ + SortedMultiset headMultiset(@ParametricNullness E upperBound, BoundType boundType); + + /** + * Returns a view of this multiset restricted to the range between {@code lowerBound} and {@code + * upperBound}. The returned multiset is a view of this multiset, so changes to one will be + * reflected in the other. The returned multiset supports all operations that this multiset + * supports. + * + *

The returned multiset will throw an {@link IllegalArgumentException} on attempts to add + * elements outside its range. + * + *

This method is equivalent to {@code tailMultiset(lowerBound, + * lowerBoundType).headMultiset(upperBound, upperBoundType)}. + */ + SortedMultiset subMultiset( + @ParametricNullness E lowerBound, + BoundType lowerBoundType, + @ParametricNullness E upperBound, + BoundType upperBoundType); + + /** + * Returns a view of this multiset restricted to the elements greater than {@code lowerBound}, + * optionally including {@code lowerBound} itself. The returned multiset is a view of this + * multiset, so changes to one will be reflected in the other. The returned multiset supports all + * operations that this multiset supports. + * + *

The returned multiset will throw an {@link IllegalArgumentException} on attempts to add + * elements outside its range. + */ + SortedMultiset tailMultiset(@ParametricNullness E lowerBound, BoundType boundType); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultisetBridge.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultisetBridge.java new file mode 100644 index 0000000000000..a6d2b2d63568e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultisetBridge.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.SortedSet; + +/** + * Superinterface of {@link SortedMultiset} to introduce a bridge method for {@code elementSet()}, + * to ensure binary compatibility with older Guava versions that specified {@code elementSet()} to + * return {@code SortedSet}. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +interface SortedMultisetBridge extends Multiset { + @Override + SortedSet elementSet(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultisets.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultisets.java new file mode 100644 index 0000000000000..d2effbe54dc52 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedMultisets.java @@ -0,0 +1,182 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.collect.Multiset.Entry; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.SortedSet; + +import static org.apache.iotdb.google.common.collect.BoundType.CLOSED; +import static org.apache.iotdb.google.common.collect.BoundType.OPEN; + +/** + * Provides static utility methods for creating and working with {@link SortedMultiset} instances. + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class SortedMultisets { + private SortedMultisets() {} + + /** A skeleton implementation for {@link SortedMultiset#elementSet}. */ + @SuppressWarnings("JdkObsolete") // TODO(b/6160855): Switch GWT emulations to NavigableSet. + static class ElementSet extends Multisets.ElementSet + implements SortedSet { + private final SortedMultiset multiset; + + ElementSet(SortedMultiset multiset) { + this.multiset = multiset; + } + + @Override + final SortedMultiset multiset() { + return multiset; + } + + @Override + public Iterator iterator() { + return Multisets.elementIterator(multiset().entrySet().iterator()); + } + + @Override + public Comparator comparator() { + return multiset().comparator(); + } + + @Override + public SortedSet subSet(@ParametricNullness E fromElement, @ParametricNullness E toElement) { + return multiset().subMultiset(fromElement, CLOSED, toElement, OPEN).elementSet(); + } + + @Override + public SortedSet headSet(@ParametricNullness E toElement) { + return multiset().headMultiset(toElement, OPEN).elementSet(); + } + + @Override + public SortedSet tailSet(@ParametricNullness E fromElement) { + return multiset().tailMultiset(fromElement, CLOSED).elementSet(); + } + + @Override + @ParametricNullness + public E first() { + return getElementOrThrow(multiset().firstEntry()); + } + + @Override + @ParametricNullness + public E last() { + return getElementOrThrow(multiset().lastEntry()); + } + } + + /** A skeleton navigable implementation for {@link SortedMultiset#elementSet}. */ + @GwtIncompatible // Navigable + static class NavigableElementSet extends ElementSet + implements NavigableSet { + NavigableElementSet(SortedMultiset multiset) { + super(multiset); + } + + @Override + public E lower(@ParametricNullness E e) { + return getElementOrNull(multiset().headMultiset(e, OPEN).lastEntry()); + } + + @Override + public E floor(@ParametricNullness E e) { + return getElementOrNull(multiset().headMultiset(e, CLOSED).lastEntry()); + } + + @Override + public E ceiling(@ParametricNullness E e) { + return getElementOrNull(multiset().tailMultiset(e, CLOSED).firstEntry()); + } + + @Override + public E higher(@ParametricNullness E e) { + return getElementOrNull(multiset().tailMultiset(e, OPEN).firstEntry()); + } + + @Override + public NavigableSet descendingSet() { + return new NavigableElementSet<>(multiset().descendingMultiset()); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public E pollFirst() { + return getElementOrNull(multiset().pollFirstEntry()); + } + + @Override + public E pollLast() { + return getElementOrNull(multiset().pollLastEntry()); + } + + @Override + public NavigableSet subSet( + @ParametricNullness E fromElement, + boolean fromInclusive, + @ParametricNullness E toElement, + boolean toInclusive) { + return new NavigableElementSet<>( + multiset() + .subMultiset( + fromElement, BoundType.forBoolean(fromInclusive), + toElement, BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableSet headSet(@ParametricNullness E toElement, boolean inclusive) { + return new NavigableElementSet<>( + multiset().headMultiset(toElement, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableSet tailSet(@ParametricNullness E fromElement, boolean inclusive) { + return new NavigableElementSet<>( + multiset().tailMultiset(fromElement, BoundType.forBoolean(inclusive))); + } + } + + private static E getElementOrThrow(Entry entry) { + if (entry == null) { + throw new NoSuchElementException(); + } + return entry.getElement(); + } + + private static E getElementOrNull(Entry entry) { + return (entry == null) ? null : entry.getElement(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedSetMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedSetMultimap.java new file mode 100644 index 0000000000000..5a0bed7402aa2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SortedSetMultimap.java @@ -0,0 +1,117 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +/** + * A {@code SetMultimap} whose set of values for a given key are kept sorted; that is, they comprise + * a {@link SortedSet}. It cannot hold duplicate key-value pairs; adding a key-value pair that's + * already in the multimap has no effect. This interface does not specify the ordering of the + * multimap's keys. See the {@link Multimap} documentation for information common to all multimaps. + * + *

The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods each return a {@link + * SortedSet} of values, while {@link Multimap#entries()} returns a {@link Set} of map entries. + * Though the method signature doesn't say so explicitly, the map returned by {@link #asMap} has + * {@code SortedSet} values. + * + *

Warning: As in all {@link SetMultimap}s, do not modify either a key or a value + * of a {@code SortedSetMultimap} in a way that affects its {@link Object#equals} behavior (or its + * position in the order of the values). Undefined behavior and bugs will result. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface SortedSetMultimap extends SetMultimap { + // Following Javadoc copied from Multimap. + + /** + * Returns a collection view of all values associated with a key. If no mappings in the multimap + * have the provided key, an empty collection is returned. + * + *

Changes to the returned collection will update the underlying multimap, and vice versa. + * + *

Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method + * returns a {@link SortedSet}, instead of the {@link java.util.Collection} specified in the + * {@link Multimap} interface. + */ + @Override + SortedSet get(@ParametricNullness K key); + + /** + * Removes all values associated with a given key. + * + *

Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method + * returns a {@link SortedSet}, instead of the {@link java.util.Collection} specified in the + * {@link Multimap} interface. + */ + @Override + SortedSet removeAll(Object key); + + /** + * Stores a collection of values with the same key, replacing any existing values for that key. + * + *

Because a {@code SortedSetMultimap} has unique sorted values for a given key, this method + * returns a {@link SortedSet}, instead of the {@link java.util.Collection} specified in the + * {@link Multimap} interface. + * + *

Any duplicates in {@code values} will be stored in the multimap once. + */ + @Override + SortedSet replaceValues(@ParametricNullness K key, Iterable values); + + /** + * Returns a map view that associates each key with the corresponding values in the multimap. + * Changes to the returned map, such as element removal, will update the underlying multimap. The + * map does not support {@code setValue()} on its entries, {@code put}, or {@code putAll}. + * + *

When passed a key that is present in the map, {@code asMap().get(Object)} has the same + * behavior as {@link #get}, returning a live collection. When passed a key that is not present, + * however, {@code asMap().get(Object)} returns {@code null} instead of an empty collection. + * + *

Note: The returned map's values are guaranteed to be of type {@link SortedSet}. To + * obtain this map with the more specific generic type {@code Map>}, call {@link + * Multimaps#asMap(SortedSetMultimap)} instead. However, the returned map itself is + * not necessarily a {@link SortedMap}: A {@code SortedSetMultimap} must expose the values + * for a given key in sorted order, but it need not expose the keys in sorted order. + * Individual {@code SortedSetMultimap} implementations, like those built with {@link + * MultimapBuilder#treeKeys()}, may make additional guarantees. + */ + @Override + Map> asMap(); + + /** + * Returns the comparator that orders the multimap values, with {@code null} indicating that + * natural ordering is used. + */ + Comparator valueComparator(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SparseImmutableTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SparseImmutableTable.java new file mode 100644 index 0000000000000..e1ee1e0abfd9e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/SparseImmutableTable.java @@ -0,0 +1,147 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static java.util.Objects.requireNonNull; + +/** A {@code RegularImmutableTable} optimized for sparse data. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class SparseImmutableTable extends RegularImmutableTable { + static final ImmutableTable EMPTY = + new SparseImmutableTable<>( + ImmutableList.>of(), ImmutableSet.of(), ImmutableSet.of()); + + private final ImmutableMap> rowMap; + private final ImmutableMap> columnMap; + + // For each cell in iteration order, the index of that cell's row key in the row key list. + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final int[] cellRowIndices; + + // For each cell in iteration order, the index of that cell's column key in the list of column + // keys present in that row. + @SuppressWarnings("Immutable") // We don't modify this after construction. + private final int[] cellColumnInRowIndices; + + SparseImmutableTable( + ImmutableList> cellList, + ImmutableSet rowSpace, + ImmutableSet columnSpace) { + Map rowIndex = Maps.indexMap(rowSpace); + Map> rows = Maps.newLinkedHashMap(); + for (R row : rowSpace) { + rows.put(row, new LinkedHashMap()); + } + Map> columns = Maps.newLinkedHashMap(); + for (C col : columnSpace) { + columns.put(col, new LinkedHashMap()); + } + int[] cellRowIndices = new int[cellList.size()]; + int[] cellColumnInRowIndices = new int[cellList.size()]; + for (int i = 0; i < cellList.size(); i++) { + Cell cell = cellList.get(i); + R rowKey = cell.getRowKey(); + C columnKey = cell.getColumnKey(); + V value = cell.getValue(); + + /* + * These requireNonNull calls are safe because we construct the maps to hold all the provided + * cells. + */ + cellRowIndices[i] = requireNonNull(rowIndex.get(rowKey)); + Map thisRow = requireNonNull(rows.get(rowKey)); + cellColumnInRowIndices[i] = thisRow.size(); + V oldValue = thisRow.put(columnKey, value); + checkNoDuplicate(rowKey, columnKey, oldValue, value); + requireNonNull(columns.get(columnKey)).put(rowKey, value); + } + this.cellRowIndices = cellRowIndices; + this.cellColumnInRowIndices = cellColumnInRowIndices; + ImmutableMap.Builder> rowBuilder = + new ImmutableMap.Builder<>(rows.size()); + for (Entry> row : rows.entrySet()) { + rowBuilder.put(row.getKey(), ImmutableMap.copyOf(row.getValue())); + } + this.rowMap = rowBuilder.buildOrThrow(); + + ImmutableMap.Builder> columnBuilder = + new ImmutableMap.Builder<>(columns.size()); + for (Entry> col : columns.entrySet()) { + columnBuilder.put(col.getKey(), ImmutableMap.copyOf(col.getValue())); + } + this.columnMap = columnBuilder.buildOrThrow(); + } + + @Override + public ImmutableMap> columnMap() { + // Casts without copying. + ImmutableMap> columnMap = this.columnMap; + return ImmutableMap.>copyOf(columnMap); + } + + @Override + public ImmutableMap> rowMap() { + // Casts without copying. + ImmutableMap> rowMap = this.rowMap; + return ImmutableMap.>copyOf(rowMap); + } + + @Override + public int size() { + return cellRowIndices.length; + } + + @Override + Cell getCell(int index) { + int rowIndex = cellRowIndices[index]; + Entry> rowEntry = rowMap.entrySet().asList().get(rowIndex); + ImmutableMap row = rowEntry.getValue(); + int columnIndex = cellColumnInRowIndices[index]; + Entry colEntry = row.entrySet().asList().get(columnIndex); + return cellOf(rowEntry.getKey(), colEntry.getKey(), colEntry.getValue()); + } + + @Override + V getValue(int index) { + int rowIndex = cellRowIndices[index]; + ImmutableMap row = rowMap.values().asList().get(rowIndex); + int columnIndex = cellColumnInRowIndices[index]; + return row.values().asList().get(columnIndex); + } + + @Override + SerializedForm createSerializedForm() { + Map columnKeyToIndex = Maps.indexMap(columnKeySet()); + int[] cellColumnIndices = new int[cellSet().size()]; + int i = 0; + for (Cell cell : cellSet()) { + // requireNonNull is safe because the cell exists in the table. + cellColumnIndices[i++] = requireNonNull(columnKeyToIndex.get(cell.getColumnKey())); + } + return SerializedForm.create(this, cellRowIndices, cellColumnIndices); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/StandardRowSortedTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/StandardRowSortedTable.java new file mode 100644 index 0000000000000..1020516386a09 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/StandardRowSortedTable.java @@ -0,0 +1,146 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@code Table} whose iteration ordering across row keys is sorted by their + * natural ordering or by a supplied comparator. Note that iterations across the columns keys for a + * single row key may or may not be ordered, depending on the implementation. When rows and columns + * are both sorted, it's easier to use the {@link TreeBasedTable} subclass. + * + *

The {@link #rowKeySet} method returns a {@link SortedSet} and the {@link #rowMap} method + * returns a {@link SortedMap}, instead of the {@link Set} and {@link Map} specified by the {@link + * Table} interface. + * + *

Null keys and values are not supported. + * + *

See the {@link StandardTable} superclass for more information about the behavior of this + * class. + * + * @author Jared Levy + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +class StandardRowSortedTable extends StandardTable + implements RowSortedTable { + /* + * TODO(jlevy): Consider adding headTable, tailTable, and subTable methods, + * which return a Table view with rows keys in a given range. Create a + * RowSortedTable subinterface with the revised methods? + */ + + StandardRowSortedTable( + SortedMap> backingMap, Supplier> factory) { + super(backingMap, factory); + } + + private SortedMap> sortedBackingMap() { + return (SortedMap>) backingMap; + } + + /** + * {@inheritDoc} + * + *

This method returns a {@link SortedSet}, instead of the {@code Set} specified in the {@link + * Table} interface. + */ + @Override + public SortedSet rowKeySet() { + return (SortedSet) rowMap().keySet(); + } + + /** + * {@inheritDoc} + * + *

This method returns a {@link SortedMap}, instead of the {@code Map} specified in the {@link + * Table} interface. + */ + @Override + public SortedMap> rowMap() { + return (SortedMap>) super.rowMap(); + } + + @Override + SortedMap> createRowMap() { + return new RowSortedMap(); + } + + private class RowSortedMap extends RowMap implements SortedMap> { + @Override + public SortedSet keySet() { + return (SortedSet) super.keySet(); + } + + @Override + SortedSet createKeySet() { + return new Maps.SortedKeySet<>(this); + } + + @Override + public Comparator comparator() { + return sortedBackingMap().comparator(); + } + + @Override + public R firstKey() { + return sortedBackingMap().firstKey(); + } + + @Override + public R lastKey() { + return sortedBackingMap().lastKey(); + } + + @Override + public SortedMap> headMap(R toKey) { + checkNotNull(toKey); + return new StandardRowSortedTable(sortedBackingMap().headMap(toKey), factory) + .rowMap(); + } + + @Override + public SortedMap> subMap(R fromKey, R toKey) { + checkNotNull(fromKey); + checkNotNull(toKey); + return new StandardRowSortedTable(sortedBackingMap().subMap(fromKey, toKey), factory) + .rowMap(); + } + + @Override + public SortedMap> tailMap(R fromKey) { + checkNotNull(fromKey); + return new StandardRowSortedTable(sortedBackingMap().tailMap(fromKey), factory) + .rowMap(); + } + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/StandardTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/StandardTable.java new file mode 100644 index 0000000000000..8615245b4b3d9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/StandardTable.java @@ -0,0 +1,1021 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.collect.Maps.IteratorBasedAbstractMap; +import org.apache.iotdb.google.common.collect.Maps.ViewCachingAbstractMap; +import org.apache.iotdb.google.common.collect.Sets.ImprovedAbstractSet; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Predicates.alwaysTrue; +import static org.apache.iotdb.google.common.base.Predicates.equalTo; +import static org.apache.iotdb.google.common.base.Predicates.in; +import static org.apache.iotdb.google.common.base.Predicates.not; +import static org.apache.iotdb.google.common.collect.Maps.safeContainsKey; +import static org.apache.iotdb.google.common.collect.Maps.safeGet; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * {@link Table} implementation backed by a map that associates row keys with column key / value + * secondary maps. This class provides rapid access to records by the row key alone or by both keys, + * but not by just the column key. + * + *

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link #columnMap()} have + * iterators that don't support {@code remove()}. Otherwise, all optional operations are supported. + * Null row keys, columns keys, and values are not supported. + * + *

Lookups by row key are often faster than lookups by column key, because the data is stored in + * a {@code Map>}. A method call like {@code column(columnKey).get(rowKey)} still runs + * quickly, since the row key is provided. However, {@code column(columnKey).size()} takes longer, + * since an iteration across all row keys occurs. + * + *

Note that this implementation is not synchronized. If multiple threads access this table + * concurrently and one of the threads modifies the table, it must be synchronized externally. + * + * @author Jared Levy + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +class StandardTable extends AbstractTable implements Serializable { + @GwtTransient final Map> backingMap; + @GwtTransient final Supplier> factory; + + StandardTable(Map> backingMap, Supplier> factory) { + this.backingMap = backingMap; + this.factory = factory; + } + + // Accessors + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + if (columnKey == null) { + return false; + } + for (Map map : backingMap.values()) { + if (safeContainsKey(map, columnKey)) { + return true; + } + } + return false; + } + + @Override + public boolean containsRow(Object rowKey) { + return rowKey != null && safeContainsKey(backingMap, rowKey); + } + + @Override + public boolean containsValue(Object value) { + return value != null && super.containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return (rowKey == null || columnKey == null) ? null : super.get(rowKey, columnKey); + } + + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override + public int size() { + int size = 0; + for (Map map : backingMap.values()) { + size += map.size(); + } + return size; + } + + // Mutators + + @Override + public void clear() { + backingMap.clear(); + } + + private Map getOrCreate(R rowKey) { + Map map = backingMap.get(rowKey); + if (map == null) { + map = factory.get(); + backingMap.put(rowKey, map); + } + return map; + } + + @Override + public V put(R rowKey, C columnKey, V value) { + checkNotNull(rowKey); + checkNotNull(columnKey); + checkNotNull(value); + return getOrCreate(rowKey).put(columnKey, value); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + if ((rowKey == null) || (columnKey == null)) { + return null; + } + Map map = safeGet(backingMap, rowKey); + if (map == null) { + return null; + } + V value = map.remove(columnKey); + if (map.isEmpty()) { + backingMap.remove(rowKey); + } + return value; + } + + private Map removeColumn(Object column) { + Map output = new LinkedHashMap<>(); + Iterator>> iterator = backingMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + V value = entry.getValue().remove(column); + if (value != null) { + output.put(entry.getKey(), value); + if (entry.getValue().isEmpty()) { + iterator.remove(); + } + } + } + return output; + } + + private boolean containsMapping(Object rowKey, Object columnKey, Object value) { + return value != null && value.equals(get(rowKey, columnKey)); + } + + /** Remove a row key / column key / value mapping, if present. */ + private boolean removeMapping(Object rowKey, Object columnKey, Object value) { + if (containsMapping(rowKey, columnKey, value)) { + remove(rowKey, columnKey); + return true; + } + return false; + } + + // Views + + /** + * Abstract set whose {@code isEmpty()} returns whether the table is empty and whose {@code + * clear()} clears all table mappings. + */ + private abstract class TableSet extends ImprovedAbstractSet { + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override + public void clear() { + backingMap.clear(); + } + } + + /** + * {@inheritDoc} + * + *

The set's iterator traverses the mappings for the first row, the mappings for the second + * row, and so on. + * + *

Each cell is an immutable snapshot of a row key / column key / value mapping, taken at the + * time the cell is returned by a method call to the set or its iterator. + */ + @Override + public Set> cellSet() { + return super.cellSet(); + } + + @Override + Iterator> cellIterator() { + return new CellIterator(); + } + + private class CellIterator implements Iterator> { + final Iterator>> rowIterator = backingMap.entrySet().iterator(); + Entry> rowEntry; + Iterator> columnIterator = Iterators.emptyModifiableIterator(); + + @Override + public boolean hasNext() { + return rowIterator.hasNext() || columnIterator.hasNext(); + } + + @Override + public Cell next() { + if (!columnIterator.hasNext()) { + rowEntry = rowIterator.next(); + columnIterator = rowEntry.getValue().entrySet().iterator(); + } + /* + * requireNonNull is safe because: + * + * - columnIterator started off pointing to an empty iterator, so we must have entered the + * `if` body above at least once. Thus, if we got this far, that `if` body initialized + * rowEntry at least once. + * + * - The only case in which rowEntry is cleared (during remove() below) happens only if the + * caller removed every element from columnIterator. During that process, we would have had + * to iterate it to exhaustion. Then we can apply the logic above about an empty + * columnIterator. (This assumes no concurrent modification, but behavior under concurrent + * modification is undefined, anyway.) + */ + requireNonNull(rowEntry); + Entry columnEntry = columnIterator.next(); + return Tables.immutableCell(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); + } + + @Override + public void remove() { + columnIterator.remove(); + /* + * requireNonNull is safe because: + * + * - columnIterator.remove() succeeded, so it must have returned a value, so it must have been + * initialized by next() -- which initializes rowEntry, too. + * + * - rowEntry isn't cleared except below. If it was cleared below, then either + * columnIterator.remove() would have failed above (if the user hasn't called next() since + * then) or rowEntry would have been initialized by next() (as discussed above). + */ + if (requireNonNull(rowEntry).getValue().isEmpty()) { + rowIterator.remove(); + rowEntry = null; + } + } + } + + @Override + Spliterator> cellSpliterator() { + return CollectSpliterators.flatMap( + backingMap.entrySet().spliterator(), + (Entry> rowEntry) -> + CollectSpliterators.map( + rowEntry.getValue().entrySet().spliterator(), + (Entry columnEntry) -> + Tables.immutableCell( + rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue())), + Spliterator.DISTINCT | Spliterator.SIZED, + size()); + } + + @Override + public Map row(R rowKey) { + return new Row(rowKey); + } + + class Row extends IteratorBasedAbstractMap { + final R rowKey; + + Row(R rowKey) { + this.rowKey = checkNotNull(rowKey); + } + + Map backingRowMap; + + final void updateBackingRowMapField() { + if (backingRowMap == null || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) { + backingRowMap = computeBackingRowMap(); + } + } + + Map computeBackingRowMap() { + return backingMap.get(rowKey); + } + + // Call this every time we perform a removal. + void maintainEmptyInvariant() { + updateBackingRowMapField(); + if (backingRowMap != null && backingRowMap.isEmpty()) { + backingMap.remove(rowKey); + backingRowMap = null; + } + } + + @Override + public boolean containsKey(Object key) { + updateBackingRowMapField(); + return (key != null && backingRowMap != null) && Maps.safeContainsKey(backingRowMap, key); + } + + @Override + public V get(Object key) { + updateBackingRowMapField(); + return (key != null && backingRowMap != null) ? Maps.safeGet(backingRowMap, key) : null; + } + + @Override + public V put(C key, V value) { + checkNotNull(key); + checkNotNull(value); + if (backingRowMap != null && !backingRowMap.isEmpty()) { + return backingRowMap.put(key, value); + } + return StandardTable.this.put(rowKey, key, value); + } + + @Override + public V remove(Object key) { + updateBackingRowMapField(); + if (backingRowMap == null) { + return null; + } + V result = Maps.safeRemove(backingRowMap, key); + maintainEmptyInvariant(); + return result; + } + + @Override + public void clear() { + updateBackingRowMapField(); + if (backingRowMap != null) { + backingRowMap.clear(); + } + maintainEmptyInvariant(); + } + + @Override + public int size() { + updateBackingRowMapField(); + return (backingRowMap == null) ? 0 : backingRowMap.size(); + } + + @Override + Iterator> entryIterator() { + updateBackingRowMapField(); + if (backingRowMap == null) { + return Iterators.emptyModifiableIterator(); + } + final Iterator> iterator = backingRowMap.entrySet().iterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Entry next() { + return wrapEntry(iterator.next()); + } + + @Override + public void remove() { + iterator.remove(); + maintainEmptyInvariant(); + } + }; + } + + @Override + Spliterator> entrySpliterator() { + updateBackingRowMapField(); + if (backingRowMap == null) { + return Spliterators.emptySpliterator(); + } + return CollectSpliterators.map(backingRowMap.entrySet().spliterator(), this::wrapEntry); + } + + Entry wrapEntry(final Entry entry) { + return new ForwardingMapEntry() { + @Override + protected Entry delegate() { + return entry; + } + + @Override + public V setValue(V value) { + return super.setValue(checkNotNull(value)); + } + + @Override + public boolean equals(Object object) { + // TODO(lowasser): identify why this affects GWT tests + return standardEquals(object); + } + }; + } + } + + /** + * {@inheritDoc} + * + *

The returned map's views have iterators that don't support {@code remove()}. + */ + @Override + public Map column(C columnKey) { + return new Column(columnKey); + } + + private class Column extends ViewCachingAbstractMap { + final C columnKey; + + Column(C columnKey) { + this.columnKey = checkNotNull(columnKey); + } + + @Override + public V put(R key, V value) { + return StandardTable.this.put(key, columnKey, value); + } + + @Override + public V get(Object key) { + return StandardTable.this.get(key, columnKey); + } + + @Override + public boolean containsKey(Object key) { + return StandardTable.this.contains(key, columnKey); + } + + @Override + public V remove(Object key) { + return StandardTable.this.remove(key, columnKey); + } + + /** Removes all {@code Column} mappings whose row key and value satisfy the given predicate. */ + boolean removeFromColumnIf(Predicate> predicate) { + boolean changed = false; + Iterator>> iterator = backingMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + Map map = entry.getValue(); + V value = map.get(columnKey); + if (value != null && predicate.apply(Maps.immutableEntry(entry.getKey(), value))) { + map.remove(columnKey); + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + Set> createEntrySet() { + return new EntrySet(); + } + + private class EntrySet extends ImprovedAbstractSet> { + @Override + public Iterator> iterator() { + return new EntrySetIterator(); + } + + @Override + public int size() { + int size = 0; + for (Map map : backingMap.values()) { + if (map.containsKey(columnKey)) { + size++; + } + } + return size; + } + + @Override + public boolean isEmpty() { + return !containsColumn(columnKey); + } + + @Override + public void clear() { + removeFromColumnIf(alwaysTrue()); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + return containsMapping(entry.getKey(), columnKey, entry.getValue()); + } + return false; + } + + @Override + public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return removeMapping(entry.getKey(), columnKey, entry.getValue()); + } + return false; + } + + @Override + public boolean retainAll(Collection c) { + return removeFromColumnIf(not(in(c))); + } + } + + private class EntrySetIterator extends AbstractIterator> { + final Iterator>> iterator = backingMap.entrySet().iterator(); + + @Override + protected Entry computeNext() { + while (iterator.hasNext()) { + final Entry> entry = iterator.next(); + if (entry.getValue().containsKey(columnKey)) { + class EntryImpl extends AbstractMapEntry { + @Override + public R getKey() { + return entry.getKey(); + } + + @Override + public V getValue() { + return entry.getValue().get(columnKey); + } + + @Override + public V setValue(V value) { + /* + * The cast is safe because of the containsKey check above. (Well, it's possible for + * the map to change between that call and this one. But if that happens, the + * behavior is undefined because of the concurrent mutation.) + * + * (Our prototype checker happens to be "smart" enough to understand this for the + * *get* call in getValue but not for the *put* call here.) + * + * (Arguably we should use requireNonNull rather than uncheckedCastNullableTToT: We + * know that V is a non-null type because that's the only kind of value type that + * StandardTable supports. Thus, requireNonNull is safe as long as the cell is still + * present. (And if it's not present, behavior is undefined.) However, that's a + * behavior change relative to the old code, so it didn't seem worth risking.) + */ + return uncheckedCastNullableTToT( + entry.getValue().put(columnKey, checkNotNull(value))); + } + } + return new EntryImpl(); + } + } + return endOfData(); + } + } + + @Override + Set createKeySet() { + return new KeySet(); + } + + private class KeySet extends Maps.KeySet { + KeySet() { + super(Column.this); + } + + @Override + public boolean contains(Object obj) { + return StandardTable.this.contains(obj, columnKey); + } + + @Override + public boolean remove(Object obj) { + return StandardTable.this.remove(obj, columnKey) != null; + } + + @Override + public boolean retainAll(final Collection c) { + return removeFromColumnIf(Maps.keyPredicateOnEntries(not(in(c)))); + } + } + + @Override + Collection createValues() { + return new Values(); + } + + private class Values extends Maps.Values { + Values() { + super(Column.this); + } + + @Override + public boolean remove(Object obj) { + return obj != null && removeFromColumnIf(Maps.valuePredicateOnEntries(equalTo(obj))); + } + + @Override + public boolean removeAll(final Collection c) { + return removeFromColumnIf(Maps.valuePredicateOnEntries(in(c))); + } + + @Override + public boolean retainAll(final Collection c) { + return removeFromColumnIf(Maps.valuePredicateOnEntries(not(in(c)))); + } + } + } + + @Override + public Set rowKeySet() { + return rowMap().keySet(); + } + + private transient Set columnKeySet; + + /** + * {@inheritDoc} + * + *

The returned set has an iterator that does not support {@code remove()}. + * + *

The set's iterator traverses the columns of the first row, the columns of the second row, + * etc., skipping any columns that have appeared previously. + */ + @Override + public Set columnKeySet() { + Set result = columnKeySet; + return (result == null) ? columnKeySet = new ColumnKeySet() : result; + } + + private class ColumnKeySet extends TableSet { + @Override + public Iterator iterator() { + return createColumnKeyIterator(); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + + @Override + public boolean remove(Object obj) { + if (obj == null) { + return false; + } + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + if (map.keySet().remove(obj)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + checkNotNull(c); + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + // map.keySet().removeAll(c) can throw a NPE when map is a TreeMap with + // natural ordering and c contains a null. + if (Iterators.removeAll(map.keySet().iterator(), c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + if (map.keySet().retainAll(c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + public boolean contains(Object obj) { + return containsColumn(obj); + } + } + + /** Creates an iterator that returns each column value with duplicates omitted. */ + Iterator createColumnKeyIterator() { + return new ColumnKeyIterator(); + } + + private class ColumnKeyIterator extends AbstractIterator { + // Use the same map type to support TreeMaps with comparators that aren't + // consistent with equals(). + final Map seen = factory.get(); + final Iterator> mapIterator = backingMap.values().iterator(); + Iterator> entryIterator = Iterators.emptyIterator(); + + @Override + protected C computeNext() { + while (true) { + if (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + if (!seen.containsKey(entry.getKey())) { + seen.put(entry.getKey(), entry.getValue()); + return entry.getKey(); + } + } else if (mapIterator.hasNext()) { + entryIterator = mapIterator.next().entrySet().iterator(); + } else { + return endOfData(); + } + } + } + } + + /** + * {@inheritDoc} + * + *

The collection's iterator traverses the values for the first row, the values for the second + * row, and so on. + */ + @Override + public Collection values() { + return super.values(); + } + + private transient Map> rowMap; + + @Override + public Map> rowMap() { + Map> result = rowMap; + return (result == null) ? rowMap = createRowMap() : result; + } + + Map> createRowMap() { + return new RowMap(); + } + + class RowMap extends ViewCachingAbstractMap> { + @Override + public boolean containsKey(Object key) { + return containsRow(key); + } + + // performing cast only when key is in backing map and has the correct type + @SuppressWarnings("unchecked") + @Override + public Map get(Object key) { + // requireNonNull is safe because of the containsRow check. + return containsRow(key) ? row((R) requireNonNull(key)) : null; + } + + @Override + public Map remove(Object key) { + return (key == null) ? null : backingMap.remove(key); + } + + @Override + protected Set>> createEntrySet() { + return new EntrySet(); + } + + class EntrySet extends TableSet>> { + @Override + public Iterator>> iterator() { + return Maps.asMapEntryIterator( + backingMap.keySet(), + new Function>() { + @Override + public Map apply(R rowKey) { + return row(rowKey); + } + }); + } + + @Override + public int size() { + return backingMap.size(); + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return entry.getKey() != null + && entry.getValue() instanceof Map + && Collections2.safeContains(backingMap.entrySet(), entry); + } + return false; + } + + @Override + public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return entry.getKey() != null + && entry.getValue() instanceof Map + && backingMap.entrySet().remove(entry); + } + return false; + } + } + } + + private transient ColumnMap columnMap; + + @Override + public Map> columnMap() { + ColumnMap result = columnMap; + return (result == null) ? columnMap = new ColumnMap() : result; + } + + private class ColumnMap extends ViewCachingAbstractMap> { + // The cast to C occurs only when the key is in the map, implying that it + // has the correct type. + @SuppressWarnings("unchecked") + @Override + public Map get(Object key) { + // requireNonNull is safe because of the containsColumn check. + return containsColumn(key) ? column((C) requireNonNull(key)) : null; + } + + @Override + public boolean containsKey(Object key) { + return containsColumn(key); + } + + @Override + public Map remove(Object key) { + return containsColumn(key) ? removeColumn(key) : null; + } + + @Override + public Set>> createEntrySet() { + return new ColumnMapEntrySet(); + } + + @Override + public Set keySet() { + return columnKeySet(); + } + + @Override + Collection> createValues() { + return new ColumnMapValues(); + } + + class ColumnMapEntrySet extends TableSet>> { + @Override + public Iterator>> iterator() { + return Maps.asMapEntryIterator( + columnKeySet(), + new Function>() { + @Override + public Map apply(C columnKey) { + return column(columnKey); + } + }); + } + + @Override + public int size() { + return columnKeySet().size(); + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + if (containsColumn(entry.getKey())) { + // requireNonNull is safe because of the containsColumn check. + return requireNonNull(get(entry.getKey())).equals(entry.getValue()); + } + } + return false; + } + + @Override + public boolean remove(Object obj) { + /* + * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our + * nullness checker. + */ + if (contains(obj) && obj instanceof Entry) { + Entry entry = (Entry) obj; + removeColumn(entry.getKey()); + return true; + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + /* + * We can't inherit the normal implementation (which calls + * Sets.removeAllImpl(Set, *Collection*)) because, under some + * circumstances, it attempts to call columnKeySet().iterator().remove, + * which is unsupported. + */ + checkNotNull(c); + return Sets.removeAllImpl(this, c.iterator()); + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + } + + private class ColumnMapValues extends Maps.Values> { + ColumnMapValues() { + super(ColumnMap.this); + } + + @Override + public boolean remove(Object obj) { + for (Entry> entry : ColumnMap.this.entrySet()) { + if (entry.getValue().equals(obj)) { + removeColumn(entry.getKey()); + return true; + } + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (c.contains(column(columnKey))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (!c.contains(column(columnKey))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + } + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Streams.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Streams.java new file mode 100644 index 0000000000000..2830a89fae6a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Streams.java @@ -0,0 +1,954 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.math.LongMath; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.Spliterators.AbstractSpliterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; +import java.util.stream.BaseStream; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static java.lang.Math.min; +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * Static utility methods related to {@code Stream} instances. + * + * @since 21.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Streams { + /** + * Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link + * Collection#stream} if possible. + */ + public static Stream stream(Iterable iterable) { + return (iterable instanceof Collection) + ? ((Collection) iterable).stream() + : StreamSupport.stream(iterable.spliterator(), false); + } + + /** + * Returns {@link Collection#stream}. + * + * @deprecated There is no reason to use this; just invoke {@code collection.stream()} directly. + */ + @Deprecated + public static Stream stream(Collection collection) { + return collection.stream(); + } + + /** + * Returns a sequential {@link Stream} of the remaining contents of {@code iterator}. Do not use + * {@code iterator} directly after passing it to this method. + */ + public static Stream stream(Iterator iterator) { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + */ + public static Stream stream(org.apache.iotdb.google.common.base.Optional optional) { + return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + public static Stream stream(java.util.Optional optional) { + return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + public static IntStream stream(OptionalInt optional) { + return optional.isPresent() ? IntStream.of(optional.getAsInt()) : IntStream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + public static LongStream stream(OptionalLong optional) { + return optional.isPresent() ? LongStream.of(optional.getAsLong()) : LongStream.empty(); + } + + /** + * If a value is present in {@code optional}, returns a stream containing only that element, + * otherwise returns an empty stream. + * + *

Java 9 users: use {@code optional.stream()} instead. + */ + @Beta + public static DoubleStream stream(OptionalDouble optional) { + return optional.isPresent() ? DoubleStream.of(optional.getAsDouble()) : DoubleStream.empty(); + } + + private static void closeAll(BaseStream[] toClose) { + for (BaseStream stream : toClose) { + // TODO(b/80534298): Catch exceptions, rethrowing later with extras as suppressed exceptions. + stream.close(); + } + } + + /** + * Returns a {@link Stream} containing the elements of the first stream, followed by the elements + * of the second stream, and so on. + * + *

This is equivalent to {@code Stream.of(streams).flatMap(stream -> stream)}, but the returned + * stream may perform better. + * + * @see Stream#concat(Stream, Stream) + */ + @SafeVarargs + public static Stream concat(Stream... streams) { + // TODO(lowasser): consider an implementation that can support SUBSIZED + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder> splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (Stream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.stream( + CollectSpliterators.flatMap( + splitrsBuilder.build().spliterator(), + splitr -> (Spliterator) splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns an {@link IntStream} containing the elements of the first stream, followed by the + * elements of the second stream, and so on. + * + *

This is equivalent to {@code Stream.of(streams).flatMapToInt(stream -> stream)}, but the + * returned stream may perform better. + * + * @see IntStream#concat(IntStream, IntStream) + */ + public static IntStream concat(IntStream... streams) { + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (IntStream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator.OfInt splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.intStream( + CollectSpliterators.flatMapToInt( + splitrsBuilder.build().spliterator(), + splitr -> splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns a {@link LongStream} containing the elements of the first stream, followed by the + * elements of the second stream, and so on. + * + *

This is equivalent to {@code Stream.of(streams).flatMapToLong(stream -> stream)}, but the + * returned stream may perform better. + * + * @see LongStream#concat(LongStream, LongStream) + */ + public static LongStream concat(LongStream... streams) { + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (LongStream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator.OfLong splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.longStream( + CollectSpliterators.flatMapToLong( + splitrsBuilder.build().spliterator(), + splitr -> splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns a {@link DoubleStream} containing the elements of the first stream, followed by the + * elements of the second stream, and so on. + * + *

This is equivalent to {@code Stream.of(streams).flatMapToDouble(stream -> stream)}, but the + * returned stream may perform better. + * + * @see DoubleStream#concat(DoubleStream, DoubleStream) + */ + public static DoubleStream concat(DoubleStream... streams) { + boolean isParallel = false; + int characteristics = Spliterator.ORDERED | Spliterator.SIZED | Spliterator.NONNULL; + long estimatedSize = 0L; + ImmutableList.Builder splitrsBuilder = + new ImmutableList.Builder<>(streams.length); + for (DoubleStream stream : streams) { + isParallel |= stream.isParallel(); + Spliterator.OfDouble splitr = stream.spliterator(); + splitrsBuilder.add(splitr); + characteristics &= splitr.characteristics(); + estimatedSize = LongMath.saturatedAdd(estimatedSize, splitr.estimateSize()); + } + return StreamSupport.doubleStream( + CollectSpliterators.flatMapToDouble( + splitrsBuilder.build().spliterator(), + splitr -> splitr, + characteristics, + estimatedSize), + isParallel) + .onClose(() -> closeAll(streams)); + } + + /** + * Returns a stream in which each element is the result of passing the corresponding element of + * each of {@code streamA} and {@code streamB} to {@code function}. + * + *

For example: + * + *

{@code
+   * Streams.zip(
+   *   Stream.of("foo1", "foo2", "foo3"),
+   *   Stream.of("bar1", "bar2"),
+   *   (arg1, arg2) -> arg1 + ":" + arg2)
+   * }
+ * + *

will return {@code Stream.of("foo1:bar1", "foo2:bar2")}. + * + *

The resulting stream will only be as long as the shorter of the two input streams; if one + * stream is longer, its extra elements will be ignored. + * + *

Note that if you are calling {@link Stream#forEach} on the resulting stream, you might want + * to consider using {@link #forEachPair} instead of this method. + * + *

Performance note: The resulting stream is not efficiently splittable. + * This may harm parallel performance. + */ + @Beta + public static Stream zip( + Stream streamA, Stream streamB, BiFunction function) { + checkNotNull(streamA); + checkNotNull(streamB); + checkNotNull(function); + boolean isParallel = streamA.isParallel() || streamB.isParallel(); // same as Stream.concat + Spliterator splitrA = streamA.spliterator(); + Spliterator splitrB = streamB.spliterator(); + int characteristics = + splitrA.characteristics() + & splitrB.characteristics() + & (Spliterator.SIZED | Spliterator.ORDERED); + Iterator itrA = Spliterators.iterator(splitrA); + Iterator itrB = Spliterators.iterator(splitrB); + return StreamSupport.stream( + new AbstractSpliterator( + min(splitrA.estimateSize(), splitrB.estimateSize()), characteristics) { + @Override + public boolean tryAdvance(Consumer action) { + if (itrA.hasNext() && itrB.hasNext()) { + action.accept(function.apply(itrA.next(), itrB.next())); + return true; + } + return false; + } + }, + isParallel) + .onClose(streamA::close) + .onClose(streamB::close); + } + + /** + * Invokes {@code consumer} once for each pair of corresponding elements in {@code streamA} + * and {@code streamB}. If one stream is longer than the other, the extra elements are silently + * ignored. Elements passed to the consumer are guaranteed to come from the same position in their + * respective source streams. For example: + * + *

{@code
+   * Streams.forEachPair(
+   *   Stream.of("foo1", "foo2", "foo3"),
+   *   Stream.of("bar1", "bar2"),
+   *   (arg1, arg2) -> System.out.println(arg1 + ":" + arg2)
+   * }
+ * + *

will print: + * + *

{@code
+   * foo1:bar1
+   * foo2:bar2
+   * }
+ * + *

Warning: If either supplied stream is a parallel stream, the same correspondence + * between elements will be made, but the order in which those pairs of elements are passed to the + * consumer is not defined. + * + *

Note that many usages of this method can be replaced with simpler calls to {@link #zip}. + * This method behaves equivalently to {@linkplain #zip zipping} the stream elements into + * temporary pair objects and then using {@link Stream#forEach} on that stream. + * + * @since 22.0 + */ + @Beta + public static void forEachPair( + Stream streamA, Stream streamB, BiConsumer consumer) { + checkNotNull(consumer); + + if (streamA.isParallel() || streamB.isParallel()) { + zip(streamA, streamB, TemporaryPair::new).forEach(pair -> consumer.accept(pair.a, pair.b)); + } else { + Iterator iterA = streamA.iterator(); + Iterator iterB = streamB.iterator(); + while (iterA.hasNext() && iterB.hasNext()) { + consumer.accept(iterA.next(), iterB.next()); + } + } + } + + // Use this carefully - it doesn't implement value semantics + private static class TemporaryPair { + @ParametricNullness final A a; + @ParametricNullness final B b; + + TemporaryPair(@ParametricNullness A a, @ParametricNullness B b) { + this.a = a; + this.b = b; + } + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indices in the stream. For example, + * + *

{@code
+   * mapWithIndex(
+   *     Stream.of("a", "b", "c"),
+   *     (e, index) -> index + ":" + e)
+   * }
+ * + *

would return {@code Stream.of("0:a", "1:b", "2:c")}. + * + *

The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + public static Stream mapWithIndex( + Stream stream, FunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + Iterator fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.next(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator, R, Splitr> implements Consumer { + T holder; + + Splitr(Spliterator splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(@ParametricNullness T t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + try { + // The cast is safe because tryAdvance puts a T into `holder`. + action.accept(function.apply(uncheckedCastNullableTToT(holder), index++)); + return true; + } finally { + holder = null; + } + } + return false; + } + + @Override + Splitr createSplit(Spliterator from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indexes in the stream. For example, + * + *

{@code
+   * mapWithIndex(
+   *     IntStream.of(10, 11, 12),
+   *     (e, index) -> index + ":" + e)
+   * }
+ * + *

...would return {@code Stream.of("0:10", "1:11", "2:12")}. + * + *

The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + public static Stream mapWithIndex( + IntStream stream, IntFunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator.OfInt fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + PrimitiveIterator.OfInt fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.nextInt(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator + implements IntConsumer, Spliterator { + int holder; + + Splitr(Spliterator.OfInt splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(int t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + action.accept(function.apply(holder, index++)); + return true; + } + return false; + } + + @Override + Splitr createSplit(Spliterator.OfInt from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indexes in the stream. For example, + * + *

{@code
+   * mapWithIndex(
+   *     LongStream.of(10, 11, 12),
+   *     (e, index) -> index + ":" + e)
+   * }
+ * + *

...would return {@code Stream.of("0:10", "1:11", "2:12")}. + * + *

The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + public static Stream mapWithIndex( + LongStream stream, LongFunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator.OfLong fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + PrimitiveIterator.OfLong fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.nextLong(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator + implements LongConsumer, Spliterator { + long holder; + + Splitr(Spliterator.OfLong splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(long t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + action.accept(function.apply(holder, index++)); + return true; + } + return false; + } + + @Override + Splitr createSplit(Spliterator.OfLong from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * Returns a stream consisting of the results of applying the given function to the elements of + * {@code stream} and their indexes in the stream. For example, + * + *

{@code
+   * mapWithIndex(
+   *     DoubleStream.of(0.0, 1.0, 2.0)
+   *     (e, index) -> index + ":" + e)
+   * }
+ * + *

...would return {@code Stream.of("0:0.0", "1:1.0", "2:2.0")}. + * + *

The resulting stream is efficiently splittable + * if and only if {@code stream} was efficiently splittable and its underlying spliterator + * reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream + * comes from a data structure supporting efficient indexed random access, typically an array or + * list. + * + *

The order of the resulting stream is defined if and only if the order of the original stream + * was defined. + */ + public static Stream mapWithIndex( + DoubleStream stream, DoubleFunctionWithIndex function) { + checkNotNull(stream); + checkNotNull(function); + boolean isParallel = stream.isParallel(); + Spliterator.OfDouble fromSpliterator = stream.spliterator(); + + if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + PrimitiveIterator.OfDouble fromIterator = Spliterators.iterator(fromSpliterator); + return StreamSupport.stream( + new AbstractSpliterator( + fromSpliterator.estimateSize(), + fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) { + long index = 0; + + @Override + public boolean tryAdvance(Consumer action) { + if (fromIterator.hasNext()) { + action.accept(function.apply(fromIterator.nextDouble(), index++)); + return true; + } + return false; + } + }, + isParallel) + .onClose(stream::close); + } + class Splitr extends MapWithIndexSpliterator + implements DoubleConsumer, Spliterator { + double holder; + + Splitr(Spliterator.OfDouble splitr, long index) { + super(splitr, index); + } + + @Override + public void accept(double t) { + this.holder = t; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (fromSpliterator.tryAdvance(this)) { + action.accept(function.apply(holder, index++)); + return true; + } + return false; + } + + @Override + Splitr createSplit(Spliterator.OfDouble from, long i) { + return new Splitr(from, i); + } + } + return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close); + } + + /** + * An analogue of {@link java.util.function.Function} also accepting an index. + * + *

This interface is only intended for use by callers of {@link #mapWithIndex(Stream, + * FunctionWithIndex)}. + * + * @since 21.0 + */ + public interface FunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(@ParametricNullness T from, long index); + } + + private abstract static class MapWithIndexSpliterator< + F extends Spliterator, R extends Object, S extends MapWithIndexSpliterator> + implements Spliterator { + final F fromSpliterator; + long index; + + MapWithIndexSpliterator(F fromSpliterator, long index) { + this.fromSpliterator = fromSpliterator; + this.index = index; + } + + abstract S createSplit(F from, long i); + + @Override + public S trySplit() { + Spliterator splitOrNull = fromSpliterator.trySplit(); + if (splitOrNull == null) { + return null; + } + @SuppressWarnings("unchecked") + F split = (F) splitOrNull; + S result = createSplit(split, index); + this.index += split.getExactSizeIfKnown(); + return result; + } + + @Override + public long estimateSize() { + return fromSpliterator.estimateSize(); + } + + @Override + public int characteristics() { + return fromSpliterator.characteristics() + & (Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED); + } + } + + /** + * An analogue of {@link java.util.function.IntFunction} also accepting an index. + * + *

This interface is only intended for use by callers of {@link #mapWithIndex(IntStream, + * IntFunctionWithIndex)}. + * + * @since 21.0 + */ + public interface IntFunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(int from, long index); + } + + /** + * An analogue of {@link java.util.function.LongFunction} also accepting an index. + * + *

This interface is only intended for use by callers of {@link #mapWithIndex(LongStream, + * LongFunctionWithIndex)}. + * + * @since 21.0 + */ + public interface LongFunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(long from, long index); + } + + /** + * An analogue of {@link java.util.function.DoubleFunction} also accepting an index. + * + *

This interface is only intended for use by callers of {@link #mapWithIndex(DoubleStream, + * DoubleFunctionWithIndex)}. + * + * @since 21.0 + */ + public interface DoubleFunctionWithIndex { + /** Applies this function to the given argument and its index within a stream. */ + @ParametricNullness + R apply(double from, long index); + } + + /** + * Returns the last element of the specified stream, or {@link java.util.Optional#empty} if the + * stream is empty. + * + *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + *

If the stream has nondeterministic order, this has equivalent semantics to {@link + * Stream#findAny} (which you might as well use). + * + * @see Stream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + /* + * By declaring instead of , we declare this method as requiring a + * stream whose elements are non-null. However, the method goes out of its way to still handle + * nulls in the stream. This means that the method can safely be used with a stream that contains + * nulls as long as the *last* element is *not* null. + * + * (To "go out of its way," the method tracks a `set` bit so that it can distinguish "the final + * split has a last element of null, so throw NPE" from "the final split was empty, so look for an + * element in the prior one.") + */ + public static java.util.Optional findLast(Stream stream) { + class OptionalState { + boolean set = false; + T value = null; + + void set(T value) { + this.set = true; + this.value = value; + } + + T get() { + /* + * requireNonNull is safe because we call get() only if we've previously called set(). + * + * (For further discussion of nullness, see the comment above the method.) + */ + return requireNonNull(value); + } + } + OptionalState state = new OptionalState(); + + Deque> splits = new ArrayDeque<>(); + splits.addLast(stream.spliterator()); + + while (!splits.isEmpty()) { + Spliterator spliterator = splits.removeLast(); + + if (spliterator.getExactSizeIfKnown() == 0) { + continue; // drop this split + } + + // Many spliterators will have trySplits that are SUBSIZED even if they are not themselves + // SUBSIZED. + if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) { + // we can drill down to exactly the smallest nonempty spliterator + while (true) { + Spliterator prefix = spliterator.trySplit(); + if (prefix == null || prefix.getExactSizeIfKnown() == 0) { + break; + } else if (spliterator.getExactSizeIfKnown() == 0) { + spliterator = prefix; + break; + } + } + + // spliterator is known to be nonempty now + spliterator.forEachRemaining(state::set); + return java.util.Optional.of(state.get()); + } + + Spliterator prefix = spliterator.trySplit(); + if (prefix == null || prefix.getExactSizeIfKnown() == 0) { + // we can't split this any further + spliterator.forEachRemaining(state::set); + if (state.set) { + return java.util.Optional.of(state.get()); + } + // fall back to the last split + continue; + } + splits.addLast(prefix); + splits.addLast(spliterator); + } + return java.util.Optional.empty(); + } + + /** + * Returns the last element of the specified stream, or {@link OptionalInt#empty} if the stream is + * empty. + * + *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + * @see IntStream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + public static OptionalInt findLast(IntStream stream) { + // findLast(Stream) does some allocation, so we might as well box some more + java.util.Optional boxedLast = findLast(stream.boxed()); + return boxedLast.map(OptionalInt::of).orElseGet(OptionalInt::empty); + } + + /** + * Returns the last element of the specified stream, or {@link OptionalLong#empty} if the stream + * is empty. + * + *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + * @see LongStream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + public static OptionalLong findLast(LongStream stream) { + // findLast(Stream) does some allocation, so we might as well box some more + java.util.Optional boxedLast = findLast(stream.boxed()); + return boxedLast.map(OptionalLong::of).orElseGet(OptionalLong::empty); + } + + /** + * Returns the last element of the specified stream, or {@link OptionalDouble#empty} if the stream + * is empty. + * + *

Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This + * method's runtime will be between O(log n) and O(n), performing better on efficiently splittable + * streams. + * + * @see DoubleStream#findFirst() + * @throws NullPointerException if the last element of the stream is null + */ + public static OptionalDouble findLast(DoubleStream stream) { + // findLast(Stream) does some allocation, so we might as well box some more + java.util.Optional boxedLast = findLast(stream.boxed()); + return boxedLast.map(OptionalDouble::of).orElseGet(OptionalDouble::empty); + } + + private Streams() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Synchronized.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Synchronized.java new file mode 100644 index 0000000000000..b236fa429f73e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Synchronized.java @@ -0,0 +1,2202 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Queue; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Synchronized collection views. The returned synchronized collection views are serializable if the + * backing collection and the mutex are serializable. + * + *

If {@code null} is passed as the {@code mutex} parameter to any of this class's top-level + * methods or inner class constructors, the created object uses itself as the synchronization mutex. + * + *

This class should be used by other collection classes only. + * + * @author Mike Bostock + * @author Jared Levy + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +/* + * I have decided not to bother adding @ParametricNullness annotations in this class. Adding them is + * a lot of busy work, and the annotation matters only when the APIs to be annotated are visible to + * Kotlin code. In this class, nothing is publicly visible (nor exposed indirectly through a + * publicly visible subclass), and I doubt any of our current or future Kotlin extensions for the + * package will refer to the class. Plus, @ParametricNullness is only a temporary workaround, + * anyway, so we just need to get by without the annotations here until Kotlin better understands + * our other nullness annotations. + */ +final class Synchronized { + private Synchronized() {} + + static class SynchronizedObject implements Serializable { + final Object delegate; + final Object mutex; + + SynchronizedObject(Object delegate, Object mutex) { + this.delegate = checkNotNull(delegate); + this.mutex = (mutex == null) ? this : mutex; + } + + Object delegate() { + return delegate; + } + + // No equals and hashCode; see ForwardingObject for details. + + @Override + public String toString() { + synchronized (mutex) { + return delegate.toString(); + } + } + + // Serialization invokes writeObject only when it's private. + // The SynchronizedObject subclasses don't need a writeObject method since + // they don't contain any non-transient member variables, while the + // following writeObject() handles the SynchronizedObject members. + + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + synchronized (mutex) { + stream.defaultWriteObject(); + } + } + + @GwtIncompatible // not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; + } + + private static Collection collection( + Collection collection, Object mutex) { + return new SynchronizedCollection(collection, mutex); + } + + @VisibleForTesting + static class SynchronizedCollection extends SynchronizedObject + implements Collection { + private SynchronizedCollection(Collection delegate, Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") + @Override + Collection delegate() { + return (Collection) super.delegate(); + } + + @Override + public boolean add(E e) { + synchronized (mutex) { + return delegate().add(e); + } + } + + @Override + public boolean addAll(Collection c) { + synchronized (mutex) { + return delegate().addAll(c); + } + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public boolean contains(Object o) { + synchronized (mutex) { + return delegate().contains(o); + } + } + + @Override + public boolean containsAll(Collection c) { + synchronized (mutex) { + return delegate().containsAll(c); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public Iterator iterator() { + return delegate().iterator(); // manually synchronized + } + + @Override + public Spliterator spliterator() { + synchronized (mutex) { + return delegate().spliterator(); + } + } + + @Override + public Stream stream() { + synchronized (mutex) { + return delegate().stream(); + } + } + + @Override + public Stream parallelStream() { + synchronized (mutex) { + return delegate().parallelStream(); + } + } + + @Override + public void forEach(Consumer action) { + synchronized (mutex) { + delegate().forEach(action); + } + } + + @Override + public boolean remove(Object o) { + synchronized (mutex) { + return delegate().remove(o); + } + } + + @Override + public boolean removeAll(Collection c) { + synchronized (mutex) { + return delegate().removeAll(c); + } + } + + @Override + public boolean retainAll(Collection c) { + synchronized (mutex) { + return delegate().retainAll(c); + } + } + + @Override + public boolean removeIf(Predicate filter) { + synchronized (mutex) { + return delegate().removeIf(filter); + } + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public Object[] toArray() { + synchronized (mutex) { + return delegate().toArray(); + } + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] a) { + synchronized (mutex) { + return delegate().toArray(a); + } + } + + private static final long serialVersionUID = 0; + } + + @VisibleForTesting + static Set set(Set set, Object mutex) { + return new SynchronizedSet(set, mutex); + } + + static class SynchronizedSet extends SynchronizedCollection + implements Set { + + SynchronizedSet(Set delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + Set delegate() { + return (Set) super.delegate(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + private static SortedSet sortedSet(SortedSet set, Object mutex) { + return new SynchronizedSortedSet(set, mutex); + } + + static class SynchronizedSortedSet extends SynchronizedSet + implements SortedSet { + SynchronizedSortedSet(SortedSet delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + SortedSet delegate() { + return (SortedSet) super.delegate(); + } + + @Override + public Comparator comparator() { + synchronized (mutex) { + return delegate().comparator(); + } + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + synchronized (mutex) { + return sortedSet(delegate().subSet(fromElement, toElement), mutex); + } + } + + @Override + public SortedSet headSet(E toElement) { + synchronized (mutex) { + return sortedSet(delegate().headSet(toElement), mutex); + } + } + + @Override + public SortedSet tailSet(E fromElement) { + synchronized (mutex) { + return sortedSet(delegate().tailSet(fromElement), mutex); + } + } + + @Override + public E first() { + synchronized (mutex) { + return delegate().first(); + } + } + + @Override + public E last() { + synchronized (mutex) { + return delegate().last(); + } + } + + private static final long serialVersionUID = 0; + } + + private static List list(List list, Object mutex) { + return (list instanceof RandomAccess) + ? new SynchronizedRandomAccessList(list, mutex) + : new SynchronizedList(list, mutex); + } + + private static class SynchronizedList extends SynchronizedCollection + implements List { + SynchronizedList(List delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + List delegate() { + return (List) super.delegate(); + } + + @Override + public void add(int index, E element) { + synchronized (mutex) { + delegate().add(index, element); + } + } + + @Override + public boolean addAll(int index, Collection c) { + synchronized (mutex) { + return delegate().addAll(index, c); + } + } + + @Override + public E get(int index) { + synchronized (mutex) { + return delegate().get(index); + } + } + + @Override + public int indexOf(Object o) { + synchronized (mutex) { + return delegate().indexOf(o); + } + } + + @Override + public int lastIndexOf(Object o) { + synchronized (mutex) { + return delegate().lastIndexOf(o); + } + } + + @Override + public ListIterator listIterator() { + return delegate().listIterator(); // manually synchronized + } + + @Override + public ListIterator listIterator(int index) { + return delegate().listIterator(index); // manually synchronized + } + + @Override + public E remove(int index) { + synchronized (mutex) { + return delegate().remove(index); + } + } + + @Override + public E set(int index, E element) { + synchronized (mutex) { + return delegate().set(index, element); + } + } + + @Override + public void replaceAll(UnaryOperator operator) { + synchronized (mutex) { + delegate().replaceAll(operator); + } + } + + @Override + public void sort(Comparator c) { + synchronized (mutex) { + delegate().sort(c); + } + } + + @Override + public List subList(int fromIndex, int toIndex) { + synchronized (mutex) { + return list(delegate().subList(fromIndex, toIndex), mutex); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + private static class SynchronizedRandomAccessList extends SynchronizedList + implements RandomAccess { + SynchronizedRandomAccessList(List list, Object mutex) { + super(list, mutex); + } + + private static final long serialVersionUID = 0; + } + + static Multiset multiset(Multiset multiset, Object mutex) { + if (multiset instanceof SynchronizedMultiset || multiset instanceof ImmutableMultiset) { + return multiset; + } + return new SynchronizedMultiset(multiset, mutex); + } + + private static class SynchronizedMultiset extends SynchronizedCollection + implements Multiset { + transient Set elementSet; + transient Set> entrySet; + + SynchronizedMultiset(Multiset delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + Multiset delegate() { + return (Multiset) super.delegate(); + } + + @Override + public int count(Object o) { + synchronized (mutex) { + return delegate().count(o); + } + } + + @Override + public int add(@ParametricNullness E e, int n) { + synchronized (mutex) { + return delegate().add(e, n); + } + } + + @Override + public int remove(Object o, int n) { + synchronized (mutex) { + return delegate().remove(o, n); + } + } + + @Override + public int setCount(@ParametricNullness E element, int count) { + synchronized (mutex) { + return delegate().setCount(element, count); + } + } + + @Override + public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { + synchronized (mutex) { + return delegate().setCount(element, oldCount, newCount); + } + } + + @Override + public Set elementSet() { + synchronized (mutex) { + if (elementSet == null) { + elementSet = typePreservingSet(delegate().elementSet(), mutex); + } + return elementSet; + } + } + + @Override + public Set> entrySet() { + synchronized (mutex) { + if (entrySet == null) { + entrySet = typePreservingSet(delegate().entrySet(), mutex); + } + return entrySet; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + static Multimap multimap( + Multimap multimap, Object mutex) { + if (multimap instanceof SynchronizedMultimap || multimap instanceof BaseImmutableMultimap) { + return multimap; + } + return new SynchronizedMultimap<>(multimap, mutex); + } + + private static class SynchronizedMultimap + extends SynchronizedObject implements Multimap { + transient Set keySet; + transient Collection valuesCollection; + transient Collection> entries; + transient Map> asMap; + transient Multiset keys; + + @SuppressWarnings("unchecked") + @Override + Multimap delegate() { + return (Multimap) super.delegate(); + } + + SynchronizedMultimap(Multimap delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public boolean containsKey(Object key) { + synchronized (mutex) { + return delegate().containsKey(key); + } + } + + @Override + public boolean containsValue(Object value) { + synchronized (mutex) { + return delegate().containsValue(value); + } + } + + @Override + public boolean containsEntry(Object key, Object value) { + synchronized (mutex) { + return delegate().containsEntry(key, value); + } + } + + @Override + public Collection get(@ParametricNullness K key) { + synchronized (mutex) { + return typePreservingCollection(delegate().get(key), mutex); + } + } + + @Override + public boolean put(@ParametricNullness K key, @ParametricNullness V value) { + synchronized (mutex) { + return delegate().put(key, value); + } + } + + @Override + public boolean putAll(@ParametricNullness K key, Iterable values) { + synchronized (mutex) { + return delegate().putAll(key, values); + } + } + + @Override + public boolean putAll(Multimap multimap) { + synchronized (mutex) { + return delegate().putAll(multimap); + } + } + + @Override + public Collection replaceValues(@ParametricNullness K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + @Override + public boolean remove(Object key, Object value) { + synchronized (mutex) { + return delegate().remove(key, value); + } + } + + @Override + public Collection removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public Set keySet() { + synchronized (mutex) { + if (keySet == null) { + keySet = typePreservingSet(delegate().keySet(), mutex); + } + return keySet; + } + } + + @Override + public Collection values() { + synchronized (mutex) { + if (valuesCollection == null) { + valuesCollection = collection(delegate().values(), mutex); + } + return valuesCollection; + } + } + + @Override + public Collection> entries() { + synchronized (mutex) { + if (entries == null) { + entries = typePreservingCollection(delegate().entries(), mutex); + } + return entries; + } + } + + @Override + public void forEach(BiConsumer action) { + synchronized (mutex) { + delegate().forEach(action); + } + } + + @Override + public Map> asMap() { + synchronized (mutex) { + if (asMap == null) { + asMap = new SynchronizedAsMap<>(delegate().asMap(), mutex); + } + return asMap; + } + } + + @Override + public Multiset keys() { + synchronized (mutex) { + if (keys == null) { + keys = multiset(delegate().keys(), mutex); + } + return keys; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + static ListMultimap listMultimap( + ListMultimap multimap, Object mutex) { + if (multimap instanceof SynchronizedListMultimap || multimap instanceof BaseImmutableMultimap) { + return multimap; + } + return new SynchronizedListMultimap<>(multimap, mutex); + } + + private static class SynchronizedListMultimap + extends SynchronizedMultimap implements ListMultimap { + SynchronizedListMultimap(ListMultimap delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + ListMultimap delegate() { + return (ListMultimap) super.delegate(); + } + + @Override + public List get(K key) { + synchronized (mutex) { + return list(delegate().get(key), mutex); + } + } + + @Override + public List removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public List replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + private static final long serialVersionUID = 0; + } + + static SetMultimap setMultimap( + SetMultimap multimap, Object mutex) { + if (multimap instanceof SynchronizedSetMultimap || multimap instanceof BaseImmutableMultimap) { + return multimap; + } + return new SynchronizedSetMultimap<>(multimap, mutex); + } + + private static class SynchronizedSetMultimap + extends SynchronizedMultimap implements SetMultimap { + transient Set> entrySet; + + SynchronizedSetMultimap(SetMultimap delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + SetMultimap delegate() { + return (SetMultimap) super.delegate(); + } + + @Override + public Set get(K key) { + synchronized (mutex) { + return set(delegate().get(key), mutex); + } + } + + @Override + public Set removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public Set replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + @Override + public Set> entries() { + synchronized (mutex) { + if (entrySet == null) { + entrySet = set(delegate().entries(), mutex); + } + return entrySet; + } + } + + private static final long serialVersionUID = 0; + } + + static SortedSetMultimap sortedSetMultimap( + SortedSetMultimap multimap, Object mutex) { + if (multimap instanceof SynchronizedSortedSetMultimap) { + return multimap; + } + return new SynchronizedSortedSetMultimap<>(multimap, mutex); + } + + private static class SynchronizedSortedSetMultimap + extends SynchronizedSetMultimap implements SortedSetMultimap { + SynchronizedSortedSetMultimap(SortedSetMultimap delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + SortedSetMultimap delegate() { + return (SortedSetMultimap) super.delegate(); + } + + @Override + public SortedSet get(K key) { + synchronized (mutex) { + return sortedSet(delegate().get(key), mutex); + } + } + + @Override + public SortedSet removeAll(Object key) { + synchronized (mutex) { + return delegate().removeAll(key); // copy not synchronized + } + } + + @Override + public SortedSet replaceValues(K key, Iterable values) { + synchronized (mutex) { + return delegate().replaceValues(key, values); // copy not synchronized + } + } + + @Override + public Comparator valueComparator() { + synchronized (mutex) { + return delegate().valueComparator(); + } + } + + private static final long serialVersionUID = 0; + } + + private static Collection typePreservingCollection( + Collection collection, Object mutex) { + if (collection instanceof SortedSet) { + return sortedSet((SortedSet) collection, mutex); + } + if (collection instanceof Set) { + return set((Set) collection, mutex); + } + if (collection instanceof List) { + return list((List) collection, mutex); + } + return collection(collection, mutex); + } + + private static Set typePreservingSet(Set set, Object mutex) { + if (set instanceof SortedSet) { + return sortedSet((SortedSet) set, mutex); + } else { + return set(set, mutex); + } + } + + private static class SynchronizedAsMapEntries + extends SynchronizedSet>> { + SynchronizedAsMapEntries(Set>> delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + public Iterator>> iterator() { + // Must be manually synchronized. + return new TransformedIterator>, Map.Entry>>( + super.iterator()) { + @Override + Map.Entry> transform(final Map.Entry> entry) { + return new ForwardingMapEntry>() { + @Override + protected Map.Entry> delegate() { + return entry; + } + + @Override + public Collection getValue() { + return typePreservingCollection(entry.getValue(), mutex); + } + }; + } + }; + } + + // See Collections.CheckedMap.CheckedEntrySet for details on attacks. + + @Override + public Object[] toArray() { + synchronized (mutex) { + /* + * toArrayImpl returns `@Nullable Object[]` rather than `Object[]` but only because it can + * be used with collections that may contain null. This collection never contains nulls, so + * we could return `Object[]`. But this class is private and J2KT cannot change return types + * in overrides, so we declare `@Nullable Object[]` as the return type. + */ + return ObjectArrays.toArrayImpl(delegate()); + } + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + synchronized (mutex) { + return ObjectArrays.toArrayImpl(delegate(), array); + } + } + + @Override + public boolean contains(Object o) { + synchronized (mutex) { + return Maps.containsEntryImpl(delegate(), o); + } + } + + @Override + public boolean containsAll(Collection c) { + synchronized (mutex) { + return Collections2.containsAllImpl(delegate(), c); + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return Sets.equalsImpl(delegate(), o); + } + } + + @Override + public boolean remove(Object o) { + synchronized (mutex) { + return Maps.removeEntryImpl(delegate(), o); + } + } + + @Override + public boolean removeAll(Collection c) { + synchronized (mutex) { + return Iterators.removeAll(delegate().iterator(), c); + } + } + + @Override + public boolean retainAll(Collection c) { + synchronized (mutex) { + return Iterators.retainAll(delegate().iterator(), c); + } + } + + private static final long serialVersionUID = 0; + } + + @VisibleForTesting + static Map map(Map map, Object mutex) { + return new SynchronizedMap<>(map, mutex); + } + + private static class SynchronizedMap + extends SynchronizedObject implements Map { + transient Set keySet; + transient Collection values; + transient Set> entrySet; + + SynchronizedMap(Map delegate, Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") + @Override + Map delegate() { + return (Map) super.delegate(); + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public boolean containsKey(Object key) { + synchronized (mutex) { + return delegate().containsKey(key); + } + } + + @Override + public boolean containsValue(Object value) { + synchronized (mutex) { + return delegate().containsValue(value); + } + } + + @Override + public Set> entrySet() { + synchronized (mutex) { + if (entrySet == null) { + entrySet = set(delegate().entrySet(), mutex); + } + return entrySet; + } + } + + @Override + public void forEach(BiConsumer action) { + synchronized (mutex) { + delegate().forEach(action); + } + } + + @Override + public V get(Object key) { + synchronized (mutex) { + return delegate().get(key); + } + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + synchronized (mutex) { + return delegate().getOrDefault(key, defaultValue); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public Set keySet() { + synchronized (mutex) { + if (keySet == null) { + keySet = set(delegate().keySet(), mutex); + } + return keySet; + } + } + + @Override + public V put(K key, V value) { + synchronized (mutex) { + return delegate().put(key, value); + } + } + + @Override + public V putIfAbsent(K key, V value) { + synchronized (mutex) { + return delegate().putIfAbsent(key, value); + } + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + synchronized (mutex) { + return delegate().replace(key, oldValue, newValue); + } + } + + @Override + public V replace(K key, V value) { + synchronized (mutex) { + return delegate().replace(key, value); + } + } + + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + synchronized (mutex) { + return delegate().computeIfAbsent(key, mappingFunction); + } + } + + @Override + public V computeIfPresent( + K key, BiFunction remappingFunction) { + synchronized (mutex) { + return delegate().computeIfPresent(key, remappingFunction); + } + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + synchronized (mutex) { + return delegate().compute(key, remappingFunction); + } + } + + @Override + public V merge( + K key, V value, BiFunction remappingFunction) { + synchronized (mutex) { + return delegate().merge(key, value, remappingFunction); + } + } + + @Override + public void putAll(Map map) { + synchronized (mutex) { + delegate().putAll(map); + } + } + + @Override + public void replaceAll(BiFunction function) { + synchronized (mutex) { + delegate().replaceAll(function); + } + } + + @Override + public V remove(Object key) { + synchronized (mutex) { + return delegate().remove(key); + } + } + + @Override + public boolean remove(Object key, Object value) { + synchronized (mutex) { + return delegate().remove(key, value); + } + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public Collection values() { + synchronized (mutex) { + if (values == null) { + values = collection(delegate().values(), mutex); + } + return values; + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + synchronized (mutex) { + return delegate().equals(o); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + private static final long serialVersionUID = 0; + } + + static SortedMap sortedMap( + SortedMap sortedMap, Object mutex) { + return new SynchronizedSortedMap<>(sortedMap, mutex); + } + + static class SynchronizedSortedMap + extends SynchronizedMap implements SortedMap { + + SynchronizedSortedMap(SortedMap delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + SortedMap delegate() { + return (SortedMap) super.delegate(); + } + + @Override + public Comparator comparator() { + synchronized (mutex) { + return delegate().comparator(); + } + } + + @Override + public K firstKey() { + synchronized (mutex) { + return delegate().firstKey(); + } + } + + @Override + public SortedMap headMap(K toKey) { + synchronized (mutex) { + return sortedMap(delegate().headMap(toKey), mutex); + } + } + + @Override + public K lastKey() { + synchronized (mutex) { + return delegate().lastKey(); + } + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + synchronized (mutex) { + return sortedMap(delegate().subMap(fromKey, toKey), mutex); + } + } + + @Override + public SortedMap tailMap(K fromKey) { + synchronized (mutex) { + return sortedMap(delegate().tailMap(fromKey), mutex); + } + } + + private static final long serialVersionUID = 0; + } + + static BiMap biMap(BiMap bimap, Object mutex) { + if (bimap instanceof SynchronizedBiMap || bimap instanceof ImmutableBiMap) { + return bimap; + } + return new SynchronizedBiMap<>(bimap, mutex, null); + } + + @VisibleForTesting + static class SynchronizedBiMap extends SynchronizedMap + implements BiMap, Serializable { + private transient Set valueSet; + private transient BiMap inverse; + + private SynchronizedBiMap(BiMap delegate, Object mutex, BiMap inverse) { + super(delegate, mutex); + this.inverse = inverse; + } + + @Override + BiMap delegate() { + return (BiMap) super.delegate(); + } + + @Override + public Set values() { + synchronized (mutex) { + if (valueSet == null) { + valueSet = set(delegate().values(), mutex); + } + return valueSet; + } + } + + @Override + public V forcePut(@ParametricNullness K key, @ParametricNullness V value) { + synchronized (mutex) { + return delegate().forcePut(key, value); + } + } + + @Override + public BiMap inverse() { + synchronized (mutex) { + if (inverse == null) { + inverse = new SynchronizedBiMap<>(delegate().inverse(), mutex, this); + } + return inverse; + } + } + + private static final long serialVersionUID = 0; + } + + private static class SynchronizedAsMap + extends SynchronizedMap> { + transient Set>> asMapEntrySet; + transient Collection> asMapValues; + + SynchronizedAsMap(Map> delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + public Collection get(Object key) { + synchronized (mutex) { + Collection collection = super.get(key); + return (collection == null) ? null : typePreservingCollection(collection, mutex); + } + } + + @Override + public Set>> entrySet() { + synchronized (mutex) { + if (asMapEntrySet == null) { + asMapEntrySet = new SynchronizedAsMapEntries<>(delegate().entrySet(), mutex); + } + return asMapEntrySet; + } + } + + @Override + public Collection> values() { + synchronized (mutex) { + if (asMapValues == null) { + asMapValues = new SynchronizedAsMapValues(delegate().values(), mutex); + } + return asMapValues; + } + } + + @Override + public boolean containsValue(Object o) { + // values() and its contains() method are both synchronized. + return values().contains(o); + } + + private static final long serialVersionUID = 0; + } + + private static class SynchronizedAsMapValues + extends SynchronizedCollection> { + SynchronizedAsMapValues(Collection> delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + public Iterator> iterator() { + // Must be manually synchronized. + return new TransformedIterator, Collection>(super.iterator()) { + @Override + Collection transform(Collection from) { + return typePreservingCollection(from, mutex); + } + }; + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible // NavigableSet + @VisibleForTesting + static class SynchronizedNavigableSet extends SynchronizedSortedSet + implements NavigableSet { + SynchronizedNavigableSet(NavigableSet delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + NavigableSet delegate() { + return (NavigableSet) super.delegate(); + } + + @Override + public E ceiling(E e) { + synchronized (mutex) { + return delegate().ceiling(e); + } + } + + @Override + public Iterator descendingIterator() { + return delegate().descendingIterator(); // manually synchronized + } + + transient NavigableSet descendingSet; + + @Override + public NavigableSet descendingSet() { + synchronized (mutex) { + if (descendingSet == null) { + NavigableSet dS = Synchronized.navigableSet(delegate().descendingSet(), mutex); + descendingSet = dS; + return dS; + } + return descendingSet; + } + } + + @Override + public E floor(E e) { + synchronized (mutex) { + return delegate().floor(e); + } + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + synchronized (mutex) { + return Synchronized.navigableSet(delegate().headSet(toElement, inclusive), mutex); + } + } + + @Override + public SortedSet headSet(E toElement) { + return headSet(toElement, false); + } + + @Override + public E higher(E e) { + synchronized (mutex) { + return delegate().higher(e); + } + } + + @Override + public E lower(E e) { + synchronized (mutex) { + return delegate().lower(e); + } + } + + @Override + public E pollFirst() { + synchronized (mutex) { + return delegate().pollFirst(); + } + } + + @Override + public E pollLast() { + synchronized (mutex) { + return delegate().pollLast(); + } + } + + @Override + public NavigableSet subSet( + E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized (mutex) { + return Synchronized.navigableSet( + delegate().subSet(fromElement, fromInclusive, toElement, toInclusive), mutex); + } + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet tailSet(E fromElement, boolean inclusive) { + synchronized (mutex) { + return Synchronized.navigableSet(delegate().tailSet(fromElement, inclusive), mutex); + } + } + + @Override + public SortedSet tailSet(E fromElement) { + return tailSet(fromElement, true); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible // NavigableSet + static NavigableSet navigableSet( + NavigableSet navigableSet, Object mutex) { + return new SynchronizedNavigableSet(navigableSet, mutex); + } + + @GwtIncompatible // NavigableSet + static NavigableSet navigableSet(NavigableSet navigableSet) { + return navigableSet(navigableSet, null); + } + + @GwtIncompatible // NavigableMap + static NavigableMap navigableMap( + NavigableMap navigableMap) { + return navigableMap(navigableMap, null); + } + + @GwtIncompatible // NavigableMap + static NavigableMap navigableMap( + NavigableMap navigableMap, Object mutex) { + return new SynchronizedNavigableMap<>(navigableMap, mutex); + } + + @GwtIncompatible // NavigableMap + @VisibleForTesting + static class SynchronizedNavigableMap + extends SynchronizedSortedMap implements NavigableMap { + + SynchronizedNavigableMap(NavigableMap delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + NavigableMap delegate() { + return (NavigableMap) super.delegate(); + } + + @Override + public Map.Entry ceilingEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().ceilingEntry(key), mutex); + } + } + + @Override + public K ceilingKey(K key) { + synchronized (mutex) { + return delegate().ceilingKey(key); + } + } + + transient NavigableSet descendingKeySet; + + @Override + public NavigableSet descendingKeySet() { + synchronized (mutex) { + if (descendingKeySet == null) { + return descendingKeySet = Synchronized.navigableSet(delegate().descendingKeySet(), mutex); + } + return descendingKeySet; + } + } + + transient NavigableMap descendingMap; + + @Override + public NavigableMap descendingMap() { + synchronized (mutex) { + if (descendingMap == null) { + return descendingMap = navigableMap(delegate().descendingMap(), mutex); + } + return descendingMap; + } + } + + @Override + public Map.Entry firstEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().firstEntry(), mutex); + } + } + + @Override + public Map.Entry floorEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().floorEntry(key), mutex); + } + } + + @Override + public K floorKey(K key) { + synchronized (mutex) { + return delegate().floorKey(key); + } + } + + @Override + public NavigableMap headMap(K toKey, boolean inclusive) { + synchronized (mutex) { + return navigableMap(delegate().headMap(toKey, inclusive), mutex); + } + } + + @Override + public SortedMap headMap(K toKey) { + return headMap(toKey, false); + } + + @Override + public Map.Entry higherEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().higherEntry(key), mutex); + } + } + + @Override + public K higherKey(K key) { + synchronized (mutex) { + return delegate().higherKey(key); + } + } + + @Override + public Map.Entry lastEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().lastEntry(), mutex); + } + } + + @Override + public Map.Entry lowerEntry(K key) { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().lowerEntry(key), mutex); + } + } + + @Override + public K lowerKey(K key) { + synchronized (mutex) { + return delegate().lowerKey(key); + } + } + + @Override + public Set keySet() { + return navigableKeySet(); + } + + transient NavigableSet navigableKeySet; + + @Override + public NavigableSet navigableKeySet() { + synchronized (mutex) { + if (navigableKeySet == null) { + return navigableKeySet = Synchronized.navigableSet(delegate().navigableKeySet(), mutex); + } + return navigableKeySet; + } + } + + @Override + public Map.Entry pollFirstEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().pollFirstEntry(), mutex); + } + } + + @Override + public Map.Entry pollLastEntry() { + synchronized (mutex) { + return nullableSynchronizedEntry(delegate().pollLastEntry(), mutex); + } + } + + @Override + public NavigableMap subMap( + K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + synchronized (mutex) { + return navigableMap(delegate().subMap(fromKey, fromInclusive, toKey, toInclusive), mutex); + } + } + + @Override + public SortedMap subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap tailMap(K fromKey, boolean inclusive) { + synchronized (mutex) { + return navigableMap(delegate().tailMap(fromKey, inclusive), mutex); + } + } + + @Override + public SortedMap tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + private static final long serialVersionUID = 0; + } + + @GwtIncompatible // works but is needed only for NavigableMap + private static Map.Entry nullableSynchronizedEntry( + Map.Entry entry, Object mutex) { + if (entry == null) { + return null; + } + return new SynchronizedEntry<>(entry, mutex); + } + + @GwtIncompatible // works but is needed only for NavigableMap + private static class SynchronizedEntry + extends SynchronizedObject implements Map.Entry { + + SynchronizedEntry(Map.Entry delegate, Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") // guaranteed by the constructor + @Override + Map.Entry delegate() { + return (Map.Entry) super.delegate(); + } + + @Override + public boolean equals(Object obj) { + synchronized (mutex) { + return delegate().equals(obj); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + @Override + public K getKey() { + synchronized (mutex) { + return delegate().getKey(); + } + } + + @Override + public V getValue() { + synchronized (mutex) { + return delegate().getValue(); + } + } + + @Override + public V setValue(V value) { + synchronized (mutex) { + return delegate().setValue(value); + } + } + + private static final long serialVersionUID = 0; + } + + static Queue queue(Queue queue, Object mutex) { + return (queue instanceof SynchronizedQueue) ? queue : new SynchronizedQueue(queue, mutex); + } + + private static class SynchronizedQueue extends SynchronizedCollection + implements Queue { + + SynchronizedQueue(Queue delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + Queue delegate() { + return (Queue) super.delegate(); + } + + @Override + public E element() { + synchronized (mutex) { + return delegate().element(); + } + } + + @Override + public boolean offer(E e) { + synchronized (mutex) { + return delegate().offer(e); + } + } + + @Override + public E peek() { + synchronized (mutex) { + return delegate().peek(); + } + } + + @Override + public E poll() { + synchronized (mutex) { + return delegate().poll(); + } + } + + @Override + public E remove() { + synchronized (mutex) { + return delegate().remove(); + } + } + + private static final long serialVersionUID = 0; + } + + static Deque deque(Deque deque, Object mutex) { + return new SynchronizedDeque(deque, mutex); + } + + private static final class SynchronizedDeque extends SynchronizedQueue + implements Deque { + + SynchronizedDeque(Deque delegate, Object mutex) { + super(delegate, mutex); + } + + @Override + Deque delegate() { + return (Deque) super.delegate(); + } + + @Override + public void addFirst(E e) { + synchronized (mutex) { + delegate().addFirst(e); + } + } + + @Override + public void addLast(E e) { + synchronized (mutex) { + delegate().addLast(e); + } + } + + @Override + public boolean offerFirst(E e) { + synchronized (mutex) { + return delegate().offerFirst(e); + } + } + + @Override + public boolean offerLast(E e) { + synchronized (mutex) { + return delegate().offerLast(e); + } + } + + @Override + public E removeFirst() { + synchronized (mutex) { + return delegate().removeFirst(); + } + } + + @Override + public E removeLast() { + synchronized (mutex) { + return delegate().removeLast(); + } + } + + @Override + public E pollFirst() { + synchronized (mutex) { + return delegate().pollFirst(); + } + } + + @Override + public E pollLast() { + synchronized (mutex) { + return delegate().pollLast(); + } + } + + @Override + public E getFirst() { + synchronized (mutex) { + return delegate().getFirst(); + } + } + + @Override + public E getLast() { + synchronized (mutex) { + return delegate().getLast(); + } + } + + @Override + public E peekFirst() { + synchronized (mutex) { + return delegate().peekFirst(); + } + } + + @Override + public E peekLast() { + synchronized (mutex) { + return delegate().peekLast(); + } + } + + @Override + public boolean removeFirstOccurrence(Object o) { + synchronized (mutex) { + return delegate().removeFirstOccurrence(o); + } + } + + @Override + public boolean removeLastOccurrence(Object o) { + synchronized (mutex) { + return delegate().removeLastOccurrence(o); + } + } + + @Override + public void push(E e) { + synchronized (mutex) { + delegate().push(e); + } + } + + @Override + public E pop() { + synchronized (mutex) { + return delegate().pop(); + } + } + + @Override + public Iterator descendingIterator() { + synchronized (mutex) { + return delegate().descendingIterator(); + } + } + + private static final long serialVersionUID = 0; + } + + static Table table( + Table table, Object mutex) { + return new SynchronizedTable<>(table, mutex); + } + + private static final class SynchronizedTable + extends SynchronizedObject implements Table { + + SynchronizedTable(Table delegate, Object mutex) { + super(delegate, mutex); + } + + @SuppressWarnings("unchecked") + @Override + Table delegate() { + return (Table) super.delegate(); + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + synchronized (mutex) { + return delegate().contains(rowKey, columnKey); + } + } + + @Override + public boolean containsRow(Object rowKey) { + synchronized (mutex) { + return delegate().containsRow(rowKey); + } + } + + @Override + public boolean containsColumn(Object columnKey) { + synchronized (mutex) { + return delegate().containsColumn(columnKey); + } + } + + @Override + public boolean containsValue(Object value) { + synchronized (mutex) { + return delegate().containsValue(value); + } + } + + @Override + public V get(Object rowKey, Object columnKey) { + synchronized (mutex) { + return delegate().get(rowKey, columnKey); + } + } + + @Override + public boolean isEmpty() { + synchronized (mutex) { + return delegate().isEmpty(); + } + } + + @Override + public int size() { + synchronized (mutex) { + return delegate().size(); + } + } + + @Override + public void clear() { + synchronized (mutex) { + delegate().clear(); + } + } + + @Override + public V put( + @ParametricNullness R rowKey, + @ParametricNullness C columnKey, + @ParametricNullness V value) { + synchronized (mutex) { + return delegate().put(rowKey, columnKey, value); + } + } + + @Override + public void putAll(Table table) { + synchronized (mutex) { + delegate().putAll(table); + } + } + + @Override + public V remove(Object rowKey, Object columnKey) { + synchronized (mutex) { + return delegate().remove(rowKey, columnKey); + } + } + + @Override + public Map row(@ParametricNullness R rowKey) { + synchronized (mutex) { + return map(delegate().row(rowKey), mutex); + } + } + + @Override + public Map column(@ParametricNullness C columnKey) { + synchronized (mutex) { + return map(delegate().column(columnKey), mutex); + } + } + + @Override + public Set> cellSet() { + synchronized (mutex) { + return set(delegate().cellSet(), mutex); + } + } + + @Override + public Set rowKeySet() { + synchronized (mutex) { + return set(delegate().rowKeySet(), mutex); + } + } + + @Override + public Set columnKeySet() { + synchronized (mutex) { + return set(delegate().columnKeySet(), mutex); + } + } + + @Override + public Collection values() { + synchronized (mutex) { + return collection(delegate().values(), mutex); + } + } + + @Override + public Map> rowMap() { + synchronized (mutex) { + return map( + Maps.transformValues( + delegate().rowMap(), + new org.apache.iotdb.google.common.base.Function, Map>() { + @Override + public Map apply(Map t) { + return map(t, mutex); + } + }), + mutex); + } + } + + @Override + public Map> columnMap() { + synchronized (mutex) { + return map( + Maps.transformValues( + delegate().columnMap(), + new org.apache.iotdb.google.common.base.Function, Map>() { + @Override + public Map apply(Map t) { + return map(t, mutex); + } + }), + mutex); + } + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate().hashCode(); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + synchronized (mutex) { + return delegate().equals(obj); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Table.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Table.java new file mode 100644 index 0000000000000..a837ca35e9d1f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Table.java @@ -0,0 +1,284 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Objects; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A collection that associates an ordered pair of keys, called a row key and a column key, with a + * single value. A table may be sparse, with only a small fraction of row key / column key pairs + * possessing a corresponding value. + * + *

The mappings corresponding to a given row key may be viewed as a {@link Map} whose keys are + * the columns. The reverse is also available, associating a column with a row key / value map. Note + * that, in some implementations, data access by column key may have fewer supported operations or + * worse performance than data access by row key. + * + *

The methods returning collections or maps always return views of the underlying table. + * Updating the table can change the contents of those collections, and updating the collections + * will change the table. + * + *

All methods that modify the table are optional, and the views returned by the table may or may + * not be modifiable. When modification isn't supported, those methods will throw an {@link + * UnsupportedOperationException}. + * + *

Implementations

+ * + *
    + *
  • {@link ImmutableTable} + *
  • {@link HashBasedTable} + *
  • {@link TreeBasedTable} + *
  • {@link ArrayTable} + *
  • {@link Tables#newCustomTable Tables.newCustomTable} + *
+ * + *

See the Guava User Guide article on {@code Table}. + * + * @author Jared Levy + * @param the type of the table row keys + * @param the type of the table column keys + * @param the type of the mapped values + * @since 7.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface Table { + // TODO(jlevy): Consider adding methods similar to ConcurrentMap methods. + + // Accessors + + /** + * Returns {@code true} if the table contains a mapping with the specified row and column keys. + * + * @param rowKey key of row to search for + * @param columnKey key of column to search for + */ + boolean contains(Object rowKey, Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified row key. + * + * @param rowKey key of row to search for + */ + boolean containsRow(Object rowKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified column. + * + * @param columnKey key of column to search for + */ + boolean containsColumn(Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified value. + * + * @param value value to search for + */ + boolean containsValue(Object value); + + /** + * Returns the value corresponding to the given row and column keys, or {@code null} if no such + * mapping exists. + * + * @param rowKey key of row to search for + * @param columnKey key of column to search for + */ + V get(Object rowKey, Object columnKey); + + /** Returns {@code true} if the table contains no mappings. */ + boolean isEmpty(); + + /** Returns the number of row key / column key / value mappings in the table. */ + int size(); + + /** + * Compares the specified object with this table for equality. Two tables are equal when their + * cell views, as returned by {@link #cellSet}, are equal. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code for this table. The hash code of a table is defined as the hash code of + * its cell view, as returned by {@link #cellSet}. + */ + @Override + int hashCode(); + + // Mutators + + /** Removes all mappings from the table. */ + void clear(); + + /** + * Associates the specified value with the specified keys. If the table already contained a + * mapping for those keys, the old value is replaced with the specified value. + * + * @param rowKey row key that the value should be associated with + * @param columnKey column key that the value should be associated with + * @param value value to be associated with the specified keys + * @return the value previously associated with the keys, or {@code null} if no mapping existed + * for the keys + */ + V put(@ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value); + + /** + * Copies all mappings from the specified table to this table. The effect is equivalent to calling + * {@link #put} with each row key / column key / value mapping in {@code table}. + * + * @param table the table to add to this table + */ + void putAll(Table table); + + /** + * Removes the mapping, if any, associated with the given keys. + * + * @param rowKey row key of mapping to be removed + * @param columnKey column key of mapping to be removed + * @return the value previously associated with the keys, or {@code null} if no such value existed + */ + V remove(Object rowKey, Object columnKey); + + // Views + + /** + * Returns a view of all mappings that have the given row key. For each row key / column key / + * value mapping in the table with that row key, the returned map associates the column key with + * the value. If no mappings in the table have the provided row key, an empty map is returned. + * + *

Changes to the returned map will update the underlying table, and vice versa. + * + * @param rowKey key of row to search for in the table + * @return the corresponding map from column keys to values + */ + Map row(@ParametricNullness R rowKey); + + /** + * Returns a view of all mappings that have the given column key. For each row key / column key / + * value mapping in the table with that column key, the returned map associates the row key with + * the value. If no mappings in the table have the provided column key, an empty map is returned. + * + *

Changes to the returned map will update the underlying table, and vice versa. + * + * @param columnKey key of column to search for in the table + * @return the corresponding map from row keys to values + */ + Map column(@ParametricNullness C columnKey); + + /** + * Returns a set of all row key / column key / value triplets. Changes to the returned set will + * update the underlying table, and vice versa. The cell set does not support the {@code add} or + * {@code addAll} methods. + * + * @return set of table cells consisting of row key / column key / value triplets + */ + Set> cellSet(); + + /** + * Returns a set of row keys that have one or more values in the table. Changes to the set will + * update the underlying table, and vice versa. + * + * @return set of row keys + */ + Set rowKeySet(); + + /** + * Returns a set of column keys that have one or more values in the table. Changes to the set will + * update the underlying table, and vice versa. + * + * @return set of column keys + */ + Set columnKeySet(); + + /** + * Returns a collection of all values, which may contain duplicates. Changes to the returned + * collection will update the underlying table, and vice versa. + * + * @return collection of values + */ + Collection values(); + + /** + * Returns a view that associates each row key with the corresponding map from column keys to + * values. Changes to the returned map will update this table. The returned map does not support + * {@code put()} or {@code putAll()}, or {@code setValue()} on its entries. + * + *

In contrast, the maps returned by {@code rowMap().get()} have the same behavior as those + * returned by {@link #row}. Those maps may support {@code setValue()}, {@code put()}, and {@code + * putAll()}. + * + * @return a map view from each row key to a secondary map from column keys to values + */ + Map> rowMap(); + + /** + * Returns a view that associates each column key with the corresponding map from row keys to + * values. Changes to the returned map will update this table. The returned map does not support + * {@code put()} or {@code putAll()}, or {@code setValue()} on its entries. + * + *

In contrast, the maps returned by {@code columnMap().get()} have the same behavior as those + * returned by {@link #column}. Those maps may support {@code setValue()}, {@code put()}, and + * {@code putAll()}. + * + * @return a map view from each column key to a secondary map from row keys to values + */ + Map> columnMap(); + + /** + * Row key / column key / value triplet corresponding to a mapping in a table. + * + * @since 7.0 + */ + interface Cell { + /** Returns the row key of this cell. */ + @ParametricNullness + R getRowKey(); + + /** Returns the column key of this cell. */ + @ParametricNullness + C getColumnKey(); + + /** Returns the value of this cell. */ + @ParametricNullness + V getValue(); + + /** + * Compares the specified object with this cell for equality. Two cells are equal when they have + * equal row keys, column keys, and values. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code of this cell. + * + *

The hash code of a table cell is equal to {@link Objects#hashCode}{@code (e.getRowKey(), + * e.getColumnKey(), e.getValue())}. + */ + @Override + int hashCode(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TableCollectors.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TableCollectors.java new file mode 100644 index 0000000000000..d945a4ff081ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TableCollectors.java @@ -0,0 +1,220 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.Tables.AbstractCell; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** Collectors utilities for {@code common.collect.Table} internals. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class TableCollectors { + + static Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction) { + checkNotNull(rowFunction, "rowFunction"); + checkNotNull(columnFunction, "columnFunction"); + checkNotNull(valueFunction, "valueFunction"); + return Collector.of( + (Supplier>) ImmutableTable.Builder::new, + (builder, t) -> + builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)), + ImmutableTable.Builder::combine, + ImmutableTable.Builder::build); + } + + static Collector> toImmutableTable( + Function rowFunction, + Function columnFunction, + Function valueFunction, + BinaryOperator mergeFunction) { + + checkNotNull(rowFunction, "rowFunction"); + checkNotNull(columnFunction, "columnFunction"); + checkNotNull(valueFunction, "valueFunction"); + checkNotNull(mergeFunction, "mergeFunction"); + + /* + * No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but + * the Builder can't efficiently support merging of duplicate values. Getting around this + * requires some work. + */ + + return Collector.of( + ImmutableTableCollectorState::new, + (state, input) -> + state.put( + rowFunction.apply(input), + columnFunction.apply(input), + valueFunction.apply(input), + mergeFunction), + (s1, s2) -> s1.combine(s2, mergeFunction), + state -> state.toTable()); + } + + static < + T extends Object, + R extends Object, + C extends Object, + V extends Object, + I extends Table> + Collector toTable( + java.util.function.Function rowFunction, + java.util.function.Function columnFunction, + java.util.function.Function valueFunction, + java.util.function.Supplier tableSupplier) { + return toTable( + rowFunction, + columnFunction, + valueFunction, + (v1, v2) -> { + throw new IllegalStateException("Conflicting values " + v1 + " and " + v2); + }, + tableSupplier); + } + + static < + T extends Object, + R extends Object, + C extends Object, + V extends Object, + I extends Table> + Collector toTable( + java.util.function.Function rowFunction, + java.util.function.Function columnFunction, + java.util.function.Function valueFunction, + BinaryOperator mergeFunction, + java.util.function.Supplier tableSupplier) { + checkNotNull(rowFunction); + checkNotNull(columnFunction); + checkNotNull(valueFunction); + checkNotNull(mergeFunction); + checkNotNull(tableSupplier); + return Collector.of( + tableSupplier, + (table, input) -> + mergeTables( + table, + rowFunction.apply(input), + columnFunction.apply(input), + valueFunction.apply(input), + mergeFunction), + (table1, table2) -> { + for (Table.Cell cell2 : table2.cellSet()) { + mergeTables( + table1, cell2.getRowKey(), cell2.getColumnKey(), cell2.getValue(), mergeFunction); + } + return table1; + }); + } + + private static final class ImmutableTableCollectorState { + final List> insertionOrder = new ArrayList<>(); + final Table> table = HashBasedTable.create(); + + void put(R row, C column, V value, BinaryOperator merger) { + MutableCell oldCell = table.get(row, column); + if (oldCell == null) { + MutableCell cell = new MutableCell<>(row, column, value); + insertionOrder.add(cell); + table.put(row, column, cell); + } else { + oldCell.merge(value, merger); + } + } + + ImmutableTableCollectorState combine( + ImmutableTableCollectorState other, BinaryOperator merger) { + for (MutableCell cell : other.insertionOrder) { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger); + } + return this; + } + + ImmutableTable toTable() { + return ImmutableTable.copyOf(insertionOrder); + } + } + + private static final class MutableCell extends AbstractCell { + private final R row; + private final C column; + private V value; + + MutableCell(R row, C column, V value) { + this.row = checkNotNull(row, "row"); + this.column = checkNotNull(column, "column"); + this.value = checkNotNull(value, "value"); + } + + @Override + public R getRowKey() { + return row; + } + + @Override + public C getColumnKey() { + return column; + } + + @Override + public V getValue() { + return value; + } + + void merge(V value, BinaryOperator mergeFunction) { + checkNotNull(value, "value"); + this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply"); + } + } + + private static void mergeTables( + Table table, + @ParametricNullness R row, + @ParametricNullness C column, + @ParametricNullness V value, + BinaryOperator mergeFunction) { + checkNotNull(value); + V oldValue = table.get(row, column); + if (oldValue == null) { + table.put(row, column, value); + } else { + V newValue = mergeFunction.apply(oldValue, value); + if (newValue == null) { + table.remove(row, column); + } else { + table.put(row, column, newValue); + } + } + } + + private TableCollectors() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Tables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Tables.java new file mode 100644 index 0000000000000..b4e55dad14e34 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/Tables.java @@ -0,0 +1,741 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.collect.Table.Cell; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Spliterator; +import java.util.function.BinaryOperator; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * Provides static methods that involve a {@code Table}. + * + *

See the Guava User Guide article on {@code Tables}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 7.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Tables { + private Tables() {} + + /** + * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the + * specified supplier, whose cells are generated by applying the provided mapping functions to the + * input elements. Cells are inserted into the generated {@code Table} in encounter order. + * + *

If multiple input elements map to the same row and column, an {@code IllegalStateException} + * is thrown when the collection operation is performed. + * + *

To collect to an {@link ImmutableTable}, use {@link ImmutableTable#toImmutableTable}. + * + * @since 21.0 + */ + public static < + T extends Object, + R extends Object, + C extends Object, + V extends Object, + I extends Table> + Collector toTable( + java.util.function.Function rowFunction, + java.util.function.Function columnFunction, + java.util.function.Function valueFunction, + java.util.function.Supplier tableSupplier) { + return TableCollectors.toTable(rowFunction, columnFunction, valueFunction, tableSupplier); + } + + /** + * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the + * specified supplier, whose cells are generated by applying the provided mapping functions to the + * input elements. Cells are inserted into the generated {@code Table} in encounter order. + * + *

If multiple input elements map to the same row and column, the specified merging function is + * used to combine the values. Like {@link + * java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function, + * BinaryOperator, java.util.function.Supplier)}, this Collector throws a {@code + * NullPointerException} on null values returned from {@code valueFunction}, and treats nulls + * returned from {@code mergeFunction} as removals of that row/column pair. + * + * @since 21.0 + */ + public static < + T extends Object, + R extends Object, + C extends Object, + V extends Object, + I extends Table> + Collector toTable( + java.util.function.Function rowFunction, + java.util.function.Function columnFunction, + java.util.function.Function valueFunction, + BinaryOperator mergeFunction, + java.util.function.Supplier tableSupplier) { + return TableCollectors.toTable( + rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier); + } + + /** + * Returns an immutable cell with the specified row key, column key, and value. + * + *

The returned cell is serializable. + * + * @param rowKey the row key to be associated with the returned cell + * @param columnKey the column key to be associated with the returned cell + * @param value the value to be associated with the returned cell + */ + public static Cell immutableCell( + @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { + return new ImmutableCell<>(rowKey, columnKey, value); + } + + static final class ImmutableCell + extends AbstractCell implements Serializable { + @ParametricNullness private final R rowKey; + @ParametricNullness private final C columnKey; + @ParametricNullness private final V value; + + ImmutableCell( + @ParametricNullness R rowKey, + @ParametricNullness C columnKey, + @ParametricNullness V value) { + this.rowKey = rowKey; + this.columnKey = columnKey; + this.value = value; + } + + @Override + @ParametricNullness + public R getRowKey() { + return rowKey; + } + + @Override + @ParametricNullness + public C getColumnKey() { + return columnKey; + } + + @Override + @ParametricNullness + public V getValue() { + return value; + } + + private static final long serialVersionUID = 0; + } + + abstract static class AbstractCell + implements Cell { + // needed for serialization + AbstractCell() {} + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Cell) { + Cell other = (Cell) obj; + return Objects.equal(getRowKey(), other.getRowKey()) + && Objects.equal(getColumnKey(), other.getColumnKey()) + && Objects.equal(getValue(), other.getValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); + } + + @Override + public String toString() { + return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); + } + } + + /** + * Creates a transposed view of a given table that flips its row and column keys. In other words, + * calling {@code get(columnKey, rowKey)} on the generated table always returns the same value as + * calling {@code get(rowKey, columnKey)} on the original table. Updating the original table + * changes the contents of the transposed table and vice versa. + * + *

The returned table supports update operations as long as the input table supports the + * analogous operation with swapped rows and columns. For example, in a {@link HashBasedTable} + * instance, {@code rowKeySet().iterator()} supports {@code remove()} but {@code + * columnKeySet().iterator()} doesn't. With a transposed {@link HashBasedTable}, it's the other + * way around. + */ + public static Table transpose( + Table table) { + return (table instanceof TransposeTable) + ? ((TransposeTable) table).original + : new TransposeTable(table); + } + + private static class TransposeTable + extends AbstractTable { + final Table original; + + TransposeTable(Table original) { + this.original = checkNotNull(original); + } + + @Override + public void clear() { + original.clear(); + } + + @Override + public Map column(@ParametricNullness R columnKey) { + return original.row(columnKey); + } + + @Override + public Set columnKeySet() { + return original.rowKeySet(); + } + + @Override + public Map> columnMap() { + return original.rowMap(); + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return original.contains(columnKey, rowKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return original.containsRow(columnKey); + } + + @Override + public boolean containsRow(Object rowKey) { + return original.containsColumn(rowKey); + } + + @Override + public boolean containsValue(Object value) { + return original.containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return original.get(columnKey, rowKey); + } + + @Override + public V put( + @ParametricNullness C rowKey, + @ParametricNullness R columnKey, + @ParametricNullness V value) { + return original.put(columnKey, rowKey, value); + } + + @Override + public void putAll(Table table) { + original.putAll(transpose(table)); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + return original.remove(columnKey, rowKey); + } + + @Override + public Map row(@ParametricNullness C rowKey) { + return original.column(rowKey); + } + + @Override + public Set rowKeySet() { + return original.columnKeySet(); + } + + @Override + public Map> rowMap() { + return original.columnMap(); + } + + @Override + public int size() { + return original.size(); + } + + @Override + public Collection values() { + return original.values(); + } + + // Will cast TRANSPOSE_CELL to a type that always succeeds + private static final Function TRANSPOSE_CELL = + new Function, Cell>() { + @Override + public Cell apply(Cell cell) { + return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); + } + }; + + @SuppressWarnings("unchecked") + @Override + Iterator> cellIterator() { + return Iterators.transform( + original.cellSet().iterator(), (Function, Cell>) TRANSPOSE_CELL); + } + + @SuppressWarnings("unchecked") + @Override + Spliterator> cellSpliterator() { + return CollectSpliterators.map( + original.cellSet().spliterator(), + (Function, Cell>) TRANSPOSE_CELL); + } + } + + /** + * Creates a table that uses the specified backing map and factory. It can generate a table based + * on arbitrary {@link Map} classes. + * + *

The {@code factory}-generated and {@code backingMap} classes determine the table iteration + * order. However, the table's {@code row()} method returns instances of a different class than + * {@code factory.get()} does. + * + *

Call this method only when the simpler factory methods in classes like {@link + * HashBasedTable} and {@link TreeBasedTable} won't suffice. + * + *

The views returned by the {@code Table} methods {@link Table#column}, {@link + * Table#columnKeySet}, and {@link Table#columnMap} have iterators that don't support {@code + * remove()}. Otherwise, all optional operations are supported. Null row keys, columns keys, and + * values are not supported. + * + *

Lookups by row key are often faster than lookups by column key, because the data is stored + * in a {@code Map>}. A method call like {@code column(columnKey).get(rowKey)} still + * runs quickly, since the row key is provided. However, {@code column(columnKey).size()} takes + * longer, since an iteration across all row keys occurs. + * + *

Note that this implementation is not synchronized. If multiple threads access this table + * concurrently and one of the threads modifies the table, it must be synchronized externally. + * + *

The table is serializable if {@code backingMap}, {@code factory}, the maps generated by + * {@code factory}, and the table contents are all serializable. + * + *

Note: the table assumes complete ownership over of {@code backingMap} and the maps returned + * by {@code factory}. Those objects should not be manually updated and they should not use soft, + * weak, or phantom references. + * + * @param backingMap place to store the mapping from each row key to its corresponding column key + * / value map + * @param factory supplier of new, empty maps that will each hold all column key / value mappings + * for a given row key + * @throws IllegalArgumentException if {@code backingMap} is not empty + * @since 10.0 + */ + public static Table newCustomTable( + Map> backingMap, Supplier> factory) { + checkArgument(backingMap.isEmpty()); + checkNotNull(factory); + // TODO(jlevy): Wrap factory to validate that the supplied maps are empty? + return new StandardTable<>(backingMap, factory); + } + + /** + * Returns a view of a table where each value is transformed by a function. All other properties + * of the table, such as iteration order, are left intact. + * + *

Changes in the underlying table are reflected in this view. Conversely, this view supports + * removal operations, and these are reflected in the underlying table. + * + *

It's acceptable for the underlying table to contain null keys, and even null values provided + * that the function is capable of accepting null input. The transformed table might contain null + * values, if the function sometimes gives a null result. + * + *

The returned table is not thread-safe or serializable, even if the underlying table is. + * + *

The function is applied lazily, invoked when needed. This is necessary for the returned + * table to be a view, but it means that the function will be applied many times for bulk + * operations like {@link Table#containsValue} and {@code Table.toString()}. For this to perform + * well, {@code function} should be fast. To avoid lazy evaluation when the returned table doesn't + * need to be a view, copy the returned table into a new table of your choosing. + * + * @since 10.0 + */ + public static + Table transformValues( + Table fromTable, Function function) { + return new TransformedTable<>(fromTable, function); + } + + private static class TransformedTable< + R extends Object, C extends Object, V1 extends Object, V2 extends Object> + extends AbstractTable { + final Table fromTable; + final Function function; + + TransformedTable(Table fromTable, Function function) { + this.fromTable = checkNotNull(fromTable); + this.function = checkNotNull(function); + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return fromTable.contains(rowKey, columnKey); + } + + @Override + public V2 get(Object rowKey, Object columnKey) { + // The function is passed a null input only when the table contains a null + // value. + // The cast is safe because of the contains() check. + return contains(rowKey, columnKey) + ? function.apply(uncheckedCastNullableTToT(fromTable.get(rowKey, columnKey))) + : null; + } + + @Override + public int size() { + return fromTable.size(); + } + + @Override + public void clear() { + fromTable.clear(); + } + + @Override + public V2 put( + @ParametricNullness R rowKey, + @ParametricNullness C columnKey, + @ParametricNullness V2 value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + @Override + public V2 remove(Object rowKey, Object columnKey) { + return contains(rowKey, columnKey) + // The cast is safe because of the contains() check. + ? function.apply(uncheckedCastNullableTToT(fromTable.remove(rowKey, columnKey))) + : null; + } + + @Override + public Map row(@ParametricNullness R rowKey) { + return Maps.transformValues(fromTable.row(rowKey), function); + } + + @Override + public Map column(@ParametricNullness C columnKey) { + return Maps.transformValues(fromTable.column(columnKey), function); + } + + Function, Cell> cellFunction() { + return new Function, Cell>() { + @Override + public Cell apply(Cell cell) { + return immutableCell( + cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); + } + }; + } + + @Override + Iterator> cellIterator() { + return Iterators.transform(fromTable.cellSet().iterator(), cellFunction()); + } + + @Override + Spliterator> cellSpliterator() { + return CollectSpliterators.map(fromTable.cellSet().spliterator(), cellFunction()); + } + + @Override + public Set rowKeySet() { + return fromTable.rowKeySet(); + } + + @Override + public Set columnKeySet() { + return fromTable.columnKeySet(); + } + + @Override + Collection createValues() { + return Collections2.transform(fromTable.values(), function); + } + + @Override + public Map> rowMap() { + Function, Map> rowFunction = + new Function, Map>() { + @Override + public Map apply(Map row) { + return Maps.transformValues(row, function); + } + }; + return Maps.transformValues(fromTable.rowMap(), rowFunction); + } + + @Override + public Map> columnMap() { + Function, Map> columnFunction = + new Function, Map>() { + @Override + public Map apply(Map column) { + return Maps.transformValues(column, function); + } + }; + return Maps.transformValues(fromTable.columnMap(), columnFunction); + } + } + + /** + * Returns an unmodifiable view of the specified table. This method allows modules to provide + * users with "read-only" access to internal tables. Query operations on the returned table "read + * through" to the specified table, and attempts to modify the returned table, whether direct or + * via its collection views, result in an {@code UnsupportedOperationException}. + * + *

The returned table will be serializable if the specified table is serializable. + * + *

Consider using an {@link ImmutableTable}, which is guaranteed never to change. + * + * @since 11.0 + */ + public static + Table unmodifiableTable(Table table) { + return new UnmodifiableTable<>(table); + } + + private static class UnmodifiableTable + extends ForwardingTable implements Serializable { + final Table delegate; + + UnmodifiableTable(Table delegate) { + this.delegate = checkNotNull(delegate); + } + + @SuppressWarnings("unchecked") // safe, covariant cast + @Override + protected Table delegate() { + return (Table) delegate; + } + + @Override + public Set> cellSet() { + return Collections.unmodifiableSet(super.cellSet()); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Map column(@ParametricNullness C columnKey) { + return Collections.unmodifiableMap(super.column(columnKey)); + } + + @Override + public Set columnKeySet() { + return Collections.unmodifiableSet(super.columnKeySet()); + } + + @Override + public Map> columnMap() { + Function, Map> wrapper = unmodifiableWrapper(); + return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); + } + + @Override + public V put( + @ParametricNullness R rowKey, + @ParametricNullness C columnKey, + @ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } + + @Override + public Map row(@ParametricNullness R rowKey) { + return Collections.unmodifiableMap(super.row(rowKey)); + } + + @Override + public Set rowKeySet() { + return Collections.unmodifiableSet(super.rowKeySet()); + } + + @Override + public Map> rowMap() { + Function, Map> wrapper = unmodifiableWrapper(); + return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); + } + + @Override + public Collection values() { + return Collections.unmodifiableCollection(super.values()); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to + * provide users with "read-only" access to internal tables. Query operations on the returned + * table "read through" to the specified table, and attempts to modify the returned table, whether + * direct or via its collection views, result in an {@code UnsupportedOperationException}. + * + *

The returned table will be serializable if the specified table is serializable. + * + * @param table the row-sorted table for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified table + * @since 11.0 + */ + public static + RowSortedTable unmodifiableRowSortedTable( + RowSortedTable table) { + /* + * It's not ? extends R, because it's technically not covariant in R. Specifically, + * table.rowMap().comparator() could return a comparator that only works for the ? extends R. + * Collections.unmodifiableSortedMap makes the same distinction. + */ + return new UnmodifiableRowSortedMap<>(table); + } + + static final class UnmodifiableRowSortedMap + extends UnmodifiableTable implements RowSortedTable { + + public UnmodifiableRowSortedMap(RowSortedTable delegate) { + super(delegate); + } + + @Override + protected RowSortedTable delegate() { + return (RowSortedTable) super.delegate(); + } + + @Override + public SortedMap> rowMap() { + Function, Map> wrapper = unmodifiableWrapper(); + return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); + } + + @Override + public SortedSet rowKeySet() { + return Collections.unmodifiableSortedSet(delegate().rowKeySet()); + } + + private static final long serialVersionUID = 0; + } + + @SuppressWarnings("unchecked") + private static + Function, Map> unmodifiableWrapper() { + return (Function) UNMODIFIABLE_WRAPPER; + } + + private static final Function, ? extends Map> UNMODIFIABLE_WRAPPER = + new Function, Map>() { + @Override + public Map apply(Map input) { + return Collections.unmodifiableMap(input); + } + }; + + /** + * Returns a synchronized (thread-safe) table backed by the specified table. In order to guarantee + * serial access, it is critical that all access to the backing table is accomplished + * through the returned table. + * + *

It is imperative that the user manually synchronize on the returned table when accessing any + * of its collection views: + * + *

{@code
+   * Table table = Tables.synchronizedTable(HashBasedTable.create());
+   * ...
+   * Map row = table.row(rowKey);  // Needn't be in synchronized block
+   * ...
+   * synchronized (table) {  // Synchronizing on table, not row!
+   *   Iterator> i = row.entrySet().iterator(); // Must be in synchronized block
+   *   while (i.hasNext()) {
+   *     foo(i.next());
+   *   }
+   * }
+   * }
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned table will be serializable if the specified table is serializable. + * + * @param table the table to be wrapped in a synchronized view + * @return a synchronized view of the specified table + * @since 22.0 + */ + public static + Table synchronizedTable(Table table) { + return Synchronized.table(table, null); + } + + static boolean equalsImpl(Table table, Object obj) { + if (obj == table) { + return true; + } else if (obj instanceof Table) { + Table that = (Table) obj; + return table.cellSet().equals(that.cellSet()); + } else { + return false; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TopKSelector.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TopKSelector.java new file mode 100644 index 0000000000000..0db3514e9da12 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TopKSelector.java @@ -0,0 +1,289 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.math.IntMath; + +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * An accumulator that selects the "top" {@code k} elements added to it, relative to a provided + * comparator. "Top" can mean the greatest or the lowest elements, specified in the factory used to + * create the {@code TopKSelector} instance. + * + *

If your input data is available as a {@link Stream}, prefer passing {@link + * Comparators#least(int)} to {@link Stream#collect(java.util.stream.Collector)}. If it is available + * as an {@link Iterable} or {@link Iterator}, prefer {@link Ordering#leastOf(Iterable, int)}. + * + *

This uses the same efficient implementation as {@link Ordering#leastOf(Iterable, int)}, + * offering expected O(n + k log k) performance (worst case O(n log k)) for n calls to {@link + * #offer} and a call to {@link #topK}, with O(k) memory. In comparison, quickselect has the same + * asymptotics but requires O(n) memory, and a {@code PriorityQueue} implementation takes O(n log + * k). In benchmarks, this implementation performs at least as well as either implementation, and + * degrades more gracefully for worst-case input. + * + *

The implementation does not necessarily use a stable sorting algorithm; when multiple + * equivalent elements are added to it, it is undefined which will come first in the output. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class TopKSelector { + + /** + * Returns a {@code TopKSelector} that collects the lowest {@code k} elements added to it, + * relative to the natural ordering of the elements, and returns them via {@link #topK} in + * ascending order. + * + * @throws IllegalArgumentException if {@code k < 0} or {@code k > Integer.MAX_VALUE / 2} + */ + public static > TopKSelector least(int k) { + return least(k, Ordering.natural()); + } + + /** + * Returns a {@code TopKSelector} that collects the lowest {@code k} elements added to it, + * relative to the specified comparator, and returns them via {@link #topK} in ascending order. + * + * @throws IllegalArgumentException if {@code k < 0} or {@code k > Integer.MAX_VALUE / 2} + */ + public static TopKSelector least(int k, Comparator comparator) { + return new TopKSelector(comparator, k); + } + + /** + * Returns a {@code TopKSelector} that collects the greatest {@code k} elements added to it, + * relative to the natural ordering of the elements, and returns them via {@link #topK} in + * descending order. + * + * @throws IllegalArgumentException if {@code k < 0} or {@code k > Integer.MAX_VALUE / 2} + */ + public static > TopKSelector greatest(int k) { + return greatest(k, Ordering.natural()); + } + + /** + * Returns a {@code TopKSelector} that collects the greatest {@code k} elements added to it, + * relative to the specified comparator, and returns them via {@link #topK} in descending order. + * + * @throws IllegalArgumentException if {@code k < 0} or {@code k > Integer.MAX_VALUE / 2} + */ + public static TopKSelector greatest( + int k, Comparator comparator) { + return new TopKSelector(Ordering.from(comparator).reverse(), k); + } + + private final int k; + private final Comparator comparator; + + /* + * We are currently considering the elements in buffer in the range [0, bufferSize) as candidates + * for the top k elements. Whenever the buffer is filled, we quickselect the top k elements to the + * range [0, k) and ignore the remaining elements. + */ + private final T[] buffer; + private int bufferSize; + + /** + * The largest of the lowest k elements we've seen so far relative to this comparator. If + * bufferSize ≥ k, then we can ignore any elements greater than this value. + */ + private T threshold; + + private TopKSelector(Comparator comparator, int k) { + this.comparator = checkNotNull(comparator, "comparator"); + this.k = k; + checkArgument(k >= 0, "k (%s) must be >= 0", k); + checkArgument(k <= Integer.MAX_VALUE / 2, "k (%s) must be <= Integer.MAX_VALUE / 2", k); + this.buffer = (T[]) new Object[IntMath.checkedMultiply(k, 2)]; + this.bufferSize = 0; + this.threshold = null; + } + + /** + * Adds {@code elem} as a candidate for the top {@code k} elements. This operation takes amortized + * O(1) time. + */ + public void offer(@ParametricNullness T elem) { + if (k == 0) { + return; + } else if (bufferSize == 0) { + buffer[0] = elem; + threshold = elem; + bufferSize = 1; + } else if (bufferSize < k) { + buffer[bufferSize++] = elem; + // uncheckedCastNullableTToT is safe because bufferSize > 0. + if (comparator.compare(elem, uncheckedCastNullableTToT(threshold)) > 0) { + threshold = elem; + } + // uncheckedCastNullableTToT is safe because bufferSize > 0. + } else if (comparator.compare(elem, uncheckedCastNullableTToT(threshold)) < 0) { + // Otherwise, we can ignore elem; we've seen k better elements. + buffer[bufferSize++] = elem; + if (bufferSize == 2 * k) { + trim(); + } + } + } + + /** + * Quickselects the top k elements from the 2k elements in the buffer. O(k) expected time, O(k log + * k) worst case. + */ + private void trim() { + int left = 0; + int right = 2 * k - 1; + + int minThresholdPosition = 0; + // The leftmost position at which the greatest of the k lower elements + // -- the new value of threshold -- might be found. + + int iterations = 0; + int maxIterations = IntMath.log2(right - left, RoundingMode.CEILING) * 3; + while (left < right) { + int pivotIndex = (left + right + 1) >>> 1; + + int pivotNewIndex = partition(left, right, pivotIndex); + + if (pivotNewIndex > k) { + right = pivotNewIndex - 1; + } else if (pivotNewIndex < k) { + left = Math.max(pivotNewIndex, left + 1); + minThresholdPosition = pivotNewIndex; + } else { + break; + } + iterations++; + if (iterations >= maxIterations) { + @SuppressWarnings("nullness") // safe because we pass sort() a range that contains real Ts + T[] castBuffer = (T[]) buffer; + // We've already taken O(k log k), let's make sure we don't take longer than O(k log k). + Arrays.sort(castBuffer, left, right + 1, comparator); + break; + } + } + bufferSize = k; + + threshold = uncheckedCastNullableTToT(buffer[minThresholdPosition]); + for (int i = minThresholdPosition + 1; i < k; i++) { + if (comparator.compare( + uncheckedCastNullableTToT(buffer[i]), uncheckedCastNullableTToT(threshold)) + > 0) { + threshold = buffer[i]; + } + } + } + + /** + * Partitions the contents of buffer in the range [left, right] around the pivot element + * previously stored in buffer[pivotValue]. Returns the new index of the pivot element, + * pivotNewIndex, so that everything in [left, pivotNewIndex] is ≤ pivotValue and everything in + * (pivotNewIndex, right] is greater than pivotValue. + */ + private int partition(int left, int right, int pivotIndex) { + T pivotValue = uncheckedCastNullableTToT(buffer[pivotIndex]); + buffer[pivotIndex] = buffer[right]; + + int pivotNewIndex = left; + for (int i = left; i < right; i++) { + if (comparator.compare(uncheckedCastNullableTToT(buffer[i]), pivotValue) < 0) { + swap(pivotNewIndex, i); + pivotNewIndex++; + } + } + buffer[right] = buffer[pivotNewIndex]; + buffer[pivotNewIndex] = pivotValue; + return pivotNewIndex; + } + + private void swap(int i, int j) { + T tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + } + + TopKSelector combine(TopKSelector other) { + for (int i = 0; i < other.bufferSize; i++) { + this.offer(uncheckedCastNullableTToT(other.buffer[i])); + } + return this; + } + + /** + * Adds each member of {@code elements} as a candidate for the top {@code k} elements. This + * operation takes amortized linear time in the length of {@code elements}. + * + *

If all input data to this {@code TopKSelector} is in a single {@code Iterable}, prefer + * {@link Ordering#leastOf(Iterable, int)}, which provides a simpler API for that use case. + */ + public void offerAll(Iterable elements) { + offerAll(elements.iterator()); + } + + /** + * Adds each member of {@code elements} as a candidate for the top {@code k} elements. This + * operation takes amortized linear time in the length of {@code elements}. The iterator is + * consumed after this operation completes. + * + *

If all input data to this {@code TopKSelector} is in a single {@code Iterator}, prefer + * {@link Ordering#leastOf(Iterator, int)}, which provides a simpler API for that use case. + */ + public void offerAll(Iterator elements) { + while (elements.hasNext()) { + offer(elements.next()); + } + } + + /** + * Returns the top {@code k} elements offered to this {@code TopKSelector}, or all elements if + * fewer than {@code k} have been offered, in the order specified by the factory used to create + * this {@code TopKSelector}. + * + *

The returned list is an unmodifiable copy and will not be affected by further changes to + * this {@code TopKSelector}. This method returns in O(k log k) time. + */ + public List topK() { + @SuppressWarnings("nullness") // safe because we pass sort() a range that contains real Ts + T[] castBuffer = (T[]) buffer; + Arrays.sort(castBuffer, 0, bufferSize, comparator); + if (bufferSize > k) { + Arrays.fill(buffer, k, buffer.length, null); + bufferSize = k; + threshold = buffer[k - 1]; + } + // Up to bufferSize, all elements of buffer are real Ts (not null unless T includes null) + T[] topK = Arrays.copyOf(castBuffer, bufferSize); + // we have to support null elements, so no ImmutableList for us + return Collections.unmodifiableList(Arrays.asList(topK)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TransformedIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TransformedIterator.java new file mode 100644 index 0000000000000..adc29e96d2cac --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TransformedIterator.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An iterator that transforms a backing iterator; for internal use. This avoids the object overhead + * of constructing a {@link org.apache.iotdb.google.common.base.Function Function} for internal + * methods. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class TransformedIterator implements Iterator { + final Iterator backingIterator; + + TransformedIterator(Iterator backingIterator) { + this.backingIterator = checkNotNull(backingIterator); + } + + @ParametricNullness + abstract T transform(@ParametricNullness F from); + + @Override + public final boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override + @ParametricNullness + public final T next() { + return transform(backingIterator.next()); + } + + @Override + public final void remove() { + backingIterator.remove(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TransformedListIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TransformedListIterator.java new file mode 100644 index 0000000000000..185d2bcd298db --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TransformedListIterator.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; + +import java.util.ListIterator; + +/** + * An iterator that transforms a backing list iterator; for internal use. This avoids the object + * overhead of constructing a {@link Function} for internal methods. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class TransformedListIterator + extends TransformedIterator implements ListIterator { + TransformedListIterator(ListIterator backingIterator) { + super(backingIterator); + } + + private ListIterator backingIterator() { + return Iterators.cast(backingIterator); + } + + @Override + public final boolean hasPrevious() { + return backingIterator().hasPrevious(); + } + + @Override + @ParametricNullness + public final T previous() { + return transform(backingIterator().previous()); + } + + @Override + public final int nextIndex() { + return backingIterator().nextIndex(); + } + + @Override + public final int previousIndex() { + return backingIterator().previousIndex(); + } + + @Override + public void set(@ParametricNullness T element) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(@ParametricNullness T element) { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeBasedTable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeBasedTable.java new file mode 100644 index 0000000000000..dce048c8c26b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeBasedTable.java @@ -0,0 +1,347 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@code Table} whose row keys and column keys are ordered by their natural + * ordering or by supplied comparators. When constructing a {@code TreeBasedTable}, you may provide + * comparators for the row keys and the column keys, or you may use natural ordering for both. + * + *

The {@link #rowKeySet} method returns a {@link SortedSet} and the {@link #rowMap} method + * returns a {@link SortedMap}, instead of the {@link Set} and {@link Map} specified by the {@link + * Table} interface. + * + *

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link #columnMap()} have + * iterators that don't support {@code remove()}. Otherwise, all optional operations are supported. + * Null row keys, columns keys, and values are not supported. + * + *

Lookups by row key are often faster than lookups by column key, because the data is stored in + * a {@code Map>}. A method call like {@code column(columnKey).get(rowKey)} still runs + * quickly, since the row key is provided. However, {@code column(columnKey).size()} takes longer, + * since an iteration across all row keys occurs. + * + *

Because a {@code TreeBasedTable} has unique sorted values for a given row, both {@code + * row(rowKey)} and {@code rowMap().get(rowKey)} are {@link SortedMap} instances, instead of the + * {@link Map} specified in the {@link Table} interface. + * + *

Note that this implementation is not synchronized. If multiple threads access this table + * concurrently and one of the threads modifies the table, it must be synchronized externally. + * + *

See the Guava User Guide article on {@code Table}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 7.0 + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +public class TreeBasedTable extends StandardRowSortedTable { + private final Comparator columnComparator; + + private static class Factory implements Supplier>, Serializable { + final Comparator comparator; + + Factory(Comparator comparator) { + this.comparator = comparator; + } + + @Override + public TreeMap get() { + return new TreeMap<>(comparator); + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates an empty {@code TreeBasedTable} that uses the natural orderings of both row and column + * keys. + * + *

The method signature specifies {@code R extends Comparable} with a raw {@link Comparable}, + * instead of {@code R extends Comparable}, and the same for {@code C}. That's + * necessary to support classes defined without generics. + */ + public static TreeBasedTable create() { + return new TreeBasedTable<>(Ordering.natural(), Ordering.natural()); + } + + /** + * Creates an empty {@code TreeBasedTable} that is ordered by the specified comparators. + * + * @param rowComparator the comparator that orders the row keys + * @param columnComparator the comparator that orders the column keys + */ + public static TreeBasedTable create( + Comparator rowComparator, Comparator columnComparator) { + checkNotNull(rowComparator); + checkNotNull(columnComparator); + return new TreeBasedTable<>(rowComparator, columnComparator); + } + + /** + * Creates a {@code TreeBasedTable} with the same mappings and sort order as the specified {@code + * TreeBasedTable}. + */ + public static TreeBasedTable create(TreeBasedTable table) { + TreeBasedTable result = + new TreeBasedTable<>(table.rowComparator(), table.columnComparator()); + result.putAll(table); + return result; + } + + TreeBasedTable(Comparator rowComparator, Comparator columnComparator) { + super(new TreeMap>(rowComparator), new Factory(columnComparator)); + this.columnComparator = columnComparator; + } + + // TODO(jlevy): Move to StandardRowSortedTable? + + /** + * Returns the comparator that orders the rows. With natural ordering, {@link Ordering#natural()} + * is returned. + * + * @deprecated Use {@code table.rowKeySet().comparator()} instead. + */ + @Deprecated + public Comparator rowComparator() { + /* + * requireNonNull is safe because the factories require non-null Comparators, which they pass on + * to the backing collections. + */ + return requireNonNull(rowKeySet().comparator()); + } + + /** + * Returns the comparator that orders the columns. With natural ordering, {@link + * Ordering#natural()} is returned. + * + * @deprecated Store the {@link Comparator} alongside the {@link Table}. Or, if you know that the + * {@link Table} contains at least one value, you can retrieve the {@link Comparator} with: + * {@code ((SortedMap) table.rowMap().values().iterator().next()).comparator();}. + */ + @Deprecated + public Comparator columnComparator() { + return columnComparator; + } + + // TODO(lowasser): make column return a SortedMap + + /** + * {@inheritDoc} + * + *

Because a {@code TreeBasedTable} has unique sorted values for a given row, this method + * returns a {@link SortedMap}, instead of the {@link Map} specified in the {@link Table} + * interface. + * + * @since 10.0 (mostly + * source-compatible since 7.0) + */ + @Override + public SortedMap row(R rowKey) { + return new TreeRow(rowKey); + } + + private class TreeRow extends Row implements SortedMap { + final C lowerBound; + final C upperBound; + + TreeRow(R rowKey) { + this(rowKey, null, null); + } + + TreeRow(R rowKey, C lowerBound, C upperBound) { + super(rowKey); + this.lowerBound = lowerBound; + this.upperBound = upperBound; + checkArgument( + lowerBound == null || upperBound == null || compare(lowerBound, upperBound) <= 0); + } + + @Override + public SortedSet keySet() { + return new Maps.SortedKeySet<>(this); + } + + @Override + public Comparator comparator() { + return columnComparator(); + } + + int compare(Object a, Object b) { + // pretend we can compare anything + @SuppressWarnings("unchecked") + Comparator cmp = (Comparator) comparator(); + return cmp.compare(a, b); + } + + boolean rangeContains(Object o) { + return o != null + && (lowerBound == null || compare(lowerBound, o) <= 0) + && (upperBound == null || compare(upperBound, o) > 0); + } + + @Override + public SortedMap subMap(C fromKey, C toKey) { + checkArgument(rangeContains(checkNotNull(fromKey)) && rangeContains(checkNotNull(toKey))); + return new TreeRow(rowKey, fromKey, toKey); + } + + @Override + public SortedMap headMap(C toKey) { + checkArgument(rangeContains(checkNotNull(toKey))); + return new TreeRow(rowKey, lowerBound, toKey); + } + + @Override + public SortedMap tailMap(C fromKey) { + checkArgument(rangeContains(checkNotNull(fromKey))); + return new TreeRow(rowKey, fromKey, upperBound); + } + + @Override + public C firstKey() { + updateBackingRowMapField(); + if (backingRowMap == null) { + throw new NoSuchElementException(); + } + return ((SortedMap) backingRowMap).firstKey(); + } + + @Override + public C lastKey() { + updateBackingRowMapField(); + if (backingRowMap == null) { + throw new NoSuchElementException(); + } + return ((SortedMap) backingRowMap).lastKey(); + } + + transient SortedMap wholeRow; + + // If the row was previously empty, we check if there's a new row here every time we're queried. + void updateWholeRowField() { + if (wholeRow == null || (wholeRow.isEmpty() && backingMap.containsKey(rowKey))) { + wholeRow = (SortedMap) backingMap.get(rowKey); + } + } + + @Override + SortedMap computeBackingRowMap() { + updateWholeRowField(); + SortedMap map = wholeRow; + if (map != null) { + if (lowerBound != null) { + map = map.tailMap(lowerBound); + } + if (upperBound != null) { + map = map.headMap(upperBound); + } + return map; + } + return null; + } + + @Override + void maintainEmptyInvariant() { + updateWholeRowField(); + if (wholeRow != null && wholeRow.isEmpty()) { + backingMap.remove(rowKey); + wholeRow = null; + backingRowMap = null; + } + } + + @Override + public boolean containsKey(Object key) { + return rangeContains(key) && super.containsKey(key); + } + + @Override + public V put(C key, V value) { + checkArgument(rangeContains(checkNotNull(key))); + return super.put(key, value); + } + } + + // rowKeySet() and rowMap() are defined here so they appear in the Javadoc. + + @Override + public SortedSet rowKeySet() { + return super.rowKeySet(); + } + + @Override + public SortedMap> rowMap() { + return super.rowMap(); + } + + /** Overridden column iterator to return columns values in globally sorted order. */ + @Override + Iterator createColumnKeyIterator() { + Comparator comparator = columnComparator(); + + Iterator merged = + Iterators.mergeSorted( + Iterables.transform( + backingMap.values(), (Map input) -> input.keySet().iterator()), + comparator); + + return new AbstractIterator() { + C lastValue; + + @Override + protected C computeNext() { + while (merged.hasNext()) { + C next = merged.next(); + boolean duplicate = lastValue != null && comparator.compare(next, lastValue) == 0; + + // Keep looping till we find a non-duplicate value. + if (!duplicate) { + lastValue = next; + return lastValue; + } + } + + lastValue = null; // clear reference to unused data + return endOfData(); + } + }; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeMultimap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeMultimap.java new file mode 100644 index 0000000000000..5af505cc11be1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeMultimap.java @@ -0,0 +1,232 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of {@code Multimap} whose keys and values are ordered by their natural ordering or + * by supplied comparators. In all cases, this implementation uses {@link Comparable#compareTo} or + * {@link Comparator#compare} instead of {@link Object#equals} to determine equivalence of + * instances. + * + *

Warning: The comparators or comparables used must be consistent with equals as + * explained by the {@link Comparable} class specification. Otherwise, the resulting multiset will + * violate the general contract of {@link SetMultimap}, which is specified in terms of {@link + * Object#equals}. + * + *

The collections returned by {@code keySet} and {@code asMap} iterate through the keys + * according to the key comparator ordering or the natural ordering of the keys. Similarly, {@code + * get}, {@code removeAll}, and {@code replaceValues} return collections that iterate through the + * values according to the value comparator ordering or the natural ordering of the values. The + * collections generated by {@code entries}, {@code keys}, and {@code values} iterate across the + * keys according to the above key ordering, and for each key they iterate across the values + * according to the value ordering. + * + *

The multimap does not store duplicate key-value pairs. Adding a new key-value pair equal to an + * existing key-value pair has no effect. + * + *

Null keys and values are permitted (provided, of course, that the respective comparators + * support them). All optional multimap methods are supported, and all returned views are + * modifiable. + * + *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent + * read operations will work correctly. To allow concurrent update operations, wrap your multimap + * with a call to {@link Multimaps#synchronizedSortedSetMultimap}. + * + *

See the Guava User Guide article on {@code Multimap}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 + */ +@GwtCompatible(serializable = true, emulated = true) +@ElementTypesAreNonnullByDefault +public class TreeMultimap + extends AbstractSortedKeySortedSetMultimap { + private transient Comparator keyComparator; + private transient Comparator valueComparator; + + /** + * Creates an empty {@code TreeMultimap} ordered by the natural ordering of its keys and values. + */ + public static TreeMultimap create() { + return new TreeMultimap<>(Ordering.natural(), Ordering.natural()); + } + + /** + * Creates an empty {@code TreeMultimap} instance using explicit comparators. Neither comparator + * may be null; use {@link Ordering#natural()} to specify natural order. + * + * @param keyComparator the comparator that determines the key ordering + * @param valueComparator the comparator that determines the value ordering + */ + public static TreeMultimap create( + Comparator keyComparator, Comparator valueComparator) { + return new TreeMultimap<>(checkNotNull(keyComparator), checkNotNull(valueComparator)); + } + + /** + * Constructs a {@code TreeMultimap}, ordered by the natural ordering of its keys and values, with + * the same mappings as the specified multimap. + * + * @param multimap the multimap whose contents are copied to this multimap + */ + public static TreeMultimap create( + Multimap multimap) { + return new TreeMultimap<>(Ordering.natural(), Ordering.natural(), multimap); + } + + TreeMultimap(Comparator keyComparator, Comparator valueComparator) { + super(new TreeMap>(keyComparator)); + this.keyComparator = keyComparator; + this.valueComparator = valueComparator; + } + + private TreeMultimap( + Comparator keyComparator, + Comparator valueComparator, + Multimap multimap) { + this(keyComparator, valueComparator); + putAll(multimap); + } + + @Override + Map> createAsMap() { + return createMaybeNavigableAsMap(); + } + + /** + * {@inheritDoc} + * + *

Creates an empty {@code TreeSet} for a collection of values for one key. + * + * @return a new {@code TreeSet} containing a collection of values for one key + */ + @Override + SortedSet createCollection() { + return new TreeSet<>(valueComparator); + } + + @Override + Collection createCollection(@ParametricNullness K key) { + if (key == null) { + int unused = keyComparator().compare(key, key); + } + return super.createCollection(key); + } + + /** + * Returns the comparator that orders the multimap keys. + * + * @deprecated Use {@code ((NavigableSet) multimap.keySet()).comparator()} instead. + */ + @Deprecated + public Comparator keyComparator() { + return keyComparator; + } + + @Override + public Comparator valueComparator() { + return valueComparator; + } + + /** + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ + @Override + @GwtIncompatible // NavigableSet + public NavigableSet get(@ParametricNullness K key) { + return (NavigableSet) super.get(key); + } + + /** + * {@inheritDoc} + * + *

Because a {@code TreeMultimap} has unique sorted keys, this method returns a {@link + * NavigableSet}, instead of the {@link java.util.Set} specified in the {@link Multimap} + * interface. + * + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ + @Override + public NavigableSet keySet() { + return (NavigableSet) super.keySet(); + } + + /** + * {@inheritDoc} + * + *

Because a {@code TreeMultimap} has unique sorted keys, this method returns a {@link + * NavigableMap}, instead of the {@link java.util.Map} specified in the {@link Multimap} + * interface. + * + * @since 14.0 (present with return type {@code SortedMap} since 2.0) + */ + @Override + public NavigableMap> asMap() { + return (NavigableMap>) super.asMap(); + } + + /** + * @serialData key comparator, value comparator, number of distinct keys, and then for each + * distinct key: the key, number of values for that key, and key values + */ + @GwtIncompatible // java.io.ObjectOutputStream + @J2ktIncompatible + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyComparator()); + stream.writeObject(valueComparator()); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible // java.io.ObjectInputStream + @J2ktIncompatible + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyComparator = checkNotNull((Comparator) stream.readObject()); + valueComparator = checkNotNull((Comparator) stream.readObject()); + setMap(new TreeMap>(keyComparator)); + Serialization.populateMultimap(this, stream); + } + + @GwtIncompatible // not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeMultiset.java new file mode 100644 index 0000000000000..6929e17c5d22e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeMultiset.java @@ -0,0 +1,1090 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.ObjIntConsumer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.CollectPreconditions.checkNonnegative; +import static org.apache.iotdb.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; + +/** + * A multiset which maintains the ordering of its elements, according to either their natural order + * or an explicit {@link Comparator}. In all cases, this implementation uses {@link + * Comparable#compareTo} or {@link Comparator#compare} instead of {@link Object#equals} to determine + * equivalence of instances. + * + *

Warning: The comparison must be consistent with equals as explained by the + * {@link Comparable} class specification. Otherwise, the resulting multiset will violate the {@link + * java.util.Collection} contract, which is specified in terms of {@link Object#equals}. + * + *

See the Guava User Guide article on {@code Multiset}. + * + * @author Louis Wasserman + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class TreeMultiset extends AbstractSortedMultiset + implements Serializable { + + /** + * Creates a new, empty multiset, sorted according to the elements' natural order. All elements + * inserted into the multiset must implement the {@code Comparable} interface. Furthermore, all + * such elements must be mutually comparable: {@code e1.compareTo(e2)} must not throw a + * {@code ClassCastException} for any elements {@code e1} and {@code e2} in the multiset. If the + * user attempts to add an element to the multiset that violates this constraint (for example, the + * user attempts to add a string element to a set whose elements are integers), the {@code + * add(Object)} call will throw a {@code ClassCastException}. + * + *

The type specification is {@code }, instead of the more specific + * {@code >}, to support classes defined without generics. + */ + public static TreeMultiset create() { + return new TreeMultiset(Ordering.natural()); + } + + /** + * Creates a new, empty multiset, sorted according to the specified comparator. All elements + * inserted into the multiset must be mutually comparable by the specified comparator: + * {@code comparator.compare(e1, e2)} must not throw a {@code ClassCastException} for any elements + * {@code e1} and {@code e2} in the multiset. If the user attempts to add an element to the + * multiset that violates this constraint, the {@code add(Object)} call will throw a {@code + * ClassCastException}. + * + * @param comparator the comparator that will be used to sort this multiset. A null value + * indicates that the elements' natural ordering should be used. + */ + @SuppressWarnings("unchecked") + public static TreeMultiset create(Comparator comparator) { + return (comparator == null) + ? new TreeMultiset((Comparator) Ordering.natural()) + : new TreeMultiset(comparator); + } + + /** + * Creates an empty multiset containing the given initial elements, sorted according to the + * elements' natural order. + * + *

This implementation is highly efficient when {@code elements} is itself a {@link Multiset}. + * + *

The type specification is {@code }, instead of the more specific + * {@code >}, to support classes defined without generics. + */ + public static TreeMultiset create(Iterable elements) { + TreeMultiset multiset = create(); + Iterables.addAll(multiset, elements); + return multiset; + } + + private final transient Reference> rootReference; + private final transient GeneralRange range; + private final transient AvlNode header; + + TreeMultiset(Reference> rootReference, GeneralRange range, AvlNode endLink) { + super(range.comparator()); + this.rootReference = rootReference; + this.range = range; + this.header = endLink; + } + + TreeMultiset(Comparator comparator) { + super(comparator); + this.range = GeneralRange.all(comparator); + this.header = new AvlNode<>(); + successor(header, header); + this.rootReference = new Reference<>(); + } + + /** A function which can be summed across a subtree. */ + private enum Aggregate { + SIZE { + @Override + int nodeAggregate(AvlNode node) { + return node.elemCount; + } + + @Override + long treeAggregate(AvlNode root) { + return (root == null) ? 0 : root.totalCount; + } + }, + DISTINCT { + @Override + int nodeAggregate(AvlNode node) { + return 1; + } + + @Override + long treeAggregate(AvlNode root) { + return (root == null) ? 0 : root.distinctElements; + } + }; + + abstract int nodeAggregate(AvlNode node); + + abstract long treeAggregate(AvlNode root); + } + + private long aggregateForEntries(Aggregate aggr) { + AvlNode root = rootReference.get(); + long total = aggr.treeAggregate(root); + if (range.hasLowerBound()) { + total -= aggregateBelowRange(aggr, root); + } + if (range.hasUpperBound()) { + total -= aggregateAboveRange(aggr, root); + } + return total; + } + + private long aggregateBelowRange(Aggregate aggr, AvlNode node) { + if (node == null) { + return 0; + } + // The cast is safe because we call this method only if hasLowerBound(). + int cmp = + comparator() + .compare(uncheckedCastNullableTToT(range.getLowerEndpoint()), node.getElement()); + if (cmp < 0) { + return aggregateBelowRange(aggr, node.left); + } else if (cmp == 0) { + switch (range.getLowerBoundType()) { + case OPEN: + return aggr.nodeAggregate(node) + aggr.treeAggregate(node.left); + case CLOSED: + return aggr.treeAggregate(node.left); + default: + throw new AssertionError(); + } + } else { + return aggr.treeAggregate(node.left) + + aggr.nodeAggregate(node) + + aggregateBelowRange(aggr, node.right); + } + } + + private long aggregateAboveRange(Aggregate aggr, AvlNode node) { + if (node == null) { + return 0; + } + // The cast is safe because we call this method only if hasUpperBound(). + int cmp = + comparator() + .compare(uncheckedCastNullableTToT(range.getUpperEndpoint()), node.getElement()); + if (cmp > 0) { + return aggregateAboveRange(aggr, node.right); + } else if (cmp == 0) { + switch (range.getUpperBoundType()) { + case OPEN: + return aggr.nodeAggregate(node) + aggr.treeAggregate(node.right); + case CLOSED: + return aggr.treeAggregate(node.right); + default: + throw new AssertionError(); + } + } else { + return aggr.treeAggregate(node.right) + + aggr.nodeAggregate(node) + + aggregateAboveRange(aggr, node.left); + } + } + + @Override + public int size() { + return Ints.saturatedCast(aggregateForEntries(Aggregate.SIZE)); + } + + @Override + int distinctElements() { + return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT)); + } + + static int distinctElements(AvlNode node) { + return (node == null) ? 0 : node.distinctElements; + } + + @Override + public int count(Object element) { + try { + @SuppressWarnings("unchecked") + E e = (E) element; + AvlNode root = rootReference.get(); + if (!range.contains(e) || root == null) { + return 0; + } + return root.count(comparator(), e); + } catch (ClassCastException | NullPointerException e) { + return 0; + } + } + + @Override + public int add(@ParametricNullness E element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + checkArgument(range.contains(element)); + AvlNode root = rootReference.get(); + if (root == null) { + int unused = comparator().compare(element, element); + AvlNode newRoot = new AvlNode(element, occurrences); + successor(header, newRoot, header); + rootReference.checkAndSet(root, newRoot); + return 0; + } + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot = root.add(comparator(), element, occurrences, result); + rootReference.checkAndSet(root, newRoot); + return result[0]; + } + + @Override + public int remove(Object element, int occurrences) { + checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return count(element); + } + AvlNode root = rootReference.get(); + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot; + try { + @SuppressWarnings("unchecked") + E e = (E) element; + if (!range.contains(e) || root == null) { + return 0; + } + newRoot = root.remove(comparator(), e, occurrences, result); + } catch (ClassCastException | NullPointerException e) { + return 0; + } + rootReference.checkAndSet(root, newRoot); + return result[0]; + } + + @Override + public int setCount(@ParametricNullness E element, int count) { + checkNonnegative(count, "count"); + if (!range.contains(element)) { + checkArgument(count == 0); + return 0; + } + + AvlNode root = rootReference.get(); + if (root == null) { + if (count > 0) { + add(element, count); + } + return 0; + } + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot = root.setCount(comparator(), element, count, result); + rootReference.checkAndSet(root, newRoot); + return result[0]; + } + + @Override + public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { + checkNonnegative(newCount, "newCount"); + checkNonnegative(oldCount, "oldCount"); + checkArgument(range.contains(element)); + + AvlNode root = rootReference.get(); + if (root == null) { + if (oldCount == 0) { + if (newCount > 0) { + add(element, newCount); + } + return true; + } else { + return false; + } + } + int[] result = new int[1]; // used as a mutable int reference to hold result + AvlNode newRoot = root.setCount(comparator(), element, oldCount, newCount, result); + rootReference.checkAndSet(root, newRoot); + return result[0] == oldCount; + } + + @Override + public void clear() { + if (!range.hasLowerBound() && !range.hasUpperBound()) { + // We can do this in O(n) rather than removing one by one, which could force rebalancing. + for (AvlNode current = header.succ(); current != header; ) { + AvlNode next = current.succ(); + + current.elemCount = 0; + // Also clear these fields so that one deleted Entry doesn't retain all elements. + current.left = null; + current.right = null; + current.pred = null; + current.succ = null; + + current = next; + } + successor(header, header); + rootReference.clear(); + } else { + // TODO(cpovirk): Perhaps we can optimize in this case, too? + Iterators.clear(entryIterator()); + } + } + + private Entry wrapEntry(final AvlNode baseEntry) { + return new Multisets.AbstractEntry() { + @Override + @ParametricNullness + public E getElement() { + return baseEntry.getElement(); + } + + @Override + public int getCount() { + int result = baseEntry.getCount(); + if (result == 0) { + return count(getElement()); + } else { + return result; + } + } + }; + } + + /** Returns the first node in the tree that is in range. */ + private AvlNode firstNode() { + AvlNode root = rootReference.get(); + if (root == null) { + return null; + } + AvlNode node; + if (range.hasLowerBound()) { + // The cast is safe because of the hasLowerBound check. + E endpoint = uncheckedCastNullableTToT(range.getLowerEndpoint()); + node = root.ceiling(comparator(), endpoint); + if (node == null) { + return null; + } + if (range.getLowerBoundType() == BoundType.OPEN + && comparator().compare(endpoint, node.getElement()) == 0) { + node = node.succ(); + } + } else { + node = header.succ(); + } + return (node == header || !range.contains(node.getElement())) ? null : node; + } + + private AvlNode lastNode() { + AvlNode root = rootReference.get(); + if (root == null) { + return null; + } + AvlNode node; + if (range.hasUpperBound()) { + // The cast is safe because of the hasUpperBound check. + E endpoint = uncheckedCastNullableTToT(range.getUpperEndpoint()); + node = root.floor(comparator(), endpoint); + if (node == null) { + return null; + } + if (range.getUpperBoundType() == BoundType.OPEN + && comparator().compare(endpoint, node.getElement()) == 0) { + node = node.pred(); + } + } else { + node = header.pred(); + } + return (node == header || !range.contains(node.getElement())) ? null : node; + } + + @Override + Iterator elementIterator() { + return Multisets.elementIterator(entryIterator()); + } + + @Override + Iterator> entryIterator() { + return new Iterator>() { + AvlNode current = firstNode(); + Entry prevEntry; + + @Override + public boolean hasNext() { + if (current == null) { + return false; + } else if (range.tooHigh(current.getElement())) { + current = null; + return false; + } else { + return true; + } + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + // requireNonNull is safe because current is only nulled out after iteration is complete. + Entry result = wrapEntry(requireNonNull(current)); + prevEntry = result; + if (current.succ() == header) { + current = null; + } else { + current = current.succ(); + } + return result; + } + + @Override + public void remove() { + checkState(prevEntry != null, "no calls to next() since the last call to remove()"); + setCount(prevEntry.getElement(), 0); + prevEntry = null; + } + }; + } + + @Override + Iterator> descendingEntryIterator() { + return new Iterator>() { + AvlNode current = lastNode(); + Entry prevEntry = null; + + @Override + public boolean hasNext() { + if (current == null) { + return false; + } else if (range.tooLow(current.getElement())) { + current = null; + return false; + } else { + return true; + } + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + // requireNonNull is safe because current is only nulled out after iteration is complete. + requireNonNull(current); + Entry result = wrapEntry(current); + prevEntry = result; + if (current.pred() == header) { + current = null; + } else { + current = current.pred(); + } + return result; + } + + @Override + public void remove() { + checkState(prevEntry != null, "no calls to next() since the last call to remove()"); + setCount(prevEntry.getElement(), 0); + prevEntry = null; + } + }; + } + + @Override + public void forEachEntry(ObjIntConsumer action) { + checkNotNull(action); + for (AvlNode node = firstNode(); + node != header && node != null && !range.tooHigh(node.getElement()); + node = node.succ()) { + action.accept(node.getElement(), node.getCount()); + } + } + + @Override + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + @Override + public SortedMultiset headMultiset(@ParametricNullness E upperBound, BoundType boundType) { + return new TreeMultiset( + rootReference, + range.intersect(GeneralRange.upTo(comparator(), upperBound, boundType)), + header); + } + + @Override + public SortedMultiset tailMultiset(@ParametricNullness E lowerBound, BoundType boundType) { + return new TreeMultiset( + rootReference, + range.intersect(GeneralRange.downTo(comparator(), lowerBound, boundType)), + header); + } + + private static final class Reference { + private T value; + + public T get() { + return value; + } + + public void checkAndSet(T expected, T newValue) { + if (value != expected) { + throw new ConcurrentModificationException(); + } + value = newValue; + } + + void clear() { + value = null; + } + } + + private static final class AvlNode { + /* + * For "normal" nodes, the type of this field is `E`, not `@Nullable E` (though note that E is a + * type that can include null, as in a TreeMultiset<@Nullable String>). + * + * For the header node, though, this field contains `null`, regardless of the type of the + * multiset. + * + * Most code that operates on an AvlNode never operates on the header node. Such code can access + * the elem field without a null check by calling getElement(). + */ + private final E elem; + + // elemCount is 0 iff this node has been deleted. + private int elemCount; + + private int distinctElements; + private long totalCount; + private int height; + private AvlNode left; + private AvlNode right; + /* + * pred and succ are nullable after construction, but we always call successor() to initialize + * them immediately thereafter. + * + * They may be subsequently nulled out by TreeMultiset.clear(). I think that the only place that + * we can reference a node whose fields have been cleared is inside the iterator (and presumably + * only under concurrent modification). + * + * To access these fields when you know that they are not null, call the pred() and succ() + * methods, which perform null checks before returning the fields. + */ + private AvlNode pred; + private AvlNode succ; + + AvlNode(@ParametricNullness E elem, int elemCount) { + checkArgument(elemCount > 0); + this.elem = elem; + this.elemCount = elemCount; + this.totalCount = elemCount; + this.distinctElements = 1; + this.height = 1; + this.left = null; + this.right = null; + } + + /** Constructor for the header node. */ + AvlNode() { + this.elem = null; + this.elemCount = 1; + } + + // For discussion of pred() and succ(), see the comment on the pred and succ fields. + + private AvlNode pred() { + return requireNonNull(pred); + } + + private AvlNode succ() { + return requireNonNull(succ); + } + + int count(Comparator comparator, @ParametricNullness E e) { + int cmp = comparator.compare(e, getElement()); + if (cmp < 0) { + return (left == null) ? 0 : left.count(comparator, e); + } else if (cmp > 0) { + return (right == null) ? 0 : right.count(comparator, e); + } else { + return elemCount; + } + } + + private AvlNode addRightChild(@ParametricNullness E e, int count) { + right = new AvlNode(e, count); + successor(this, right, succ()); + height = Math.max(2, height); + distinctElements++; + totalCount += count; + return this; + } + + private AvlNode addLeftChild(@ParametricNullness E e, int count) { + left = new AvlNode(e, count); + successor(pred(), left, this); + height = Math.max(2, height); + distinctElements++; + totalCount += count; + return this; + } + + AvlNode add( + Comparator comparator, @ParametricNullness E e, int count, int[] result) { + /* + * It speeds things up considerably to unconditionally add count to totalCount here, + * but that destroys failure atomicity in the case of count overflow. =( + */ + int cmp = comparator.compare(e, getElement()); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + return addLeftChild(e, count); + } + int initHeight = initLeft.height; + + left = initLeft.add(comparator, e, count, result); + if (result[0] == 0) { + distinctElements++; + } + this.totalCount += count; + return (left.height == initHeight) ? this : rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + return addRightChild(e, count); + } + int initHeight = initRight.height; + + right = initRight.add(comparator, e, count, result); + if (result[0] == 0) { + distinctElements++; + } + this.totalCount += count; + return (right.height == initHeight) ? this : rebalance(); + } + + // adding count to me! No rebalance possible. + result[0] = elemCount; + long resultCount = (long) elemCount + count; + checkArgument(resultCount <= Integer.MAX_VALUE); + this.elemCount += count; + this.totalCount += count; + return this; + } + + AvlNode remove( + Comparator comparator, @ParametricNullness E e, int count, int[] result) { + int cmp = comparator.compare(e, getElement()); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + return this; + } + + left = initLeft.remove(comparator, e, count, result); + + if (result[0] > 0) { + if (count >= result[0]) { + this.distinctElements--; + this.totalCount -= result[0]; + } else { + this.totalCount -= count; + } + } + return (result[0] == 0) ? this : rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + return this; + } + + right = initRight.remove(comparator, e, count, result); + + if (result[0] > 0) { + if (count >= result[0]) { + this.distinctElements--; + this.totalCount -= result[0]; + } else { + this.totalCount -= count; + } + } + return rebalance(); + } + + // removing count from me! + result[0] = elemCount; + if (count >= elemCount) { + return deleteMe(); + } else { + this.elemCount -= count; + this.totalCount -= count; + return this; + } + } + + AvlNode setCount( + Comparator comparator, @ParametricNullness E e, int count, int[] result) { + int cmp = comparator.compare(e, getElement()); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + return (count > 0) ? addLeftChild(e, count) : this; + } + + left = initLeft.setCount(comparator, e, count, result); + + if (count == 0 && result[0] != 0) { + this.distinctElements--; + } else if (count > 0 && result[0] == 0) { + this.distinctElements++; + } + + this.totalCount += count - result[0]; + return rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + return (count > 0) ? addRightChild(e, count) : this; + } + + right = initRight.setCount(comparator, e, count, result); + + if (count == 0 && result[0] != 0) { + this.distinctElements--; + } else if (count > 0 && result[0] == 0) { + this.distinctElements++; + } + + this.totalCount += count - result[0]; + return rebalance(); + } + + // setting my count + result[0] = elemCount; + if (count == 0) { + return deleteMe(); + } + this.totalCount += count - elemCount; + this.elemCount = count; + return this; + } + + AvlNode setCount( + Comparator comparator, + @ParametricNullness E e, + int expectedCount, + int newCount, + int[] result) { + int cmp = comparator.compare(e, getElement()); + if (cmp < 0) { + AvlNode initLeft = left; + if (initLeft == null) { + result[0] = 0; + if (expectedCount == 0 && newCount > 0) { + return addLeftChild(e, newCount); + } + return this; + } + + left = initLeft.setCount(comparator, e, expectedCount, newCount, result); + + if (result[0] == expectedCount) { + if (newCount == 0 && result[0] != 0) { + this.distinctElements--; + } else if (newCount > 0 && result[0] == 0) { + this.distinctElements++; + } + this.totalCount += newCount - result[0]; + } + return rebalance(); + } else if (cmp > 0) { + AvlNode initRight = right; + if (initRight == null) { + result[0] = 0; + if (expectedCount == 0 && newCount > 0) { + return addRightChild(e, newCount); + } + return this; + } + + right = initRight.setCount(comparator, e, expectedCount, newCount, result); + + if (result[0] == expectedCount) { + if (newCount == 0 && result[0] != 0) { + this.distinctElements--; + } else if (newCount > 0 && result[0] == 0) { + this.distinctElements++; + } + this.totalCount += newCount - result[0]; + } + return rebalance(); + } + + // setting my count + result[0] = elemCount; + if (expectedCount == elemCount) { + if (newCount == 0) { + return deleteMe(); + } + this.totalCount += newCount - elemCount; + this.elemCount = newCount; + } + return this; + } + + private AvlNode deleteMe() { + int oldElemCount = this.elemCount; + this.elemCount = 0; + successor(pred(), succ()); + if (left == null) { + return right; + } else if (right == null) { + return left; + } else if (left.height >= right.height) { + AvlNode newTop = pred(); + // newTop is the maximum node in my left subtree + newTop.left = left.removeMax(newTop); + newTop.right = right; + newTop.distinctElements = distinctElements - 1; + newTop.totalCount = totalCount - oldElemCount; + return newTop.rebalance(); + } else { + AvlNode newTop = succ(); + newTop.right = right.removeMin(newTop); + newTop.left = left; + newTop.distinctElements = distinctElements - 1; + newTop.totalCount = totalCount - oldElemCount; + return newTop.rebalance(); + } + } + + // Removes the minimum node from this subtree to be reused elsewhere + private AvlNode removeMin(AvlNode node) { + if (left == null) { + return right; + } else { + left = left.removeMin(node); + distinctElements--; + totalCount -= node.elemCount; + return rebalance(); + } + } + + // Removes the maximum node from this subtree to be reused elsewhere + private AvlNode removeMax(AvlNode node) { + if (right == null) { + return left; + } else { + right = right.removeMax(node); + distinctElements--; + totalCount -= node.elemCount; + return rebalance(); + } + } + + private void recomputeMultiset() { + this.distinctElements = + 1 + TreeMultiset.distinctElements(left) + TreeMultiset.distinctElements(right); + this.totalCount = elemCount + totalCount(left) + totalCount(right); + } + + private void recomputeHeight() { + this.height = 1 + Math.max(height(left), height(right)); + } + + private void recompute() { + recomputeMultiset(); + recomputeHeight(); + } + + private AvlNode rebalance() { + switch (balanceFactor()) { + case -2: + // requireNonNull is safe because right must exist in order to get a negative factor. + requireNonNull(right); + if (right.balanceFactor() > 0) { + right = right.rotateRight(); + } + return rotateLeft(); + case 2: + // requireNonNull is safe because left must exist in order to get a positive factor. + requireNonNull(left); + if (left.balanceFactor() < 0) { + left = left.rotateLeft(); + } + return rotateRight(); + default: + recomputeHeight(); + return this; + } + } + + private int balanceFactor() { + return height(left) - height(right); + } + + private AvlNode rotateLeft() { + checkState(right != null); + AvlNode newTop = right; + this.right = newTop.left; + newTop.left = this; + newTop.totalCount = this.totalCount; + newTop.distinctElements = this.distinctElements; + this.recompute(); + newTop.recomputeHeight(); + return newTop; + } + + private AvlNode rotateRight() { + checkState(left != null); + AvlNode newTop = left; + this.left = newTop.right; + newTop.right = this; + newTop.totalCount = this.totalCount; + newTop.distinctElements = this.distinctElements; + this.recompute(); + newTop.recomputeHeight(); + return newTop; + } + + private static long totalCount(AvlNode node) { + return (node == null) ? 0 : node.totalCount; + } + + private static int height(AvlNode node) { + return (node == null) ? 0 : node.height; + } + + private AvlNode ceiling(Comparator comparator, @ParametricNullness E e) { + int cmp = comparator.compare(e, getElement()); + if (cmp < 0) { + return (left == null) ? this : MoreObjects.firstNonNull(left.ceiling(comparator, e), this); + } else if (cmp == 0) { + return this; + } else { + return (right == null) ? null : right.ceiling(comparator, e); + } + } + + private AvlNode floor(Comparator comparator, @ParametricNullness E e) { + int cmp = comparator.compare(e, getElement()); + if (cmp > 0) { + return (right == null) ? this : MoreObjects.firstNonNull(right.floor(comparator, e), this); + } else if (cmp == 0) { + return this; + } else { + return (left == null) ? null : left.floor(comparator, e); + } + } + + @ParametricNullness + E getElement() { + // For discussion of this cast, see the comment on the elem field. + return uncheckedCastNullableTToT(elem); + } + + int getCount() { + return elemCount; + } + + @Override + public String toString() { + return Multisets.immutableEntry(getElement(), getCount()).toString(); + } + } + + private static void successor(AvlNode a, AvlNode b) { + a.succ = b; + b.pred = a; + } + + private static void successor(AvlNode a, AvlNode b, AvlNode c) { + successor(a, b); + successor(b, c); + } + + /* + * TODO(jlevy): Decide whether entrySet() should return entries with an equals() method that + * calls the comparator to compare the two keys. If that change is made, + * AbstractMultiset.equals() can simply check whether two multisets have equal entry sets. + */ + + /** + * @serialData the comparator, the number of distinct elements, the first element, its count, the + * second element, its count, and so on + */ + @J2ktIncompatible + @GwtIncompatible // java.io.ObjectOutputStream + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(elementSet().comparator()); + Serialization.writeMultiset(this, stream); + } + + @J2ktIncompatible + @GwtIncompatible // java.io.ObjectInputStream + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + @SuppressWarnings("unchecked") + // reading data stored by writeObject + Comparator comparator = (Comparator) stream.readObject(); + Serialization.getFieldSetter(AbstractSortedMultiset.class, "comparator").set(this, comparator); + Serialization.getFieldSetter(TreeMultiset.class, "range") + .set(this, GeneralRange.all(comparator)); + Serialization.getFieldSetter(TreeMultiset.class, "rootReference") + .set(this, new Reference>()); + AvlNode header = new AvlNode<>(); + Serialization.getFieldSetter(TreeMultiset.class, "header").set(this, header); + successor(header, header); + Serialization.populateMultiset(this, stream); + } + + @GwtIncompatible // not needed in emulated source + @J2ktIncompatible + private static final long serialVersionUID = 1; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeRangeMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeRangeMap.java new file mode 100644 index 0000000000000..0e255fa064119 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeRangeMap.java @@ -0,0 +1,807 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.collect.Maps.IteratorBasedAbstractMap; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.BiFunction; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Predicates.compose; +import static org.apache.iotdb.google.common.base.Predicates.in; +import static org.apache.iotdb.google.common.base.Predicates.not; + +/** + * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting all optional + * operations. + * + *

Like all {@code RangeMap} implementations, this supports neither null keys nor null values. + * + * @author Louis Wasserman + * @since 14.0 + */ +@GwtIncompatible // NavigableMap +@ElementTypesAreNonnullByDefault +public final class TreeRangeMap implements RangeMap { + + private final NavigableMap, RangeMapEntry> entriesByLowerBound; + + public static TreeRangeMap create() { + return new TreeRangeMap<>(); + } + + private TreeRangeMap() { + this.entriesByLowerBound = Maps.newTreeMap(); + } + + private static final class RangeMapEntry + extends AbstractMapEntry, V> { + private final Range range; + private final V value; + + RangeMapEntry(Cut lowerBound, Cut upperBound, V value) { + this(Range.create(lowerBound, upperBound), value); + } + + RangeMapEntry(Range range, V value) { + this.range = range; + this.value = value; + } + + @Override + public Range getKey() { + return range; + } + + @Override + public V getValue() { + return value; + } + + public boolean contains(K value) { + return range.contains(value); + } + + Cut getLowerBound() { + return range.lowerBound; + } + + Cut getUpperBound() { + return range.upperBound; + } + } + + @Override + public V get(K key) { + Entry, V> entry = getEntry(key); + return (entry == null) ? null : entry.getValue(); + } + + @Override + public Entry, V> getEntry(K key) { + Entry, RangeMapEntry> mapEntry = + entriesByLowerBound.floorEntry(Cut.belowValue(key)); + if (mapEntry != null && mapEntry.getValue().contains(key)) { + return mapEntry.getValue(); + } else { + return null; + } + } + + @Override + public void put(Range range, V value) { + if (!range.isEmpty()) { + checkNotNull(value); + remove(range); + entriesByLowerBound.put(range.lowerBound, new RangeMapEntry(range, value)); + } + } + + @Override + public void putCoalescing(Range range, V value) { + // don't short-circuit if the range is empty - it may be between two ranges we can coalesce. + if (entriesByLowerBound.isEmpty()) { + put(range, value); + return; + } + + Range coalescedRange = coalescedRange(range, checkNotNull(value)); + put(coalescedRange, value); + } + + /** Computes the coalesced range for the given range+value - does not mutate the map. */ + private Range coalescedRange(Range range, V value) { + Range coalescedRange = range; + Entry, RangeMapEntry> lowerEntry = + entriesByLowerBound.lowerEntry(range.lowerBound); + coalescedRange = coalesce(coalescedRange, value, lowerEntry); + + Entry, RangeMapEntry> higherEntry = + entriesByLowerBound.floorEntry(range.upperBound); + coalescedRange = coalesce(coalescedRange, value, higherEntry); + + return coalescedRange; + } + + /** Returns the range that spans the given range and entry, if the entry can be coalesced. */ + private static Range coalesce( + Range range, V value, Entry, RangeMapEntry> entry) { + if (entry != null + && entry.getValue().getKey().isConnected(range) + && entry.getValue().getValue().equals(value)) { + return range.span(entry.getValue().getKey()); + } + return range; + } + + @Override + public void putAll(RangeMap rangeMap) { + for (Entry, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + entriesByLowerBound.clear(); + } + + @Override + public Range span() { + Entry, RangeMapEntry> firstEntry = entriesByLowerBound.firstEntry(); + Entry, RangeMapEntry> lastEntry = entriesByLowerBound.lastEntry(); + // Either both are null or neither is, but we check both to satisfy the nullness checker. + if (firstEntry == null || lastEntry == null) { + throw new NoSuchElementException(); + } + return Range.create( + firstEntry.getValue().getKey().lowerBound, lastEntry.getValue().getKey().upperBound); + } + + private void putRangeMapEntry(Cut lowerBound, Cut upperBound, V value) { + entriesByLowerBound.put(lowerBound, new RangeMapEntry(lowerBound, upperBound, value)); + } + + @Override + public void remove(Range rangeToRemove) { + if (rangeToRemove.isEmpty()) { + return; + } + + /* + * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to + * indicate the bounds of ranges in the range map. + */ + Entry, RangeMapEntry> mapEntryBelowToTruncate = + entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound); + if (mapEntryBelowToTruncate != null) { + // we know ( [ + RangeMapEntry rangeMapEntry = mapEntryBelowToTruncate.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) { + // we know ( [ ) + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { + // we know ( [ ] ), so insert the range ] ) back into the map -- + // it's being split apart + putRangeMapEntry( + rangeToRemove.upperBound, + rangeMapEntry.getUpperBound(), + mapEntryBelowToTruncate.getValue().getValue()); + } + // overwrite mapEntryToTruncateBelow with a truncated range + putRangeMapEntry( + rangeMapEntry.getLowerBound(), + rangeToRemove.lowerBound, + mapEntryBelowToTruncate.getValue().getValue()); + } + } + + Entry, RangeMapEntry> mapEntryAboveToTruncate = + entriesByLowerBound.lowerEntry(rangeToRemove.upperBound); + if (mapEntryAboveToTruncate != null) { + // we know ( ] + RangeMapEntry rangeMapEntry = mapEntryAboveToTruncate.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { + // we know ( ] ), and since we dealt with truncating below already, + // we know [ ( ] ) + putRangeMapEntry( + rangeToRemove.upperBound, + rangeMapEntry.getUpperBound(), + mapEntryAboveToTruncate.getValue().getValue()); + } + } + entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); + } + + private void split(Cut cut) { + /* + * The comments for this method will use | to indicate the cut point and ( ) to indicate the + * bounds of ranges in the range map. + */ + Entry, RangeMapEntry> mapEntryToSplit = entriesByLowerBound.lowerEntry(cut); + if (mapEntryToSplit == null) { + return; + } + // we know ( | + RangeMapEntry rangeMapEntry = mapEntryToSplit.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(cut) <= 0) { + return; + } + // we know ( | ) + putRangeMapEntry(rangeMapEntry.getLowerBound(), cut, rangeMapEntry.getValue()); + putRangeMapEntry(cut, rangeMapEntry.getUpperBound(), rangeMapEntry.getValue()); + } + + @Override + public void merge( + Range range, V value, BiFunction remappingFunction) { + checkNotNull(range); + checkNotNull(remappingFunction); + + if (range.isEmpty()) { + return; + } + split(range.lowerBound); + split(range.upperBound); + + // Due to the splitting of any entries spanning the range bounds, we know that any entry with a + // lower bound in the merge range is entirely contained by the merge range. + Set, RangeMapEntry>> entriesInMergeRange = + entriesByLowerBound.subMap(range.lowerBound, range.upperBound).entrySet(); + + // Create entries mapping any unmapped ranges in the merge range to the specified value. + ImmutableMap.Builder, RangeMapEntry> gaps = ImmutableMap.builder(); + if (value != null) { + final Iterator, RangeMapEntry>> backingItr = + entriesInMergeRange.iterator(); + Cut lowerBound = range.lowerBound; + while (backingItr.hasNext()) { + RangeMapEntry entry = backingItr.next().getValue(); + Cut upperBound = entry.getLowerBound(); + if (!lowerBound.equals(upperBound)) { + gaps.put(lowerBound, new RangeMapEntry(lowerBound, upperBound, value)); + } + lowerBound = entry.getUpperBound(); + } + if (!lowerBound.equals(range.upperBound)) { + gaps.put(lowerBound, new RangeMapEntry(lowerBound, range.upperBound, value)); + } + } + + // Remap all existing entries in the merge range. + final Iterator, RangeMapEntry>> backingItr = entriesInMergeRange.iterator(); + while (backingItr.hasNext()) { + Entry, RangeMapEntry> entry = backingItr.next(); + V newValue = remappingFunction.apply(entry.getValue().getValue(), value); + if (newValue == null) { + backingItr.remove(); + } else { + entry.setValue( + new RangeMapEntry( + entry.getValue().getLowerBound(), entry.getValue().getUpperBound(), newValue)); + } + } + + entriesByLowerBound.putAll(gaps.build()); + } + + @Override + public Map, V> asMapOfRanges() { + return new AsMapOfRanges(entriesByLowerBound.values()); + } + + @Override + public Map, V> asDescendingMapOfRanges() { + return new AsMapOfRanges(entriesByLowerBound.descendingMap().values()); + } + + private final class AsMapOfRanges extends IteratorBasedAbstractMap, V> { + + final Iterable, V>> entryIterable; + + @SuppressWarnings("unchecked") // it's safe to upcast iterables + AsMapOfRanges(Iterable> entryIterable) { + this.entryIterable = (Iterable) entryIterable; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public V get(Object key) { + if (key instanceof Range) { + Range range = (Range) key; + RangeMapEntry rangeMapEntry = entriesByLowerBound.get(range.lowerBound); + if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) { + return rangeMapEntry.getValue(); + } + } + return null; + } + + @Override + public int size() { + return entriesByLowerBound.size(); + } + + @Override + Iterator, V>> entryIterator() { + return entryIterable.iterator(); + } + } + + @Override + public RangeMap subRangeMap(Range subRange) { + if (subRange.equals(Range.all())) { + return this; + } else { + return new SubRangeMap(subRange); + } + } + + @SuppressWarnings("unchecked") + private RangeMap emptySubRangeMap() { + return (RangeMap) (RangeMap) EMPTY_SUB_RANGE_MAP; + } + + @SuppressWarnings("ConstantCaseForConstants") // This RangeMap is immutable. + private static final RangeMap, Object> EMPTY_SUB_RANGE_MAP = + new RangeMap, Object>() { + @Override + public Object get(Comparable key) { + return null; + } + + @Override + public Entry>, Object> getEntry(Comparable key) { + return null; + } + + @Override + public Range> span() { + throw new NoSuchElementException(); + } + + @Override + public void put(Range> range, Object value) { + checkNotNull(range); + throw new IllegalArgumentException( + "Cannot insert range " + range + " into an empty subRangeMap"); + } + + @Override + public void putCoalescing(Range> range, Object value) { + checkNotNull(range); + throw new IllegalArgumentException( + "Cannot insert range " + range + " into an empty subRangeMap"); + } + + @Override + public void putAll(RangeMap, ? extends Object> rangeMap) { + if (!rangeMap.asMapOfRanges().isEmpty()) { + throw new IllegalArgumentException( + "Cannot putAll(nonEmptyRangeMap) into an empty subRangeMap"); + } + } + + @Override + public void clear() {} + + @Override + public void remove(Range> range) { + checkNotNull(range); + } + + @Override + public void merge( + Range> range, + Object value, + BiFunction remappingFunction) { + checkNotNull(range); + throw new IllegalArgumentException( + "Cannot merge range " + range + " into an empty subRangeMap"); + } + + @Override + public Map>, Object> asMapOfRanges() { + return Collections.emptyMap(); + } + + @Override + public Map>, Object> asDescendingMapOfRanges() { + return Collections.emptyMap(); + } + + @Override + public RangeMap, Object> subRangeMap(Range> range) { + checkNotNull(range); + return this; + } + }; + + private class SubRangeMap implements RangeMap { + + private final Range subRange; + + SubRangeMap(Range subRange) { + this.subRange = subRange; + } + + @Override + public V get(K key) { + return subRange.contains(key) ? TreeRangeMap.this.get(key) : null; + } + + @Override + public Entry, V> getEntry(K key) { + if (subRange.contains(key)) { + Entry, V> entry = TreeRangeMap.this.getEntry(key); + if (entry != null) { + return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + } + } + return null; + } + + @Override + public Range span() { + Cut lowerBound; + Entry, RangeMapEntry> lowerEntry = + entriesByLowerBound.floorEntry(subRange.lowerBound); + if (lowerEntry != null + && lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) { + lowerBound = subRange.lowerBound; + } else { + lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound); + if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) { + throw new NoSuchElementException(); + } + } + + Cut upperBound; + Entry, RangeMapEntry> upperEntry = + entriesByLowerBound.lowerEntry(subRange.upperBound); + if (upperEntry == null) { + throw new NoSuchElementException(); + } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) { + upperBound = subRange.upperBound; + } else { + upperBound = upperEntry.getValue().getUpperBound(); + } + return Range.create(lowerBound, upperBound); + } + + @Override + public void put(Range range, V value) { + checkArgument( + subRange.encloses(range), "Cannot put range %s into a subRangeMap(%s)", range, subRange); + TreeRangeMap.this.put(range, value); + } + + @Override + public void putCoalescing(Range range, V value) { + if (entriesByLowerBound.isEmpty() || !subRange.encloses(range)) { + put(range, value); + return; + } + + Range coalescedRange = coalescedRange(range, checkNotNull(value)); + // only coalesce ranges within the subRange + put(coalescedRange.intersection(subRange), value); + } + + @Override + public void putAll(RangeMap rangeMap) { + if (rangeMap.asMapOfRanges().isEmpty()) { + return; + } + Range span = rangeMap.span(); + checkArgument( + subRange.encloses(span), + "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", + span, + subRange); + TreeRangeMap.this.putAll(rangeMap); + } + + @Override + public void clear() { + TreeRangeMap.this.remove(subRange); + } + + @Override + public void remove(Range range) { + if (range.isConnected(subRange)) { + TreeRangeMap.this.remove(range.intersection(subRange)); + } + } + + @Override + public void merge( + Range range, V value, BiFunction remappingFunction) { + checkArgument( + subRange.encloses(range), + "Cannot merge range %s into a subRangeMap(%s)", + range, + subRange); + TreeRangeMap.this.merge(range, value, remappingFunction); + } + + @Override + public RangeMap subRangeMap(Range range) { + if (!range.isConnected(subRange)) { + return emptySubRangeMap(); + } else { + return TreeRangeMap.this.subRangeMap(range.intersection(subRange)); + } + } + + @Override + public Map, V> asMapOfRanges() { + return new SubRangeMapAsMap(); + } + + @Override + public Map, V> asDescendingMapOfRanges() { + return new SubRangeMapAsMap() { + + @Override + Iterator, V>> entryIterator() { + if (subRange.isEmpty()) { + return Iterators.emptyIterator(); + } + final Iterator> backingItr = + entriesByLowerBound + .headMap(subRange.upperBound, false) + .descendingMap() + .values() + .iterator(); + return new AbstractIterator, V>>() { + + @Override + protected Entry, V> computeNext() { + if (backingItr.hasNext()) { + RangeMapEntry entry = backingItr.next(); + if (entry.getUpperBound().compareTo(subRange.lowerBound) <= 0) { + return endOfData(); + } + return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + } + return endOfData(); + } + }; + } + }; + } + + @Override + public boolean equals(Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap) o; + return asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } + return false; + } + + @Override + public int hashCode() { + return asMapOfRanges().hashCode(); + } + + @Override + public String toString() { + return asMapOfRanges().toString(); + } + + class SubRangeMapAsMap extends AbstractMap, V> { + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public V get(Object key) { + try { + if (key instanceof Range) { + @SuppressWarnings("unchecked") // we catch ClassCastExceptions + Range r = (Range) key; + if (!subRange.encloses(r) || r.isEmpty()) { + return null; + } + RangeMapEntry candidate = null; + if (r.lowerBound.compareTo(subRange.lowerBound) == 0) { + // r could be truncated on the left + Entry, RangeMapEntry> entry = + entriesByLowerBound.floorEntry(r.lowerBound); + if (entry != null) { + candidate = entry.getValue(); + } + } else { + candidate = entriesByLowerBound.get(r.lowerBound); + } + + if (candidate != null + && candidate.getKey().isConnected(subRange) + && candidate.getKey().intersection(subRange).equals(r)) { + return candidate.getValue(); + } + } + } catch (ClassCastException e) { + return null; + } + return null; + } + + @Override + public V remove(Object key) { + V value = get(key); + if (value != null) { + // it's definitely in the map, so the cast and requireNonNull are safe + @SuppressWarnings("unchecked") + Range range = (Range) requireNonNull(key); + TreeRangeMap.this.remove(range); + return value; + } + return null; + } + + @Override + public void clear() { + SubRangeMap.this.clear(); + } + + private boolean removeEntryIf(Predicate, V>> predicate) { + List> toRemove = Lists.newArrayList(); + for (Entry, V> entry : entrySet()) { + if (predicate.apply(entry)) { + toRemove.add(entry.getKey()); + } + } + for (Range range : toRemove) { + TreeRangeMap.this.remove(range); + } + return !toRemove.isEmpty(); + } + + @Override + public Set> keySet() { + return new Maps.KeySet, V>(SubRangeMapAsMap.this) { + @Override + public boolean remove(Object o) { + return SubRangeMapAsMap.this.remove(o) != null; + } + + @Override + public boolean retainAll(Collection c) { + return removeEntryIf(compose(not(in(c)), Maps.>keyFunction())); + } + }; + } + + @Override + public Set, V>> entrySet() { + return new Maps.EntrySet, V>() { + @Override + Map, V> map() { + return SubRangeMapAsMap.this; + } + + @Override + public Iterator, V>> iterator() { + return entryIterator(); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntryIf(not(in(c))); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + + @Override + public boolean isEmpty() { + return !iterator().hasNext(); + } + }; + } + + Iterator, V>> entryIterator() { + if (subRange.isEmpty()) { + return Iterators.emptyIterator(); + } + Cut cutToStart = + MoreObjects.firstNonNull( + entriesByLowerBound.floorKey(subRange.lowerBound), subRange.lowerBound); + final Iterator> backingItr = + entriesByLowerBound.tailMap(cutToStart, true).values().iterator(); + return new AbstractIterator, V>>() { + + @Override + protected Entry, V> computeNext() { + while (backingItr.hasNext()) { + RangeMapEntry entry = backingItr.next(); + if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) { + return endOfData(); + } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) { + // this might not be true e.g. at the start of the iteration + return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); + } + } + return endOfData(); + } + }; + } + + @Override + public Collection values() { + return new Maps.Values, V>(this) { + @Override + public boolean removeAll(Collection c) { + return removeEntryIf(compose(in(c), Maps.valueFunction())); + } + + @Override + public boolean retainAll(Collection c) { + return removeEntryIf(compose(not(in(c)), Maps.valueFunction())); + } + }; + } + } + } + + @Override + public boolean equals(Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap) o; + return asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } + return false; + } + + @Override + public int hashCode() { + return asMapOfRanges().hashCode(); + } + + @Override + public String toString() { + return entriesByLowerBound.values().toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeRangeSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeRangeSet.java new file mode 100644 index 0000000000000..36f44d893980f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeRangeSet.java @@ -0,0 +1,931 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.MoreObjects; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeMap; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation of {@link RangeSet} backed by a {@link TreeMap}. + * + * @author Louis Wasserman + * @since 14.0 + */ +@GwtIncompatible // uses NavigableMap +@ElementTypesAreNonnullByDefault +public class TreeRangeSet> extends AbstractRangeSet + implements Serializable { + + @VisibleForTesting final NavigableMap, Range> rangesByLowerBound; + + /** Creates an empty {@code TreeRangeSet} instance. */ + public static > TreeRangeSet create() { + return new TreeRangeSet<>(new TreeMap, Range>()); + } + + /** Returns a {@code TreeRangeSet} initialized with the ranges in the specified range set. */ + public static > TreeRangeSet create(RangeSet rangeSet) { + TreeRangeSet result = create(); + result.addAll(rangeSet); + return result; + } + + /** + * Returns a {@code TreeRangeSet} representing the union of the specified ranges. + * + *

This is the smallest {@code RangeSet} which encloses each of the specified ranges. An + * element will be contained in this {@code RangeSet} if and only if it is contained in at least + * one {@code Range} in {@code ranges}. + * + * @since 21.0 + */ + public static > TreeRangeSet create(Iterable> ranges) { + TreeRangeSet result = create(); + result.addAll(ranges); + return result; + } + + private TreeRangeSet(NavigableMap, Range> rangesByLowerCut) { + this.rangesByLowerBound = rangesByLowerCut; + } + + private transient Set> asRanges; + private transient Set> asDescendingSetOfRanges; + + @Override + public Set> asRanges() { + Set> result = asRanges; + return (result == null) ? asRanges = new AsRanges(rangesByLowerBound.values()) : result; + } + + @Override + public Set> asDescendingSetOfRanges() { + Set> result = asDescendingSetOfRanges; + return (result == null) + ? asDescendingSetOfRanges = new AsRanges(rangesByLowerBound.descendingMap().values()) + : result; + } + + final class AsRanges extends ForwardingCollection> implements Set> { + + final Collection> delegate; + + AsRanges(Collection> delegate) { + this.delegate = delegate; + } + + @Override + protected Collection> delegate() { + return delegate; + } + + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(Object o) { + return Sets.equalsImpl(this, o); + } + } + + @Override + public Range rangeContaining(C value) { + checkNotNull(value); + Entry, Range> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); + if (floorEntry != null && floorEntry.getValue().contains(value)) { + return floorEntry.getValue(); + } else { + // TODO(kevinb): revisit this design choice + return null; + } + } + + @Override + public boolean intersects(Range range) { + checkNotNull(range); + Entry, Range> ceilingEntry = rangesByLowerBound.ceilingEntry(range.lowerBound); + if (ceilingEntry != null + && ceilingEntry.getValue().isConnected(range) + && !ceilingEntry.getValue().intersection(range).isEmpty()) { + return true; + } + Entry, Range> priorEntry = rangesByLowerBound.lowerEntry(range.lowerBound); + return priorEntry != null + && priorEntry.getValue().isConnected(range) + && !priorEntry.getValue().intersection(range).isEmpty(); + } + + @Override + public boolean encloses(Range range) { + checkNotNull(range); + Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); + return floorEntry != null && floorEntry.getValue().encloses(range); + } + + private Range rangeEnclosing(Range range) { + checkNotNull(range); + Entry, Range> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); + return (floorEntry != null && floorEntry.getValue().encloses(range)) + ? floorEntry.getValue() + : null; + } + + @Override + public Range span() { + Entry, Range> firstEntry = rangesByLowerBound.firstEntry(); + Entry, Range> lastEntry = rangesByLowerBound.lastEntry(); + if (firstEntry == null || lastEntry == null) { + /* + * Either both are null or neither is: Either the set is empty, or it's not. But we check both + * to make the nullness checker happy. + */ + throw new NoSuchElementException(); + } + return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); + } + + @Override + public void add(Range rangeToAdd) { + checkNotNull(rangeToAdd); + + if (rangeToAdd.isEmpty()) { + return; + } + + // We will use { } to illustrate ranges currently in the range set, and < > + // to illustrate rangeToAdd. + Cut lbToAdd = rangeToAdd.lowerBound; + Cut ubToAdd = rangeToAdd.upperBound; + + Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); + if (entryBelowLB != null) { + // { < + Range rangeBelowLB = entryBelowLB.getValue(); + if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { + // { < }, and we will need to coalesce + if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { + // { < > } + ubToAdd = rangeBelowLB.upperBound; + /* + * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If + * not, add tests to demonstrate the problem with each approach + */ + } + lbToAdd = rangeBelowLB.lowerBound; + } + } + + Entry, Range> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); + if (entryBelowUB != null) { + // { > + Range rangeBelowUB = entryBelowUB.getValue(); + if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { + // { > }, and we need to coalesce + ubToAdd = rangeBelowUB.upperBound; + } + } + + // Remove ranges which are strictly enclosed. + rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); + + replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); + } + + @Override + public void remove(Range rangeToRemove) { + checkNotNull(rangeToRemove); + + if (rangeToRemove.isEmpty()) { + return; + } + + // We will use { } to illustrate ranges currently in the range set, and < > + // to illustrate rangeToRemove. + + Entry, Range> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); + if (entryBelowLB != null) { + // { < + Range rangeBelowLB = entryBelowLB.getValue(); + if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { + // { < }, and we will need to subdivide + if (rangeToRemove.hasUpperBound() + && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + // { < > } + replaceRangeWithSameLowerBound( + Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); + } + replaceRangeWithSameLowerBound( + Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); + } + } + + Entry, Range> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); + if (entryBelowUB != null) { + // { > + Range rangeBelowUB = entryBelowUB.getValue(); + if (rangeToRemove.hasUpperBound() + && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + // { > } + replaceRangeWithSameLowerBound( + Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); + } + } + + rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); + } + + private void replaceRangeWithSameLowerBound(Range range) { + if (range.isEmpty()) { + rangesByLowerBound.remove(range.lowerBound); + } else { + rangesByLowerBound.put(range.lowerBound, range); + } + } + + private transient RangeSet complement; + + @Override + public RangeSet complement() { + RangeSet result = complement; + return (result == null) ? complement = new Complement() : result; + } + + @VisibleForTesting + static final class RangesByUpperBound> + extends AbstractNavigableMap, Range> { + private final NavigableMap, Range> rangesByLowerBound; + + /** + * upperBoundWindow represents the headMap/subMap/tailMap view of the entire "ranges by upper + * bound" map; it's a constraint on the *keys*, and does not affect the values. + */ + private final Range> upperBoundWindow; + + RangesByUpperBound(NavigableMap, Range> rangesByLowerBound) { + this.rangesByLowerBound = rangesByLowerBound; + this.upperBoundWindow = Range.all(); + } + + private RangesByUpperBound( + NavigableMap, Range> rangesByLowerBound, Range> upperBoundWindow) { + this.rangesByLowerBound = rangesByLowerBound; + this.upperBoundWindow = upperBoundWindow; + } + + private NavigableMap, Range> subMap(Range> window) { + if (window.isConnected(upperBoundWindow)) { + return new RangesByUpperBound<>(rangesByLowerBound, window.intersection(upperBoundWindow)); + } else { + return ImmutableSortedMap.of(); + } + } + + @Override + public NavigableMap, Range> subMap( + Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { + return subMap( + Range.range( + fromKey, BoundType.forBoolean(fromInclusive), + toKey, BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + @Override + public Comparator> comparator() { + return Ordering.>natural(); + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public Range get(Object key) { + if (key instanceof Cut) { + try { + @SuppressWarnings("unchecked") // we catch CCEs + Cut cut = (Cut) key; + if (!upperBoundWindow.contains(cut)) { + return null; + } + Entry, Range> candidate = rangesByLowerBound.lowerEntry(cut); + if (candidate != null && candidate.getValue().upperBound.equals(cut)) { + return candidate.getValue(); + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + @Override + Iterator, Range>> entryIterator() { + /* + * We want to start the iteration at the first range where the upper bound is in + * upperBoundWindow. + */ + Iterator> backingItr; + if (!upperBoundWindow.hasLowerBound()) { + backingItr = rangesByLowerBound.values().iterator(); + } else { + Entry, Range> lowerEntry = + rangesByLowerBound.lowerEntry(upperBoundWindow.lowerEndpoint()); + if (lowerEntry == null) { + backingItr = rangesByLowerBound.values().iterator(); + } else if (upperBoundWindow.lowerBound.isLessThan(lowerEntry.getValue().upperBound)) { + backingItr = rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); + } else { + backingItr = + rangesByLowerBound + .tailMap(upperBoundWindow.lowerEndpoint(), true) + .values() + .iterator(); + } + } + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!backingItr.hasNext()) { + return endOfData(); + } + Range range = backingItr.next(); + if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { + return endOfData(); + } else { + return Maps.immutableEntry(range.upperBound, range); + } + } + }; + } + + @Override + Iterator, Range>> descendingEntryIterator() { + Collection> candidates; + if (upperBoundWindow.hasUpperBound()) { + candidates = + rangesByLowerBound + .headMap(upperBoundWindow.upperEndpoint(), false) + .descendingMap() + .values(); + } else { + candidates = rangesByLowerBound.descendingMap().values(); + } + PeekingIterator> backingItr = Iterators.peekingIterator(candidates.iterator()); + if (backingItr.hasNext() + && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { + backingItr.next(); + } + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!backingItr.hasNext()) { + return endOfData(); + } + Range range = backingItr.next(); + return upperBoundWindow.lowerBound.isLessThan(range.upperBound) + ? Maps.immutableEntry(range.upperBound, range) + : endOfData(); + } + }; + } + + @Override + public int size() { + if (upperBoundWindow.equals(Range.all())) { + return rangesByLowerBound.size(); + } + return Iterators.size(entryIterator()); + } + + @Override + public boolean isEmpty() { + return upperBoundWindow.equals(Range.all()) + ? rangesByLowerBound.isEmpty() + : !entryIterator().hasNext(); + } + } + + private static final class ComplementRangesByLowerBound> + extends AbstractNavigableMap, Range> { + private final NavigableMap, Range> positiveRangesByLowerBound; + private final NavigableMap, Range> positiveRangesByUpperBound; + + /** + * complementLowerBoundWindow represents the headMap/subMap/tailMap view of the entire + * "complement ranges by lower bound" map; it's a constraint on the *keys*, and does not affect + * the values. + */ + private final Range> complementLowerBoundWindow; + + ComplementRangesByLowerBound(NavigableMap, Range> positiveRangesByLowerBound) { + this(positiveRangesByLowerBound, Range.>all()); + } + + private ComplementRangesByLowerBound( + NavigableMap, Range> positiveRangesByLowerBound, Range> window) { + this.positiveRangesByLowerBound = positiveRangesByLowerBound; + this.positiveRangesByUpperBound = new RangesByUpperBound<>(positiveRangesByLowerBound); + this.complementLowerBoundWindow = window; + } + + private NavigableMap, Range> subMap(Range> subWindow) { + if (!complementLowerBoundWindow.isConnected(subWindow)) { + return ImmutableSortedMap.of(); + } else { + subWindow = subWindow.intersection(complementLowerBoundWindow); + return new ComplementRangesByLowerBound<>(positiveRangesByLowerBound, subWindow); + } + } + + @Override + public NavigableMap, Range> subMap( + Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { + return subMap( + Range.range( + fromKey, BoundType.forBoolean(fromInclusive), + toKey, BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + @Override + public Comparator> comparator() { + return Ordering.>natural(); + } + + @Override + Iterator, Range>> entryIterator() { + /* + * firstComplementRangeLowerBound is the first complement range lower bound inside + * complementLowerBoundWindow. Complement range lower bounds are either positive range upper + * bounds, or Cut.belowAll(). + * + * positiveItr starts at the first positive range with lower bound greater than + * firstComplementRangeLowerBound. (Positive range lower bounds correspond to complement range + * upper bounds.) + */ + Collection> positiveRanges; + if (complementLowerBoundWindow.hasLowerBound()) { + positiveRanges = + positiveRangesByUpperBound + .tailMap( + complementLowerBoundWindow.lowerEndpoint(), + complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED) + .values(); + } else { + positiveRanges = positiveRangesByUpperBound.values(); + } + PeekingIterator> positiveItr = Iterators.peekingIterator(positiveRanges.iterator()); + Cut firstComplementRangeLowerBound; + if (complementLowerBoundWindow.contains(Cut.belowAll()) + && (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.belowAll())) { + firstComplementRangeLowerBound = Cut.belowAll(); + } else if (positiveItr.hasNext()) { + firstComplementRangeLowerBound = positiveItr.next().upperBound; + } else { + return Iterators.emptyIterator(); + } + return new AbstractIterator, Range>>() { + Cut nextComplementRangeLowerBound = firstComplementRangeLowerBound; + + @Override + protected Entry, Range> computeNext() { + if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) + || nextComplementRangeLowerBound == Cut.aboveAll()) { + return endOfData(); + } + Range negativeRange; + if (positiveItr.hasNext()) { + Range positiveRange = positiveItr.next(); + negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); + nextComplementRangeLowerBound = positiveRange.upperBound; + } else { + negativeRange = Range.create(nextComplementRangeLowerBound, Cut.aboveAll()); + nextComplementRangeLowerBound = Cut.aboveAll(); + } + return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + } + }; + } + + @Override + Iterator, Range>> descendingEntryIterator() { + /* + * firstComplementRangeUpperBound is the upper bound of the last complement range with lower + * bound inside complementLowerBoundWindow. + * + * positiveItr starts at the first positive range with upper bound less than + * firstComplementRangeUpperBound. (Positive range upper bounds correspond to complement range + * lower bounds.) + */ + Cut startingPoint = + complementLowerBoundWindow.hasUpperBound() + ? complementLowerBoundWindow.upperEndpoint() + : Cut.aboveAll(); + boolean inclusive = + complementLowerBoundWindow.hasUpperBound() + && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; + PeekingIterator> positiveItr = + Iterators.peekingIterator( + positiveRangesByUpperBound + .headMap(startingPoint, inclusive) + .descendingMap() + .values() + .iterator()); + Cut cut; + if (positiveItr.hasNext()) { + cut = + (positiveItr.peek().upperBound == Cut.aboveAll()) + ? positiveItr.next().lowerBound + : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); + } else if (!complementLowerBoundWindow.contains(Cut.belowAll()) + || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { + return Iterators.emptyIterator(); + } else { + cut = positiveRangesByLowerBound.higherKey(Cut.belowAll()); + } + Cut firstComplementRangeUpperBound = MoreObjects.firstNonNull(cut, Cut.aboveAll()); + return new AbstractIterator, Range>>() { + Cut nextComplementRangeUpperBound = firstComplementRangeUpperBound; + + @Override + protected Entry, Range> computeNext() { + if (nextComplementRangeUpperBound == Cut.belowAll()) { + return endOfData(); + } else if (positiveItr.hasNext()) { + Range positiveRange = positiveItr.next(); + Range negativeRange = + Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); + nextComplementRangeUpperBound = positiveRange.lowerBound; + if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { + return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + } + } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.belowAll())) { + Range negativeRange = Range.create(Cut.belowAll(), nextComplementRangeUpperBound); + nextComplementRangeUpperBound = Cut.belowAll(); + return Maps.immutableEntry(Cut.belowAll(), negativeRange); + } + return endOfData(); + } + }; + } + + @Override + public int size() { + return Iterators.size(entryIterator()); + } + + @Override + public Range get(Object key) { + if (key instanceof Cut) { + try { + @SuppressWarnings("unchecked") + Cut cut = (Cut) key; + // tailMap respects the current window + Entry, Range> firstEntry = tailMap(cut, true).firstEntry(); + if (firstEntry != null && firstEntry.getKey().equals(cut)) { + return firstEntry.getValue(); + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + } + + private final class Complement extends TreeRangeSet { + Complement() { + super(new ComplementRangesByLowerBound(TreeRangeSet.this.rangesByLowerBound)); + } + + @Override + public void add(Range rangeToAdd) { + TreeRangeSet.this.remove(rangeToAdd); + } + + @Override + public void remove(Range rangeToRemove) { + TreeRangeSet.this.add(rangeToRemove); + } + + @Override + public boolean contains(C value) { + return !TreeRangeSet.this.contains(value); + } + + @Override + public RangeSet complement() { + return TreeRangeSet.this; + } + } + + private static final class SubRangeSetRangesByLowerBound> + extends AbstractNavigableMap, Range> { + /** + * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the keys, and does not + * affect the values. + */ + private final Range> lowerBoundWindow; + + /** + * restriction is the subRangeSet view; ranges are truncated to their intersection with + * restriction. + */ + private final Range restriction; + + private final NavigableMap, Range> rangesByLowerBound; + private final NavigableMap, Range> rangesByUpperBound; + + private SubRangeSetRangesByLowerBound( + Range> lowerBoundWindow, + Range restriction, + NavigableMap, Range> rangesByLowerBound) { + this.lowerBoundWindow = checkNotNull(lowerBoundWindow); + this.restriction = checkNotNull(restriction); + this.rangesByLowerBound = checkNotNull(rangesByLowerBound); + this.rangesByUpperBound = new RangesByUpperBound<>(rangesByLowerBound); + } + + private NavigableMap, Range> subMap(Range> window) { + if (!window.isConnected(lowerBoundWindow)) { + return ImmutableSortedMap.of(); + } else { + return new SubRangeSetRangesByLowerBound<>( + lowerBoundWindow.intersection(window), restriction, rangesByLowerBound); + } + } + + @Override + public NavigableMap, Range> subMap( + Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { + return subMap( + Range.range( + fromKey, + BoundType.forBoolean(fromInclusive), + toKey, + BoundType.forBoolean(toInclusive))); + } + + @Override + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + @Override + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + @Override + public Comparator> comparator() { + return Ordering.>natural(); + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public Range get(Object key) { + if (key instanceof Cut) { + try { + @SuppressWarnings("unchecked") // we catch CCE's + Cut cut = (Cut) key; + if (!lowerBoundWindow.contains(cut) + || cut.compareTo(restriction.lowerBound) < 0 + || cut.compareTo(restriction.upperBound) >= 0) { + return null; + } else if (cut.equals(restriction.lowerBound)) { + // it might be present, truncated on the left + Range candidate = Maps.valueOrNull(rangesByLowerBound.floorEntry(cut)); + if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { + return candidate.intersection(restriction); + } + } else { + Range result = rangesByLowerBound.get(cut); + if (result != null) { + return result.intersection(restriction); + } + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + @Override + Iterator, Range>> entryIterator() { + if (restriction.isEmpty()) { + return Iterators.emptyIterator(); + } + Iterator> completeRangeItr; + if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { + return Iterators.emptyIterator(); + } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { + // starts at the first range with upper bound strictly greater than restriction.lowerBound + completeRangeItr = + rangesByUpperBound.tailMap(restriction.lowerBound, false).values().iterator(); + } else { + // starts at the first range with lower bound above lowerBoundWindow.lowerBound + completeRangeItr = + rangesByLowerBound + .tailMap( + lowerBoundWindow.lowerBound.endpoint(), + lowerBoundWindow.lowerBoundType() == BoundType.CLOSED) + .values() + .iterator(); + } + Cut> upperBoundOnLowerBounds = + Ordering.>>natural() + .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!completeRangeItr.hasNext()) { + return endOfData(); + } + Range nextRange = completeRangeItr.next(); + if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { + return endOfData(); + } else { + nextRange = nextRange.intersection(restriction); + return Maps.immutableEntry(nextRange.lowerBound, nextRange); + } + } + }; + } + + @Override + Iterator, Range>> descendingEntryIterator() { + if (restriction.isEmpty()) { + return Iterators.emptyIterator(); + } + Cut> upperBoundOnLowerBounds = + Ordering.>>natural() + .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); + Iterator> completeRangeItr = + rangesByLowerBound + .headMap( + upperBoundOnLowerBounds.endpoint(), + upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) + .descendingMap() + .values() + .iterator(); + return new AbstractIterator, Range>>() { + @Override + protected Entry, Range> computeNext() { + if (!completeRangeItr.hasNext()) { + return endOfData(); + } + Range nextRange = completeRangeItr.next(); + if (restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { + return endOfData(); + } + nextRange = nextRange.intersection(restriction); + if (lowerBoundWindow.contains(nextRange.lowerBound)) { + return Maps.immutableEntry(nextRange.lowerBound, nextRange); + } else { + return endOfData(); + } + } + }; + } + + @Override + public int size() { + return Iterators.size(entryIterator()); + } + } + + @Override + public RangeSet subRangeSet(Range view) { + return view.equals(Range.all()) ? this : new SubRangeSet(view); + } + + private final class SubRangeSet extends TreeRangeSet { + private final Range restriction; + + SubRangeSet(Range restriction) { + super( + new SubRangeSetRangesByLowerBound( + Range.>all(), restriction, TreeRangeSet.this.rangesByLowerBound)); + this.restriction = restriction; + } + + @Override + public boolean encloses(Range range) { + if (!restriction.isEmpty() && restriction.encloses(range)) { + Range enclosing = TreeRangeSet.this.rangeEnclosing(range); + return enclosing != null && !enclosing.intersection(restriction).isEmpty(); + } + return false; + } + + @Override + public Range rangeContaining(C value) { + if (!restriction.contains(value)) { + return null; + } + Range result = TreeRangeSet.this.rangeContaining(value); + return (result == null) ? null : result.intersection(restriction); + } + + @Override + public void add(Range rangeToAdd) { + checkArgument( + restriction.encloses(rangeToAdd), + "Cannot add range %s to subRangeSet(%s)", + rangeToAdd, + restriction); + TreeRangeSet.this.add(rangeToAdd); + } + + @Override + public void remove(Range rangeToRemove) { + if (rangeToRemove.isConnected(restriction)) { + TreeRangeSet.this.remove(rangeToRemove.intersection(restriction)); + } + } + + @Override + public boolean contains(C value) { + return restriction.contains(value) && TreeRangeSet.this.contains(value); + } + + @Override + public void clear() { + TreeRangeSet.this.remove(restriction); + } + + @Override + public RangeSet subRangeSet(Range view) { + if (view.encloses(restriction)) { + return this; + } else if (view.isConnected(restriction)) { + return new SubRangeSet(restriction.intersection(view)); + } else { + return ImmutableRangeSet.of(); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeTraverser.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeTraverser.java new file mode 100644 index 0000000000000..6ce5157858b43 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/TreeTraverser.java @@ -0,0 +1,296 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.Queue; +import java.util.function.Consumer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Views elements of a type {@code T} as nodes in a tree, and provides methods to traverse the trees + * induced by this traverser. + * + *

For example, the tree + * + *

{@code
+ *        h
+ *      / | \
+ *     /  e  \
+ *    d       g
+ *   /|\      |
+ *  / | \     f
+ * a  b  c
+ * }
+ * + *

can be iterated over in preorder (hdabcegf), postorder (abcdefgh), or breadth-first order + * (hdegabcf). + * + *

Null nodes are strictly forbidden. + * + *

For Java 8 users: Because this is an abstract class, not an interface, you can't use a + * lambda expression to extend it: + * + *

{@code
+ * // won't work
+ * TreeTraverser traverser = node -> node.getChildNodes();
+ * }
+ * + * Instead, you can pass a lambda expression to the {@code using} factory method: + * + *
{@code
+ * TreeTraverser traverser = TreeTraverser.using(node -> node.getChildNodes());
+ * }
+ * + * @author Louis Wasserman + * @since 15.0 + * @deprecated Use {@link org.apache.iotdb.google.common.graph.Traverser} instead. All instance + * methods have their equivalent on the result of {@code Traverser.forTree(tree)} where {@code + * tree} implements {@code SuccessorsFunction}, which has a similar API as {@link #children} or + * can be the same lambda function as passed into {@link #using(Function)}. + *

This class is scheduled to be removed in October 2019. + */ +// TODO(b/68134636): Remove by 2019-10 +@Deprecated +@Beta +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class TreeTraverser { + + /** + * Returns a tree traverser that uses the given function to navigate from a node to its children. + * This is useful if the function instance already exists, or so that you can supply a lambda + * expressions. If those circumstances don't apply, you probably don't need to use this; subclass + * {@code TreeTraverser} and implement its {@link #children} method directly. + * + * @since 20.0 + * @deprecated Use {@link org.apache.iotdb.google.common.graph.Traverser#forTree} instead. If you + * are using a lambda, these methods have exactly the same signature. + */ + @Deprecated + public static TreeTraverser using( + final Function> nodeToChildrenFunction) { + checkNotNull(nodeToChildrenFunction); + return new TreeTraverser() { + @Override + public Iterable children(T root) { + return nodeToChildrenFunction.apply(root); + } + }; + } + + /** Returns the children of the specified node. Must not contain null. */ + public abstract Iterable children(T root); + + /** + * Returns an unmodifiable iterable over the nodes in a tree structure, using pre-order traversal. + * That is, each node's subtrees are traversed after the node itself is returned. + * + *

No guarantees are made about the behavior of the traversal when nodes change while iteration + * is in progress or when the iterators generated by {@link #children} are advanced. + * + * @deprecated Use {@link org.apache.iotdb.google.common.graph.Traverser#depthFirstPreOrder} + * instead, which has the same behavior. + */ + @Deprecated + public final FluentIterable preOrderTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return preOrderIterator(root); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + new Consumer() { + @Override + public void accept(T t) { + action.accept(t); + children(t).forEach(this); + } + }.accept(root); + } + }; + } + + UnmodifiableIterator preOrderIterator(T root) { + return new PreOrderIterator(root); + } + + private final class PreOrderIterator extends UnmodifiableIterator { + private final Deque> stack; + + PreOrderIterator(T root) { + this.stack = new ArrayDeque<>(); + stack.addLast(Iterators.singletonIterator(checkNotNull(root))); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } + + @Override + public T next() { + Iterator itr = stack.getLast(); // throws NSEE if empty + T result = checkNotNull(itr.next()); + if (!itr.hasNext()) { + stack.removeLast(); + } + Iterator childItr = children(result).iterator(); + if (childItr.hasNext()) { + stack.addLast(childItr); + } + return result; + } + } + + /** + * Returns an unmodifiable iterable over the nodes in a tree structure, using post-order + * traversal. That is, each node's subtrees are traversed before the node itself is returned. + * + *

No guarantees are made about the behavior of the traversal when nodes change while iteration + * is in progress or when the iterators generated by {@link #children} are advanced. + * + * @deprecated Use {@link org.apache.iotdb.google.common.graph.Traverser#depthFirstPostOrder} + * instead, which has the same behavior. + */ + @Deprecated + public final FluentIterable postOrderTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return postOrderIterator(root); + } + + @Override + public void forEach(Consumer action) { + checkNotNull(action); + new Consumer() { + @Override + public void accept(T t) { + children(t).forEach(this); + action.accept(t); + } + }.accept(root); + } + }; + } + + UnmodifiableIterator postOrderIterator(T root) { + return new PostOrderIterator(root); + } + + private static final class PostOrderNode { + final T root; + final Iterator childIterator; + + PostOrderNode(T root, Iterator childIterator) { + this.root = checkNotNull(root); + this.childIterator = checkNotNull(childIterator); + } + } + + private final class PostOrderIterator extends AbstractIterator { + private final ArrayDeque> stack; + + PostOrderIterator(T root) { + this.stack = new ArrayDeque<>(); + stack.addLast(expand(root)); + } + + @Override + protected T computeNext() { + while (!stack.isEmpty()) { + PostOrderNode top = stack.getLast(); + if (top.childIterator.hasNext()) { + T child = top.childIterator.next(); + stack.addLast(expand(child)); + } else { + stack.removeLast(); + return top.root; + } + } + return endOfData(); + } + + private PostOrderNode expand(T t) { + return new PostOrderNode(t, children(t).iterator()); + } + } + + /** + * Returns an unmodifiable iterable over the nodes in a tree structure, using breadth-first + * traversal. That is, all the nodes of depth 0 are returned, then depth 1, then 2, and so on. + * + *

No guarantees are made about the behavior of the traversal when nodes change while iteration + * is in progress or when the iterators generated by {@link #children} are advanced. + * + * @deprecated Use {@link org.apache.iotdb.google.common.graph.Traverser#breadthFirst} instead, + * which has the same behavior. + */ + @Deprecated + public final FluentIterable breadthFirstTraversal(final T root) { + checkNotNull(root); + return new FluentIterable() { + @Override + public UnmodifiableIterator iterator() { + return new BreadthFirstIterator(root); + } + }; + } + + private final class BreadthFirstIterator extends UnmodifiableIterator + implements PeekingIterator { + private final Queue queue; + + BreadthFirstIterator(T root) { + this.queue = new ArrayDeque(); + queue.add(root); + } + + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + public T peek() { + return queue.element(); + } + + @Override + public T next() { + T result = queue.remove(); + Iterables.addAll(queue, children(result)); + return result; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableIterator.java new file mode 100644 index 0000000000000..67e4d77f931a8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableIterator.java @@ -0,0 +1,53 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Iterator; + +/** + * An iterator that does not support {@link #remove}. + * + *

{@code UnmodifiableIterator} is used primarily in conjunction with implementations of {@link + * ImmutableCollection}, such as {@link ImmutableList}. You can, however, convert an existing + * iterator to an {@code UnmodifiableIterator} using {@link Iterators#unmodifiableIterator}. + * + * @author Jared Levy + * @since 2.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class UnmodifiableIterator implements Iterator { + /** Constructor for use by subclasses. */ + protected UnmodifiableIterator() {} + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableListIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableListIterator.java new file mode 100644 index 0000000000000..b163edc8d13bf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableListIterator.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.ListIterator; + +/** + * A list iterator that does not support {@link #remove}, {@link #add}, or {@link #set}. + * + * @since 7.0 + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class UnmodifiableListIterator extends UnmodifiableIterator + implements ListIterator { + /** Constructor for use by subclasses. */ + protected UnmodifiableListIterator() {} + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void add(@ParametricNullness E e) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void set(@ParametricNullness E e) { + throw new UnsupportedOperationException(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableSortedMultiset.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableSortedMultiset.java new file mode 100644 index 0000000000000..7d3693697e7fe --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UnmodifiableSortedMultiset.java @@ -0,0 +1,117 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.Multisets.UnmodifiableMultiset; + +import java.util.Comparator; +import java.util.NavigableSet; + +/** + * Implementation of {@link Multisets#unmodifiableSortedMultiset(SortedMultiset)}, split out into + * its own file so it can be GWT emulated (to deal with the differing elementSet() types in GWT and + * non-GWT). + * + * @author Louis Wasserman + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class UnmodifiableSortedMultiset extends UnmodifiableMultiset + implements SortedMultiset { + UnmodifiableSortedMultiset(SortedMultiset delegate) { + super(delegate); + } + + @Override + protected SortedMultiset delegate() { + return (SortedMultiset) super.delegate(); + } + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + NavigableSet createElementSet() { + return Sets.unmodifiableNavigableSet(delegate().elementSet()); + } + + @Override + public NavigableSet elementSet() { + return (NavigableSet) super.elementSet(); + } + + private transient UnmodifiableSortedMultiset descendingMultiset; + + @Override + public SortedMultiset descendingMultiset() { + UnmodifiableSortedMultiset result = descendingMultiset; + if (result == null) { + result = new UnmodifiableSortedMultiset<>(delegate().descendingMultiset()); + result.descendingMultiset = this; + return descendingMultiset = result; + } + return result; + } + + @Override + public Entry firstEntry() { + return delegate().firstEntry(); + } + + @Override + public Entry lastEntry() { + return delegate().lastEntry(); + } + + @Override + public Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + @Override + public SortedMultiset headMultiset(@ParametricNullness E upperBound, BoundType boundType) { + return Multisets.unmodifiableSortedMultiset(delegate().headMultiset(upperBound, boundType)); + } + + @Override + public SortedMultiset subMultiset( + @ParametricNullness E lowerBound, + BoundType lowerBoundType, + @ParametricNullness E upperBound, + BoundType upperBoundType) { + return Multisets.unmodifiableSortedMultiset( + delegate().subMultiset(lowerBound, lowerBoundType, upperBound, upperBoundType)); + } + + @Override + public SortedMultiset tailMultiset(@ParametricNullness E lowerBound, BoundType boundType) { + return Multisets.unmodifiableSortedMultiset(delegate().tailMultiset(lowerBound, boundType)); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UsingToStringOrdering.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UsingToStringOrdering.java new file mode 100644 index 0000000000000..39414829baf32 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/UsingToStringOrdering.java @@ -0,0 +1,50 @@ +/* + * 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.iotdb.google.common.collect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** An ordering that uses the natural order of the string representation of the values. */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +final class UsingToStringOrdering extends Ordering implements Serializable { + static final UsingToStringOrdering INSTANCE = new UsingToStringOrdering(); + + @Override + public int compare(Object left, Object right) { + return left.toString().compareTo(right.toString()); + } + + // preserve singleton-ness, so equals() and hashCode() work correctly + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.usingToString()"; + } + + private UsingToStringOrdering() {} + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/package-info.java new file mode 100644 index 0000000000000..87e87d78fb512 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/collect/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.collect; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedCharEscaper.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedCharEscaper.java new file mode 100644 index 0000000000000..9f29a6a9ff63e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedCharEscaper.java @@ -0,0 +1,156 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link CharEscaper} that uses an array to quickly look up replacement characters for a given + * {@code char} value. An additional safe range is provided that determines whether {@code char} + * values without specific replacements are to be considered safe and left unescaped or should be + * escaped in a general way. + * + *

A good example of usage of this class is for Java source code escaping where the replacement + * array contains information about special ASCII characters such as {@code \\t} and {@code \\n} + * while {@link #escapeUnsafe} is overridden to handle general escaping of the form {@code \\uxxxx}. + * + *

The size of the data structure used by {@link ArrayBasedCharEscaper} is proportional to the + * highest valued character that requires escaping. For example a replacement map containing the + * single character '{@code \}{@code u1000}' will require approximately 16K of memory. If you need + * to create multiple escaper instances that have the same character replacement mapping consider + * using {@link ArrayBasedEscaperMap}. + * + * @author Sven Mawson + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ArrayBasedCharEscaper extends CharEscaper { + // The replacement array (see ArrayBasedEscaperMap). + private final char[][] replacements; + // The number of elements in the replacement array. + private final int replacementsLength; + // The first character in the safe range. + private final char safeMin; + // The last character in the safe range. + private final char safeMax; + + /** + * Creates a new ArrayBasedCharEscaper instance with the given replacement map and specified safe + * range. If {@code safeMax < safeMin} then no characters are considered safe. + * + *

If a character has no mapped replacement then it is checked against the safe range. If it + * lies outside that, then {@link #escapeUnsafe} is called, otherwise no escaping is performed. + * + * @param replacementMap a map of characters to their escaped representations + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + */ + protected ArrayBasedCharEscaper( + Map replacementMap, char safeMin, char safeMax) { + + this(ArrayBasedEscaperMap.create(replacementMap), safeMin, safeMax); + } + + /** + * Creates a new ArrayBasedCharEscaper instance with the given replacement map and specified safe + * range. If {@code safeMax < safeMin} then no characters are considered safe. This initializer is + * useful when explicit instances of ArrayBasedEscaperMap are used to allow the sharing of large + * replacement mappings. + * + *

If a character has no mapped replacement then it is checked against the safe range. If it + * lies outside that, then {@link #escapeUnsafe} is called, otherwise no escaping is performed. + * + * @param escaperMap the mapping of characters to be escaped + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + */ + protected ArrayBasedCharEscaper(ArrayBasedEscaperMap escaperMap, char safeMin, char safeMax) { + + checkNotNull(escaperMap); // GWT specific check (do not optimize) + this.replacements = escaperMap.getReplacementArray(); + this.replacementsLength = replacements.length; + if (safeMax < safeMin) { + // If the safe range is empty, set the range limits to opposite extremes + // to ensure the first test of either value will (almost certainly) fail. + safeMax = Character.MIN_VALUE; + safeMin = Character.MAX_VALUE; + } + this.safeMin = safeMin; + this.safeMax = safeMax; + } + + /* + * This is overridden to improve performance. Rough benchmarking shows that this almost doubles + * the speed when processing strings that do not require any escaping. + */ + @Override + public final String escape(String s) { + checkNotNull(s); // GWT specific check (do not optimize). + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c < replacementsLength && replacements[c] != null) || c > safeMax || c < safeMin) { + return escapeSlow(s, i); + } + } + return s; + } + + /** + * Escapes a single character using the replacement array and safe range values. If the given + * character does not have an explicit replacement and lies outside the safe range then {@link + * #escapeUnsafe} is called. + * + * @return the replacement characters, or {@code null} if no escaping was required + */ + @Override + protected final char[] escape(char c) { + if (c < replacementsLength) { + char[] chars = replacements[c]; + if (chars != null) { + return chars; + } + } + if (c >= safeMin && c <= safeMax) { + return null; + } + return escapeUnsafe(c); + } + + /** + * Escapes a {@code char} value that has no direct explicit value in the replacement array and + * lies outside the stated safe range. Subclasses should override this method to provide + * generalized escaping for characters. + * + *

Note that arrays returned by this method must not be modified once they have been returned. + * However it is acceptable to return the same array multiple times (even for different input + * characters). + * + * @param c the character to escape + * @return the replacement characters, or {@code null} if no escaping was required + */ + // TODO(dbeaumont,cpovirk): Rename this something better once refactoring done + protected abstract char[] escapeUnsafe(char c); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedEscaperMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedEscaperMap.java new file mode 100644 index 0000000000000..0debdd0348105 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedEscaperMap.java @@ -0,0 +1,89 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.Collections; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation-specific parameter class suitable for initializing {@link + * ArrayBasedCharEscaper} or {@link ArrayBasedUnicodeEscaper} instances. This class should be used + * when more than one escaper is created using the same character replacement mapping to allow the + * underlying (implementation specific) data structures to be shared. + * + *

The size of the data structure used by ArrayBasedCharEscaper and ArrayBasedUnicodeEscaper is + * proportional to the highest valued character that has a replacement. For example a replacement + * map containing the single character '{@literal \}u1000' will require approximately 16K of memory. + * As such sharing this data structure between escaper instances is the primary goal of this class. + * + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class ArrayBasedEscaperMap { + /** + * Returns a new ArrayBasedEscaperMap for creating ArrayBasedCharEscaper or + * ArrayBasedUnicodeEscaper instances. + * + * @param replacements a map of characters to their escaped representations + */ + public static ArrayBasedEscaperMap create(Map replacements) { + return new ArrayBasedEscaperMap(createReplacementArray(replacements)); + } + + // The underlying replacement array we can share between multiple escaper + // instances. + private final char[][] replacementArray; + + private ArrayBasedEscaperMap(char[][] replacementArray) { + this.replacementArray = replacementArray; + } + + // Returns the non-null array of replacements for fast lookup. + char[][] getReplacementArray() { + return replacementArray; + } + + // Creates a replacement array from the given map. The returned array is a + // linear lookup table of replacement character sequences indexed by the + // original character value. + @VisibleForTesting + static char[][] createReplacementArray(Map map) { + checkNotNull(map); // GWT specific check (do not optimize) + if (map.isEmpty()) { + return EMPTY_REPLACEMENT_ARRAY; + } + char max = Collections.max(map.keySet()); + char[][] replacements = new char[max + 1][]; + for (Character c : map.keySet()) { + replacements[c] = map.get(c).toCharArray(); + } + return replacements; + } + + // Immutable empty array for when there are no replacements. + private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0]; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedUnicodeEscaper.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedUnicodeEscaper.java new file mode 100644 index 0000000000000..30da300a4c8f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ArrayBasedUnicodeEscaper.java @@ -0,0 +1,203 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link UnicodeEscaper} that uses an array to quickly look up replacement characters for a given + * code point. An additional safe range is provided that determines whether code points without + * specific replacements are to be considered safe and left unescaped or should be escaped in a + * general way. + * + *

A good example of usage of this class is for HTML escaping where the replacement array + * contains information about the named HTML entities such as {@code &} and {@code "} while + * {@link #escapeUnsafe} is overridden to handle general escaping of the form {@code &#NNNNN;}. + * + *

The size of the data structure used by {@link ArrayBasedUnicodeEscaper} is proportional to the + * highest valued code point that requires escaping. For example a replacement map containing the + * single character '{@code \}{@code u1000}' will require approximately 16K of memory. If you need + * to create multiple escaper instances that have the same character replacement mapping consider + * using {@link ArrayBasedEscaperMap}. + * + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ArrayBasedUnicodeEscaper extends UnicodeEscaper { + // The replacement array (see ArrayBasedEscaperMap). + private final char[][] replacements; + // The number of elements in the replacement array. + private final int replacementsLength; + // The first code point in the safe range. + private final int safeMin; + // The last code point in the safe range. + private final int safeMax; + + // Cropped values used in the fast path range checks. + private final char safeMinChar; + private final char safeMaxChar; + + /** + * Creates a new ArrayBasedUnicodeEscaper instance with the given replacement map and specified + * safe range. If {@code safeMax < safeMin} then no code points are considered safe. + * + *

If a code point has no mapped replacement then it is checked against the safe range. If it + * lies outside that, then {@link #escapeUnsafe} is called, otherwise no escaping is performed. + * + * @param replacementMap a map of characters to their escaped representations + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + * @param unsafeReplacement the default replacement for unsafe characters or null if no default + * replacement is required + */ + protected ArrayBasedUnicodeEscaper( + Map replacementMap, int safeMin, int safeMax, String unsafeReplacement) { + this(ArrayBasedEscaperMap.create(replacementMap), safeMin, safeMax, unsafeReplacement); + } + + /** + * Creates a new ArrayBasedUnicodeEscaper instance with the given replacement map and specified + * safe range. If {@code safeMax < safeMin} then no code points are considered safe. This + * initializer is useful when explicit instances of ArrayBasedEscaperMap are used to allow the + * sharing of large replacement mappings. + * + *

If a code point has no mapped replacement then it is checked against the safe range. If it + * lies outside that, then {@link #escapeUnsafe} is called, otherwise no escaping is performed. + * + * @param escaperMap the map of replacements + * @param safeMin the lowest character value in the safe range + * @param safeMax the highest character value in the safe range + * @param unsafeReplacement the default replacement for unsafe characters or null if no default + * replacement is required + */ + protected ArrayBasedUnicodeEscaper( + ArrayBasedEscaperMap escaperMap, int safeMin, int safeMax, String unsafeReplacement) { + checkNotNull(escaperMap); // GWT specific check (do not optimize) + this.replacements = escaperMap.getReplacementArray(); + this.replacementsLength = replacements.length; + if (safeMax < safeMin) { + // If the safe range is empty, set the range limits to opposite extremes + // to ensure the first test of either value will fail. + safeMax = -1; + safeMin = Integer.MAX_VALUE; + } + this.safeMin = safeMin; + this.safeMax = safeMax; + + // This is a bit of a hack but lets us do quicker per-character checks in + // the fast path code. The safe min/max values are very unlikely to extend + // into the range of surrogate characters, but if they do we must not test + // any values in that range. To see why, consider the case where: + // safeMin <= {hi,lo} <= safeMax + // where {hi,lo} are characters forming a surrogate pair such that: + // codePointOf(hi, lo) > safeMax + // which would result in the surrogate pair being (wrongly) considered safe. + // If we clip the safe range used during the per-character tests so it is + // below the values of characters in surrogate pairs, this cannot occur. + // This approach does mean that we break out of the fast path code in cases + // where we don't strictly need to, but this situation will almost never + // occur in practice. + if (safeMin >= Character.MIN_HIGH_SURROGATE) { + // The safe range is empty or the all safe code points lie in or above the + // surrogate range. Either way the character range is empty. + this.safeMinChar = Character.MAX_VALUE; + this.safeMaxChar = 0; + } else { + // The safe range is non-empty and contains values below the surrogate + // range but may extend above it. We may need to clip the maximum value. + this.safeMinChar = (char) safeMin; + this.safeMaxChar = (char) Math.min(safeMax, Character.MIN_HIGH_SURROGATE - 1); + } + } + + /* + * This is overridden to improve performance. Rough benchmarking shows that this almost doubles + * the speed when processing strings that do not require any escaping. + */ + @Override + public final String escape(String s) { + checkNotNull(s); // GWT specific check (do not optimize) + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c < replacementsLength && replacements[c] != null) + || c > safeMaxChar + || c < safeMinChar) { + return escapeSlow(s, i); + } + } + return s; + } + + /** + * Escapes a single Unicode code point using the replacement array and safe range values. If the + * given character does not have an explicit replacement and lies outside the safe range then + * {@link #escapeUnsafe} is called. + * + * @return the replacement characters, or {@code null} if no escaping was required + */ + @Override + protected final char[] escape(int cp) { + if (cp < replacementsLength) { + char[] chars = replacements[cp]; + if (chars != null) { + return chars; + } + } + if (cp >= safeMin && cp <= safeMax) { + return null; + } + return escapeUnsafe(cp); + } + + /* Overridden for performance. */ + @Override + protected final int nextEscapeIndex(CharSequence csq, int index, int end) { + while (index < end) { + char c = csq.charAt(index); + if ((c < replacementsLength && replacements[c] != null) + || c > safeMaxChar + || c < safeMinChar) { + break; + } + index++; + } + return index; + } + + /** + * Escapes a code point that has no direct explicit value in the replacement array and lies + * outside the stated safe range. Subclasses should override this method to provide generalized + * escaping for code points if required. + * + *

Note that arrays returned by this method must not be modified once they have been returned. + * However it is acceptable to return the same array multiple times (even for different input + * characters). + * + * @param cp the Unicode code point to escape + * @return the replacement characters, or {@code null} if no escaping was required + */ + protected abstract char[] escapeUnsafe(int cp); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/CharEscaper.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/CharEscaper.java new file mode 100644 index 0000000000000..30c967ebd234b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/CharEscaper.java @@ -0,0 +1,180 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An object that converts literal text into a format safe for inclusion in a particular context + * (such as an XML document). Typically (but not always), the inverse process of "unescaping" the + * text is performed automatically by the relevant parser. + * + *

For example, an XML escaper would convert the literal string {@code "Foo"} into {@code + * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML tag. When the + * resulting XML document is parsed, the parser API will return this text as the original literal + * string {@code "Foo"}. + * + *

A {@code CharEscaper} instance is required to be stateless, and safe when used concurrently by + * multiple threads. + * + *

Popular escapers are defined as constants in classes like {@link + * org.apache.iotdb.google.common.html.HtmlEscapers} and {@link + * org.apache.iotdb.google.common.xml.XmlEscapers}. To create your own escapers extend this class + * and implement the {@link #escape(char)} method. + * + * @author Sven Mawson + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class CharEscaper extends Escaper { + /** Constructor for use by subclasses. */ + protected CharEscaper() {} + + /** + * Returns the escaped form of a given literal string. + * + * @param string the literal string to be escaped + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + */ + @Override + public String escape(String string) { + checkNotNull(string); // GWT specific check (do not optimize) + // Inlineable fast-path loop which hands off to escapeSlow() only if needed + int length = string.length(); + for (int index = 0; index < length; index++) { + if (escape(string.charAt(index)) != null) { + return escapeSlow(string, index); + } + } + return string; + } + + /** + * Returns the escaped form of the given character, or {@code null} if this character does not + * need to be escaped. If an empty array is returned, this effectively strips the input character + * from the resulting text. + * + *

If the character does not need to be escaped, this method should return {@code null}, rather + * than a one-character array containing the character itself. This enables the escaping algorithm + * to perform more efficiently. + * + *

An escaper is expected to be able to deal with any {@code char} value, so this method should + * not throw any exceptions. + * + * @param c the character to escape if necessary + * @return the replacement characters, or {@code null} if no escaping was needed + */ + protected abstract char[] escape(char c); + + /** + * Returns the escaped form of a given literal string, starting at the given index. This method is + * called by the {@link #escape(String)} method when it discovers that escaping is required. It is + * protected to allow subclasses to override the fastpath escaping function to inline their + * escaping test. See {@link CharEscaperBuilder} for an example usage. + * + * @param s the literal string to be escaped + * @param index the index to start escaping from + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + */ + protected final String escapeSlow(String s, int index) { + int slen = s.length(); + + // Get a destination buffer and setup some loop variables. + char[] dest = Platform.charBufferFromThreadLocal(); + int destSize = dest.length; + int destIndex = 0; + int lastEscape = 0; + + // Loop through the rest of the string, replacing when needed into the + // destination buffer, which gets grown as needed as well. + for (; index < slen; index++) { + + // Get a replacement for the current character. + char[] r = escape(s.charAt(index)); + + // If no replacement is needed, just continue. + if (r == null) { + continue; + } + + int rlen = r.length; + int charsSkipped = index - lastEscape; + + // This is the size needed to add the replacement, not the full size + // needed by the string. We only regrow when we absolutely must, and + // when we do grow, grow enough to avoid excessive growing. Grow. + int sizeNeeded = destIndex + charsSkipped + rlen; + if (destSize < sizeNeeded) { + destSize = sizeNeeded + DEST_PAD_MULTIPLIER * (slen - index); + dest = growBuffer(dest, destIndex, destSize); + } + + // If we have skipped any characters, we need to copy them now. + if (charsSkipped > 0) { + s.getChars(lastEscape, index, dest, destIndex); + destIndex += charsSkipped; + } + + // Copy the replacement string into the dest buffer as needed. + if (rlen > 0) { + System.arraycopy(r, 0, dest, destIndex, rlen); + destIndex += rlen; + } + lastEscape = index + 1; + } + + // Copy leftover characters if there are any. + int charsLeft = slen - lastEscape; + if (charsLeft > 0) { + int sizeNeeded = destIndex + charsLeft; + if (destSize < sizeNeeded) { + + // Regrow and copy, expensive! No padding as this is the final copy. + dest = growBuffer(dest, destIndex, sizeNeeded); + } + s.getChars(lastEscape, slen, dest, destIndex); + destIndex = sizeNeeded; + } + return new String(dest, 0, destIndex); + } + + /** + * Helper method to grow the character buffer as needed, this only happens once in a while so it's + * ok if it's in a method call. If the index passed in is 0 then no copying will be done. + */ + private static char[] growBuffer(char[] dest, int index, int size) { + if (size < 0) { // overflow - should be OutOfMemoryError but GWT/j2cl don't support it + throw new AssertionError("Cannot increase internal buffer any further"); + } + char[] copy = new char[size]; + if (index > 0) { + System.arraycopy(dest, 0, copy, 0, index); + } + return copy; + } + + /** The multiplier for padding to use when growing the escape buffer. */ + private static final int DEST_PAD_MULTIPLIER = 2; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/CharEscaperBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/CharEscaperBuilder.java new file mode 100644 index 0000000000000..500eaa761fb69 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/CharEscaperBuilder.java @@ -0,0 +1,130 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Simple helper class to build a "sparse" array of objects based on the indexes that were added to + * it. The array will be from 0 to the maximum index given. All non-set indexes will contain null + * (so it's not really a sparse array, just a pseudo sparse array). The builder can also return a + * CharEscaper based on the generated array. + * + * @author Sven Mawson + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class CharEscaperBuilder { + /** + * Simple decorator that turns an array of replacement char[]s into a CharEscaper, this results in + * a very fast escape method. + */ + private static class CharArrayDecorator extends CharEscaper { + private final char[][] replacements; + private final int replaceLength; + + CharArrayDecorator(char[][] replacements) { + this.replacements = replacements; + this.replaceLength = replacements.length; + } + + /* + * Overriding escape method to be slightly faster for this decorator. We test the replacements + * array directly, saving a method call. + */ + @Override + public String escape(String s) { + int slen = s.length(); + for (int index = 0; index < slen; index++) { + char c = s.charAt(index); + if (c < replacements.length && replacements[c] != null) { + return escapeSlow(s, index); + } + } + return s; + } + + @Override + protected char[] escape(char c) { + return c < replaceLength ? replacements[c] : null; + } + } + + // Replacement mappings. + private final Map map; + + // The highest index we've seen so far. + private int max = -1; + + /** Construct a new sparse array builder. */ + public CharEscaperBuilder() { + this.map = new HashMap<>(); + } + + /** Add a new mapping from an index to an object to the escaping. */ + public CharEscaperBuilder addEscape(char c, String r) { + map.put(c, checkNotNull(r)); + if (c > max) { + max = c; + } + return this; + } + + /** Add multiple mappings at once for a particular index. */ + public CharEscaperBuilder addEscapes(char[] cs, String r) { + checkNotNull(r); + for (char c : cs) { + addEscape(c, r); + } + return this; + } + + /** + * Convert this builder into an array of char[]s where the maximum index is the value of the + * highest character that has been seen. The array will be sparse in the sense that any unseen + * index will default to null. + * + * @return a "sparse" array that holds the replacement mappings. + */ + public char[][] toArray() { + char[][] result = new char[max + 1][]; + for (Entry entry : map.entrySet()) { + result[entry.getKey()] = entry.getValue().toCharArray(); + } + return result; + } + + /** + * Convert this builder into a char escaper which is just a decorator around the underlying array + * of replacement char[]s. + * + * @return an escaper that escapes based on the underlying array. + */ + public Escaper toEscaper() { + return new CharArrayDecorator(toArray()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..135f53cc50fcc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Escaper.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Escaper.java new file mode 100644 index 0000000000000..4a0b421f8d3ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Escaper.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; + +/** + * An object that converts literal text into a format safe for inclusion in a particular context + * (such as an XML document). Typically (but not always), the inverse process of "unescaping" the + * text is performed automatically by the relevant parser. + * + *

For example, an XML escaper would convert the literal string {@code "Foo"} into {@code + * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML tag. When the + * resulting XML document is parsed, the parser API will return this text as the original literal + * string {@code "Foo"}. + * + *

An {@code Escaper} instance is required to be stateless, and safe when used concurrently by + * multiple threads. + * + *

Because, in general, escaping operates on the code points of a string and not on its + * individual {@code char} values, it is not safe to assume that {@code escape(s)} is equivalent to + * {@code escape(s.substring(0, n)) + escape(s.substring(n))} for arbitrary {@code n}. This is + * because of the possibility of splitting a surrogate pair. The only case in which it is safe to + * escape strings and concatenate the results is if you can rule out this possibility, either by + * splitting an existing long string into short strings adaptively around {@linkplain + * Character#isHighSurrogate surrogate} {@linkplain Character#isLowSurrogate pairs}, or by starting + * with short strings already known to be free of unpaired surrogates. + * + *

The two primary implementations of this interface are {@link CharEscaper} and {@link + * UnicodeEscaper}. They are heavily optimized for performance and greatly simplify the task of + * implementing new escapers. It is strongly recommended that when implementing a new escaper you + * extend one of these classes. If you find that you are unable to achieve the desired behavior + * using either of these classes, please contact the Java libraries team for advice. + * + *

Popular escapers are defined as constants in classes like {@link + * org.apache.iotdb.google.common.html.HtmlEscapers} and {@link + * org.apache.iotdb.google.common.xml.XmlEscapers}. To create your own escapers, use {@link + * CharEscaperBuilder}, or extend {@code CharEscaper} or {@code UnicodeEscaper}. + * + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class Escaper { + // TODO(dbeaumont): evaluate custom implementations, considering package private constructor. + /** Constructor for use by subclasses. */ + protected Escaper() {} + + /** + * Returns the escaped form of a given literal string. + * + *

Note that this method may treat input characters differently depending on the specific + * escaper implementation. + * + *

    + *
  • {@link UnicodeEscaper} handles UTF-16 + * correctly, including surrogate character pairs. If the input is badly formed the escaper + * should throw {@link IllegalArgumentException}. + *
  • {@link CharEscaper} handles Java characters independently and does not verify the input + * for well formed characters. A {@code CharEscaper} should not be used in situations where + * input is not guaranteed to be restricted to the Basic Multilingual Plane (BMP). + *
+ * + * @param string the literal string to be escaped + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + * @throws IllegalArgumentException if {@code string} contains badly formed UTF-16 or cannot be + * escaped for any other reason + */ + public abstract String escape(String string); + + private final Function asFunction = this::escape; + + /** Returns a {@link Function} that invokes {@link #escape(String)} on this escaper. */ + public final Function asFunction() { + return asFunction; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Escapers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Escapers.java new file mode 100644 index 0000000000000..9ee9054519039 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Escapers.java @@ -0,0 +1,270 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@link Escaper} instances. + * + * @author Sven Mawson + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Escapers { + private Escapers() {} + + /** + * Returns an {@link Escaper} that does no escaping, passing all character data through unchanged. + */ + public static Escaper nullEscaper() { + return NULL_ESCAPER; + } + + // An Escaper that efficiently performs no escaping. + // Extending CharEscaper (instead of Escaper) makes Escapers.compose() easier. + private static final Escaper NULL_ESCAPER = + new CharEscaper() { + @Override + public String escape(String string) { + return checkNotNull(string); + } + + @Override + protected char[] escape(char c) { + // TODO: Fix tests not to call this directly and make it throw an error. + return null; + } + }; + + /** + * Returns a builder for creating simple, fast escapers. A builder instance can be reused and each + * escaper that is created will be a snapshot of the current builder state. Builders are not + * thread safe. + * + *

The initial state of the builder is such that: + * + *

    + *
  • There are no replacement mappings + *
  • {@code safeMin == Character.MIN_VALUE} + *
  • {@code safeMax == Character.MAX_VALUE} + *
  • {@code unsafeReplacement == null} + *
+ * + *

For performance reasons escapers created by this builder are not Unicode aware and will not + * validate the well-formedness of their input. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for simple, fast escapers. + * + *

Typically an escaper needs to deal with the escaping of high valued characters or code + * points. In these cases it is necessary to extend either {@link ArrayBasedCharEscaper} or {@link + * ArrayBasedUnicodeEscaper} to provide the desired behavior. However this builder is suitable for + * creating escapers that replace a relative small set of characters. + * + * @author David Beaumont + * @since 15.0 + */ + public static final class Builder { + private final Map replacementMap = new HashMap<>(); + private char safeMin = Character.MIN_VALUE; + private char safeMax = Character.MAX_VALUE; + private String unsafeReplacement = null; + + // The constructor is exposed via the builder() method above. + private Builder() {} + + /** + * Sets the safe range of characters for the escaper. Characters in this range that have no + * explicit replacement are considered 'safe' and remain unescaped in the output. If {@code + * safeMax < safeMin} then the safe range is empty. + * + * @param safeMin the lowest 'safe' character + * @param safeMax the highest 'safe' character + * @return the builder instance + */ + public Builder setSafeRange(char safeMin, char safeMax) { + this.safeMin = safeMin; + this.safeMax = safeMax; + return this; + } + + /** + * Sets the replacement string for any characters outside the 'safe' range that have no explicit + * replacement. If {@code unsafeReplacement} is {@code null} then no replacement will occur, if + * it is {@code ""} then the unsafe characters are removed from the output. + * + * @param unsafeReplacement the string to replace unsafe characters + * @return the builder instance + */ + public Builder setUnsafeReplacement(String unsafeReplacement) { + this.unsafeReplacement = unsafeReplacement; + return this; + } + + /** + * Adds a replacement string for the given input character. The specified character will be + * replaced by the given string whenever it occurs in the input, irrespective of whether it lies + * inside or outside the 'safe' range. + * + * @param c the character to be replaced + * @param replacement the string to replace the given character + * @return the builder instance + * @throws NullPointerException if {@code replacement} is null + */ + public Builder addEscape(char c, String replacement) { + checkNotNull(replacement); + // This can replace an existing character (the builder is re-usable). + replacementMap.put(c, replacement); + return this; + } + + /** Returns a new escaper based on the current state of the builder. */ + public Escaper build() { + return new ArrayBasedCharEscaper(replacementMap, safeMin, safeMax) { + private final char[] replacementChars = + unsafeReplacement != null ? unsafeReplacement.toCharArray() : null; + + @Override + protected char[] escapeUnsafe(char c) { + return replacementChars; + } + }; + } + } + + /** + * Returns a {@link UnicodeEscaper} equivalent to the given escaper instance. If the escaper is + * already a UnicodeEscaper then it is simply returned, otherwise it is wrapped in a + * UnicodeEscaper. + * + *

When a {@link CharEscaper} escaper is wrapped by this method it acquires extra behavior with + * respect to the well-formedness of Unicode character sequences and will throw {@link + * IllegalArgumentException} when given bad input. + * + * @param escaper the instance to be wrapped + * @return a UnicodeEscaper with the same behavior as the given instance + * @throws NullPointerException if escaper is null + * @throws IllegalArgumentException if escaper is not a UnicodeEscaper or a CharEscaper + */ + static UnicodeEscaper asUnicodeEscaper(Escaper escaper) { + checkNotNull(escaper); + if (escaper instanceof UnicodeEscaper) { + return (UnicodeEscaper) escaper; + } else if (escaper instanceof CharEscaper) { + return wrap((CharEscaper) escaper); + } + // In practice this shouldn't happen because it would be very odd not to + // extend either CharEscaper or UnicodeEscaper for non-trivial cases. + throw new IllegalArgumentException( + "Cannot create a UnicodeEscaper from: " + escaper.getClass().getName()); + } + + /** + * Returns a string that would replace the given character in the specified escaper, or {@code + * null} if no replacement should be made. This method is intended for use in tests through the + * {@code EscaperAsserts} class; production users of {@link CharEscaper} should limit themselves + * to its public interface. + * + * @param c the character to escape if necessary + * @return the replacement string, or {@code null} if no escaping was needed + */ + public static String computeReplacement(CharEscaper escaper, char c) { + return stringOrNull(escaper.escape(c)); + } + + /** + * Returns a string that would replace the given character in the specified escaper, or {@code + * null} if no replacement should be made. This method is intended for use in tests through the + * {@code EscaperAsserts} class; production users of {@link UnicodeEscaper} should limit + * themselves to its public interface. + * + * @param cp the Unicode code point to escape if necessary + * @return the replacement string, or {@code null} if no escaping was needed + */ + public static String computeReplacement(UnicodeEscaper escaper, int cp) { + return stringOrNull(escaper.escape(cp)); + } + + private static String stringOrNull(char[] in) { + return (in == null) ? null : new String(in); + } + + /** Private helper to wrap a CharEscaper as a UnicodeEscaper. */ + private static UnicodeEscaper wrap(CharEscaper escaper) { + return new UnicodeEscaper() { + @Override + protected char[] escape(int cp) { + // If a code point maps to a single character, just escape that. + if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + return escaper.escape((char) cp); + } + // Convert the code point to a surrogate pair and escape them both. + // Note: This code path is horribly slow and typically allocates 4 new + // char[] each time it is invoked. However this avoids any + // synchronization issues and makes the escaper thread safe. + char[] surrogateChars = new char[2]; + Character.toChars(cp, surrogateChars, 0); + char[] hiChars = escaper.escape(surrogateChars[0]); + char[] loChars = escaper.escape(surrogateChars[1]); + + // If either hiChars or lowChars are non-null, the CharEscaper is trying + // to escape the characters of a surrogate pair separately. This is + // uncommon and applies only to escapers that assume UCS-2 rather than + // UTF-16. See: http://en.wikipedia.org/wiki/UTF-16/UCS-2 + if (hiChars == null && loChars == null) { + // We expect this to be the common code path for most escapers. + return null; + } + // Combine the characters and/or escaped sequences into a single array. + int hiCount = hiChars != null ? hiChars.length : 1; + int loCount = loChars != null ? loChars.length : 1; + char[] output = new char[hiCount + loCount]; + if (hiChars != null) { + // TODO: Is this faster than System.arraycopy() for small arrays? + for (int n = 0; n < hiChars.length; ++n) { + output[n] = hiChars[n]; + } + } else { + output[0] = surrogateChars[0]; + } + if (loChars != null) { + for (int n = 0; n < loChars.length; ++n) { + output[hiCount + n] = loChars[n]; + } + } else { + output[hiCount] = surrogateChars[1]; + } + return output; + } + }; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ParametricNullness.java new file mode 100644 index 0000000000000..039c29bea1316 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Platform.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Platform.java new file mode 100644 index 0000000000000..9a5b402e10de3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/Platform.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Jesse Wilson + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class Platform { + private Platform() {} + + /** Returns a thread-local 1024-char array. */ + static char[] charBufferFromThreadLocal() { + return DEST_TL.get(); + } + + /** + * A thread-local destination buffer to keep us from creating new buffers. The starting size is + * 1024 characters. If we grow past this we don't put it back in the threadlocal, we just keep + * going and grow as needed. + */ + private static final ThreadLocal DEST_TL = + new ThreadLocal() { + @Override + protected char[] initialValue() { + return new char[1024]; + } + }; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/UnicodeEscaper.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/UnicodeEscaper.java new file mode 100644 index 0000000000000..1b14137bf1b37 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/UnicodeEscaper.java @@ -0,0 +1,307 @@ +/* + * 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.iotdb.google.common.escape; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An {@link Escaper} that converts literal text into a format safe for inclusion in a particular + * context (such as an XML document). Typically (but not always), the inverse process of + * "unescaping" the text is performed automatically by the relevant parser. + * + *

For example, an XML escaper would convert the literal string {@code "Foo"} into {@code + * "Foo<Bar>"} to prevent {@code ""} from being confused with an XML tag. When the + * resulting XML document is parsed, the parser API will return this text as the original literal + * string {@code "Foo"}. + * + *

Note: This class is similar to {@link CharEscaper} but with one very important + * difference. A CharEscaper can only process Java UTF16 characters in isolation and may not cope + * when it encounters surrogate pairs. This class facilitates the correct escaping of all Unicode + * characters. + * + *

As there are important reasons, including potential security issues, to handle Unicode + * correctly if you are considering implementing a new escaper you should favor using UnicodeEscaper + * wherever possible. + * + *

A {@code UnicodeEscaper} instance is required to be stateless, and safe when used concurrently + * by multiple threads. + * + *

Popular escapers are defined as constants in classes like {@link + * org.apache.iotdb.google.common.html.HtmlEscapers} and {@link + * org.apache.iotdb.google.common.xml.XmlEscapers}. To create your own escapers extend this class + * and implement the {@link #escape(int)} method. + * + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class UnicodeEscaper extends Escaper { + /** The amount of padding (chars) to use when growing the escape buffer. */ + private static final int DEST_PAD = 32; + + /** Constructor for use by subclasses. */ + protected UnicodeEscaper() {} + + /** + * Returns the escaped form of the given Unicode code point, or {@code null} if this code point + * does not need to be escaped. When called as part of an escaping operation, the given code point + * is guaranteed to be in the range {@code 0 <= cp <= Character#MAX_CODE_POINT}. + * + *

If an empty array is returned, this effectively strips the input character from the + * resulting text. + * + *

If the character does not need to be escaped, this method should return {@code null}, rather + * than an array containing the character representation of the code point. This enables the + * escaping algorithm to perform more efficiently. + * + *

If the implementation of this method cannot correctly handle a particular code point then it + * should either throw an appropriate runtime exception or return a suitable replacement + * character. It must never silently discard invalid input as this may constitute a security risk. + * + * @param cp the Unicode code point to escape if necessary + * @return the replacement characters, or {@code null} if no escaping was needed + */ + protected abstract char[] escape(int cp); + + /** + * Returns the escaped form of a given literal string. + * + *

If you are escaping input in arbitrary successive chunks, then it is not generally safe to + * use this method. If an input string ends with an unmatched high surrogate character, then this + * method will throw {@link IllegalArgumentException}. You should ensure your input is valid UTF-16 before calling this method. + * + *

Note: When implementing an escaper it is a good idea to override this method for + * efficiency by inlining the implementation of {@link #nextEscapeIndex(CharSequence, int, int)} + * directly. Doing this for {@link org.apache.iotdb.google.common.net.PercentEscaper} more than + * doubled the performance for unescaped strings (as measured by {@code CharEscapersBenchmark}). + * + * @param string the literal string to be escaped + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + * @throws IllegalArgumentException if invalid surrogate characters are encountered + */ + @Override + public String escape(String string) { + checkNotNull(string); + int end = string.length(); + int index = nextEscapeIndex(string, 0, end); + return index == end ? string : escapeSlow(string, index); + } + + /** + * Scans a sub-sequence of characters from a given {@link CharSequence}, returning the index of + * the next character that requires escaping. + * + *

Note: When implementing an escaper, it is a good idea to override this method for + * efficiency. The base class implementation determines successive Unicode code points and invokes + * {@link #escape(int)} for each of them. If the semantics of your escaper are such that code + * points in the supplementary range are either all escaped or all unescaped, this method can be + * implemented more efficiently using {@link CharSequence#charAt(int)}. + * + *

Note however that if your escaper does not escape characters in the supplementary range, you + * should either continue to validate the correctness of any surrogate characters encountered or + * provide a clear warning to users that your escaper does not validate its input. + * + *

See {@link org.apache.iotdb.google.common.net.PercentEscaper} for an example. + * + * @param csq a sequence of characters + * @param start the index of the first character to be scanned + * @param end the index immediately after the last character to be scanned + * @throws IllegalArgumentException if the scanned sub-sequence of {@code csq} contains invalid + * surrogate pairs + */ + protected int nextEscapeIndex(CharSequence csq, int start, int end) { + int index = start; + while (index < end) { + int cp = codePointAt(csq, index, end); + if (cp < 0 || escape(cp) != null) { + break; + } + index += Character.isSupplementaryCodePoint(cp) ? 2 : 1; + } + return index; + } + + /** + * Returns the escaped form of a given literal string, starting at the given index. This method is + * called by the {@link #escape(String)} method when it discovers that escaping is required. It is + * protected to allow subclasses to override the fastpath escaping function to inline their + * escaping test. See {@link CharEscaperBuilder} for an example usage. + * + *

This method is not reentrant and may only be invoked by the top level {@link + * #escape(String)} method. + * + * @param s the literal string to be escaped + * @param index the index to start escaping from + * @return the escaped form of {@code string} + * @throws NullPointerException if {@code string} is null + * @throws IllegalArgumentException if invalid surrogate characters are encountered + */ + protected final String escapeSlow(String s, int index) { + int end = s.length(); + + // Get a destination buffer and setup some loop variables. + char[] dest = Platform.charBufferFromThreadLocal(); + int destIndex = 0; + int unescapedChunkStart = 0; + + while (index < end) { + int cp = codePointAt(s, index, end); + if (cp < 0) { + throw new IllegalArgumentException("Trailing high surrogate at end of input"); + } + // It is possible for this to return null because nextEscapeIndex() may + // (for performance reasons) yield some false positives but it must never + // give false negatives. + char[] escaped = escape(cp); + int nextIndex = index + (Character.isSupplementaryCodePoint(cp) ? 2 : 1); + if (escaped != null) { + int charsSkipped = index - unescapedChunkStart; + + // This is the size needed to add the replacement, not the full + // size needed by the string. We only regrow when we absolutely must. + int sizeNeeded = destIndex + charsSkipped + escaped.length; + if (dest.length < sizeNeeded) { + int destLength = sizeNeeded + (end - index) + DEST_PAD; + dest = growBuffer(dest, destIndex, destLength); + } + // If we have skipped any characters, we need to copy them now. + if (charsSkipped > 0) { + s.getChars(unescapedChunkStart, index, dest, destIndex); + destIndex += charsSkipped; + } + if (escaped.length > 0) { + System.arraycopy(escaped, 0, dest, destIndex, escaped.length); + destIndex += escaped.length; + } + // If we dealt with an escaped character, reset the unescaped range. + unescapedChunkStart = nextIndex; + } + index = nextEscapeIndex(s, nextIndex, end); + } + + // Process trailing unescaped characters - no need to account for escaped + // length or padding the allocation. + int charsSkipped = end - unescapedChunkStart; + if (charsSkipped > 0) { + int endIndex = destIndex + charsSkipped; + if (dest.length < endIndex) { + dest = growBuffer(dest, destIndex, endIndex); + } + s.getChars(unescapedChunkStart, end, dest, destIndex); + destIndex = endIndex; + } + return new String(dest, 0, destIndex); + } + + /** + * Returns the Unicode code point of the character at the given index. + * + *

Unlike {@link Character#codePointAt(CharSequence, int)} or {@link String#codePointAt(int)} + * this method will never fail silently when encountering an invalid surrogate pair. + * + *

The behaviour of this method is as follows: + * + *

    + *
  1. If {@code index >= end}, {@link IndexOutOfBoundsException} is thrown. + *
  2. If the character at the specified index is not a surrogate, it is returned. + *
  3. If the first character was a high surrogate value, then an attempt is made to read the + * next character. + *
      + *
    1. If the end of the sequence was reached, the negated value of the trailing high + * surrogate is returned. + *
    2. If the next character was a valid low surrogate, the code point value of the + * high/low surrogate pair is returned. + *
    3. If the next character was not a low surrogate value, then {@link + * IllegalArgumentException} is thrown. + *
    + *
  4. If the first character was a low surrogate value, {@link IllegalArgumentException} is + * thrown. + *
+ * + * @param seq the sequence of characters from which to decode the code point + * @param index the index of the first character to decode + * @param end the index beyond the last valid character to decode + * @return the Unicode code point for the given index or the negated value of the trailing high + * surrogate character at the end of the sequence + */ + protected static int codePointAt(CharSequence seq, int index, int end) { + checkNotNull(seq); + if (index < end) { + char c1 = seq.charAt(index++); + if (c1 < Character.MIN_HIGH_SURROGATE || c1 > Character.MAX_LOW_SURROGATE) { + // Fast path (first test is probably all we need to do) + return c1; + } else if (c1 <= Character.MAX_HIGH_SURROGATE) { + // If the high surrogate was the last character, return its inverse + if (index == end) { + return -c1; + } + // Otherwise look for the low surrogate following it + char c2 = seq.charAt(index); + if (Character.isLowSurrogate(c2)) { + return Character.toCodePoint(c1, c2); + } + throw new IllegalArgumentException( + "Expected low surrogate but got char '" + + c2 + + "' with value " + + (int) c2 + + " at index " + + index + + " in '" + + seq + + "'"); + } else { + throw new IllegalArgumentException( + "Unexpected low surrogate character '" + + c1 + + "' with value " + + (int) c1 + + " at index " + + (index - 1) + + " in '" + + seq + + "'"); + } + } + throw new IndexOutOfBoundsException("Index exceeds specified range"); + } + + /** + * Helper method to grow the character buffer as needed, this only happens once in a while so it's + * ok if it's in a method call. If the index passed in is 0 then no copying will be done. + */ + private static char[] growBuffer(char[] dest, int index, int size) { + if (size < 0) { // overflow - should be OutOfMemoryError but GWT/j2cl don't support it + throw new AssertionError("Cannot increase internal buffer any further"); + } + char[] copy = new char[size]; + if (index > 0) { + System.arraycopy(dest, 0, copy, 0, index); + } + return copy; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/package-info.java new file mode 100644 index 0000000000000..c7015e894b804 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/escape/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.escape; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/AllowConcurrentEvents.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/AllowConcurrentEvents.java new file mode 100644 index 0000000000000..41b7f29b816c3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/AllowConcurrentEvents.java @@ -0,0 +1,39 @@ +/* + * 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.iotdb.google.common.eventbus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks an event subscriber method as being thread-safe. This annotation indicates that EventBus + * may invoke the event subscriber simultaneously from multiple threads. + * + *

This does not mark the method, and so should be used in combination with {@link Subscribe}. + * + * @author Cliff Biffle + * @since 10.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@ElementTypesAreNonnullByDefault +public @interface AllowConcurrentEvents {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/AsyncEventBus.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/AsyncEventBus.java new file mode 100644 index 0000000000000..661fd630d0e12 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/AsyncEventBus.java @@ -0,0 +1,68 @@ +/* + * 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.iotdb.google.common.eventbus; + +import java.util.concurrent.Executor; + +/** + * An {@link EventBus} that takes the Executor of your choice and uses it to dispatch events, + * allowing dispatch to occur asynchronously. + * + * @author Cliff Biffle + * @since 10.0 + */ +@ElementTypesAreNonnullByDefault +public class AsyncEventBus extends EventBus { + + /** + * Creates a new AsyncEventBus that will use {@code executor} to dispatch events. Assigns {@code + * identifier} as the bus's name for logging purposes. + * + * @param identifier short name for the bus, for logging purposes. + * @param executor Executor to use to dispatch events. It is the caller's responsibility to shut + * down the executor after the last event has been posted to this event bus. + */ + public AsyncEventBus(String identifier, Executor executor) { + super(identifier, executor, Dispatcher.legacyAsync(), LoggingHandler.INSTANCE); + } + + /** + * Creates a new AsyncEventBus that will use {@code executor} to dispatch events. + * + * @param executor Executor to use to dispatch events. It is the caller's responsibility to shut + * down the executor after the last event has been posted to this event bus. + * @param subscriberExceptionHandler Handler used to handle exceptions thrown from subscribers. + * See {@link SubscriberExceptionHandler} for more information. + * @since 16.0 + */ + public AsyncEventBus(Executor executor, SubscriberExceptionHandler subscriberExceptionHandler) { + super("default", executor, Dispatcher.legacyAsync(), subscriberExceptionHandler); + } + + /** + * Creates a new AsyncEventBus that will use {@code executor} to dispatch events. + * + * @param executor Executor to use to dispatch events. It is the caller's responsibility to shut + * down the executor after the last event has been posted to this event bus. + */ + public AsyncEventBus(Executor executor) { + super("default", executor, Dispatcher.legacyAsync(), LoggingHandler.INSTANCE); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/DeadEvent.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/DeadEvent.java new file mode 100644 index 0000000000000..55377c0b5d197 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/DeadEvent.java @@ -0,0 +1,76 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.base.MoreObjects; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Wraps an event that was posted, but which had no subscribers and thus could not be delivered. + * + *

Registering a DeadEvent subscriber is useful for debugging or logging, as it can detect + * misconfigurations in a system's event distribution. + * + * @author Cliff Biffle + * @since 10.0 + */ +@ElementTypesAreNonnullByDefault +public class DeadEvent { + + private final Object source; + private final Object event; + + /** + * Creates a new DeadEvent. + * + * @param source object broadcasting the DeadEvent (generally the {@link EventBus}). + * @param event the event that could not be delivered. + */ + public DeadEvent(Object source, Object event) { + this.source = checkNotNull(source); + this.event = checkNotNull(event); + } + + /** + * Returns the object that originated this event (not the object that originated the + * wrapped event). This is generally an {@link EventBus}. + * + * @return the source of this event. + */ + public Object getSource() { + return source; + } + + /** + * Returns the wrapped, 'dead' event, which the system was unable to deliver to any registered + * subscriber. + * + * @return the 'dead' event that could not be delivered. + */ + public Object getEvent() { + return event; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("source", source).add("event", event).toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Dispatcher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Dispatcher.java new file mode 100644 index 0000000000000..a3192a66f23f5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Dispatcher.java @@ -0,0 +1,197 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.collect.Queues; + +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Handler for dispatching events to subscribers, providing different event ordering guarantees that + * make sense for different situations. + * + *

Note: The dispatcher is orthogonal to the subscriber's {@code Executor}. The dispatcher + * controls the order in which events are dispatched, while the executor controls how (i.e. on which + * thread) the subscriber is actually called when an event is dispatched to it. + * + * @author Colin Decker + */ +@ElementTypesAreNonnullByDefault +abstract class Dispatcher { + + /** + * Returns a dispatcher that queues events that are posted reentrantly on a thread that is already + * dispatching an event, guaranteeing that all events posted on a single thread are dispatched to + * all subscribers in the order they are posted. + * + *

When all subscribers are dispatched to using a direct executor (which dispatches on + * the same thread that posts the event), this yields a breadth-first dispatch order on each + * thread. That is, all subscribers to a single event A will be called before any subscribers to + * any events B and C that are posted to the event bus by the subscribers to A. + */ + static Dispatcher perThreadDispatchQueue() { + return new PerThreadQueuedDispatcher(); + } + + /** + * Returns a dispatcher that queues events that are posted in a single global queue. This behavior + * matches the original behavior of AsyncEventBus exactly, but is otherwise not especially useful. + * For async dispatch, an {@linkplain #immediate() immediate} dispatcher should generally be + * preferable. + */ + static Dispatcher legacyAsync() { + return new LegacyAsyncDispatcher(); + } + + /** + * Returns a dispatcher that dispatches events to subscribers immediately as they're posted + * without using an intermediate queue to change the dispatch order. This is effectively a + * depth-first dispatch order, vs. breadth-first when using a queue. + */ + static Dispatcher immediate() { + return ImmediateDispatcher.INSTANCE; + } + + /** Dispatches the given {@code event} to the given {@code subscribers}. */ + abstract void dispatch(Object event, Iterator subscribers); + + /** Implementation of a {@link #perThreadDispatchQueue()} dispatcher. */ + private static final class PerThreadQueuedDispatcher extends Dispatcher { + + // This dispatcher matches the original dispatch behavior of EventBus. + + /** Per-thread queue of events to dispatch. */ + private final ThreadLocal> queue = + new ThreadLocal>() { + @Override + protected Queue initialValue() { + return Queues.newArrayDeque(); + } + }; + + /** Per-thread dispatch state, used to avoid reentrant event dispatching. */ + private final ThreadLocal dispatching = + new ThreadLocal() { + @Override + protected Boolean initialValue() { + return false; + } + }; + + @Override + void dispatch(Object event, Iterator subscribers) { + checkNotNull(event); + checkNotNull(subscribers); + Queue queueForThread = queue.get(); + queueForThread.offer(new Event(event, subscribers)); + + if (!dispatching.get()) { + dispatching.set(true); + try { + Event nextEvent; + while ((nextEvent = queueForThread.poll()) != null) { + while (nextEvent.subscribers.hasNext()) { + nextEvent.subscribers.next().dispatchEvent(nextEvent.event); + } + } + } finally { + dispatching.remove(); + queue.remove(); + } + } + } + + private static final class Event { + private final Object event; + private final Iterator subscribers; + + private Event(Object event, Iterator subscribers) { + this.event = event; + this.subscribers = subscribers; + } + } + } + + /** Implementation of a {@link #legacyAsync()} dispatcher. */ + private static final class LegacyAsyncDispatcher extends Dispatcher { + + // This dispatcher matches the original dispatch behavior of AsyncEventBus. + // + // We can't really make any guarantees about the overall dispatch order for this dispatcher in + // a multithreaded environment for a couple of reasons: + // + // 1. Subscribers to events posted on different threads can be interleaved with each other + // freely. (A event on one thread, B event on another could yield any of + // [a1, a2, a3, b1, b2], [a1, b2, a2, a3, b2], [a1, b2, b3, a2, a3], etc.) + // 2. It's possible for subscribers to actually be dispatched to in a different order than they + // were added to the queue. It's easily possible for one thread to take the head of the + // queue, immediately followed by another thread taking the next element in the queue. That + // second thread can then dispatch to the subscriber it took before the first thread does. + // + // All this makes me really wonder if there's any value in queueing here at all. A dispatcher + // that simply loops through the subscribers and dispatches the event to each would actually + // probably provide a stronger order guarantee, though that order would obviously be different + // in some cases. + + /** Global event queue. */ + private final ConcurrentLinkedQueue queue = + Queues.newConcurrentLinkedQueue(); + + @Override + void dispatch(Object event, Iterator subscribers) { + checkNotNull(event); + while (subscribers.hasNext()) { + queue.add(new EventWithSubscriber(event, subscribers.next())); + } + + EventWithSubscriber e; + while ((e = queue.poll()) != null) { + e.subscriber.dispatchEvent(e.event); + } + } + + private static final class EventWithSubscriber { + private final Object event; + private final Subscriber subscriber; + + private EventWithSubscriber(Object event, Subscriber subscriber) { + this.event = event; + this.subscriber = subscriber; + } + } + } + + /** Implementation of {@link #immediate()}. */ + private static final class ImmediateDispatcher extends Dispatcher { + private static final ImmediateDispatcher INSTANCE = new ImmediateDispatcher(); + + @Override + void dispatch(Object event, Iterator subscribers) { + checkNotNull(event); + while (subscribers.hasNext()) { + subscribers.next().dispatchEvent(event); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..f5bca1c851771 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/EventBus.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/EventBus.java new file mode 100644 index 0000000000000..f52f4150c2acd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/EventBus.java @@ -0,0 +1,314 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.util.concurrent.MoreExecutors; + +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.Locale; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Dispatches events to listeners, and provides ways for listeners to register themselves. + * + *

Avoid EventBus

+ * + *

We recommend against using EventBus. It was designed many years ago, and newer + * libraries offer better ways to decouple components and react to events. + * + *

To decouple components, we recommend a dependency-injection framework. For Android code, most + * apps use Dagger. For server code, common options include Guice and Spring. + * Frameworks typically offer a way to register multiple listeners independently and then request + * them together as a set (Dagger, Guice, Spring). + * + *

To react to events, we recommend a reactive-streams framework like RxJava (supplemented with its RxAndroid extension if you are building for + * Android) or Project Reactor. (For the basics of + * translating code from using an event bus to using a reactive-streams framework, see these two + * guides: 1, 2.) Some usages + * of EventBus may be better written using Kotlin coroutines, including Flow and Channels. Yet other usages are better served + * by individual libraries that provide specialized support for particular use cases. + * + *

Disadvantages of EventBus include: + * + *

    + *
  • It makes the cross-references between producer and subscriber harder to find. This can + * complicate debugging, lead to unintentional reentrant calls, and force apps to eagerly + * initialize all possible subscribers at startup time. + *
  • It uses reflection in ways that break when code is processed by optimizers/minimizers like + * R8 and Proguard. + *
  • It doesn't offer a way to wait for multiple events before taking action. For example, it + * doesn't offer a way to wait for multiple producers to all report that they're "ready," nor + * does it offer a way to batch multiple events from a single producer together. + *
  • It doesn't support backpressure and other features needed for resilience. + *
  • It doesn't provide much control of threading. + *
  • It doesn't offer much monitoring. + *
  • It doesn't propagate exceptions, so apps don't have a way to react to them. + *
  • It doesn't interoperate well with RxJava, coroutines, and other more commonly used + * alternatives. + *
  • It imposes requirements on the lifecycle of its subscribers. For example, if an event + * occurs between when one subscriber is removed and the next subscriber is added, the event + * is dropped. + *
  • Its performance is suboptimal, especially under Android. + *
  • It doesn't support parameterized + * types. + *
  • With the introduction of lambdas in Java 8, EventBus went from less verbose than listeners + * to more verbose. + *
+ * + *

EventBus Summary

+ * + *

The EventBus allows publish-subscribe-style communication between components without requiring + * the components to explicitly register with one another (and thus be aware of each other). It is + * designed exclusively to replace traditional Java in-process event distribution using explicit + * registration. It is not a general-purpose publish-subscribe system, nor is it intended + * for interprocess communication. + * + *

Receiving Events

+ * + *

To receive events, an object should: + * + *

    + *
  1. Expose a public method, known as the event subscriber, which accepts a single + * argument of the type of event desired; + *
  2. Mark it with a {@link Subscribe} annotation; + *
  3. Pass itself to an EventBus instance's {@link #register(Object)} method. + *
+ * + *

Posting Events

+ * + *

To post an event, simply provide the event object to the {@link #post(Object)} method. The + * EventBus instance will determine the type of event and route it to all registered listeners. + * + *

Events are routed based on their type — an event will be delivered to any subscriber for + * any type to which the event is assignable. This includes implemented interfaces, all + * superclasses, and all interfaces implemented by superclasses. + * + *

When {@code post} is called, all registered subscribers for an event are run in sequence, so + * subscribers should be reasonably quick. If an event may trigger an extended process (such as a + * database load), spawn a thread or queue it for later. (For a convenient way to do this, use an + * {@link AsyncEventBus}.) + * + *

Subscriber Methods

+ * + *

Event subscriber methods must accept only one argument: the event. + * + *

Subscribers should not, in general, throw. If they do, the EventBus will catch and log the + * exception. This is rarely the right solution for error handling and should not be relied upon; it + * is intended solely to help find problems during development. + * + *

The EventBus guarantees that it will not call a subscriber method from multiple threads + * simultaneously, unless the method explicitly allows it by bearing the {@link + * AllowConcurrentEvents} annotation. If this annotation is not present, subscriber methods need not + * worry about being reentrant, unless also called from outside the EventBus. + * + *

Dead Events

+ * + *

If an event is posted, but no registered subscribers can accept it, it is considered "dead." + * To give the system a second chance to handle dead events, they are wrapped in an instance of + * {@link DeadEvent} and reposted. + * + *

If a subscriber for a supertype of all events (such as Object) is registered, no event will + * ever be considered dead, and no DeadEvents will be generated. Accordingly, while DeadEvent + * extends {@link Object}, a subscriber registered to receive any Object will never receive a + * DeadEvent. + * + *

This class is safe for concurrent use. + * + *

See the Guava User Guide article on {@code EventBus}. + * + * @author Cliff Biffle + * @since 10.0 + */ +@ElementTypesAreNonnullByDefault +public class EventBus { + + private static final Logger logger = Logger.getLogger(EventBus.class.getName()); + + private final String identifier; + private final Executor executor; + private final SubscriberExceptionHandler exceptionHandler; + + private final SubscriberRegistry subscribers = new SubscriberRegistry(this); + private final Dispatcher dispatcher; + + /** Creates a new EventBus named "default". */ + public EventBus() { + this("default"); + } + + /** + * Creates a new EventBus with the given {@code identifier}. + * + * @param identifier a brief name for this bus, for logging purposes. Should be a valid Java + * identifier. + */ + public EventBus(String identifier) { + this( + identifier, + MoreExecutors.directExecutor(), + Dispatcher.perThreadDispatchQueue(), + LoggingHandler.INSTANCE); + } + + /** + * Creates a new EventBus with the given {@link SubscriberExceptionHandler}. + * + * @param exceptionHandler Handler for subscriber exceptions. + * @since 16.0 + */ + public EventBus(SubscriberExceptionHandler exceptionHandler) { + this( + "default", + MoreExecutors.directExecutor(), + Dispatcher.perThreadDispatchQueue(), + exceptionHandler); + } + + EventBus( + String identifier, + Executor executor, + Dispatcher dispatcher, + SubscriberExceptionHandler exceptionHandler) { + this.identifier = checkNotNull(identifier); + this.executor = checkNotNull(executor); + this.dispatcher = checkNotNull(dispatcher); + this.exceptionHandler = checkNotNull(exceptionHandler); + } + + /** + * Returns the identifier for this event bus. + * + * @since 19.0 + */ + public final String identifier() { + return identifier; + } + + /** Returns the default executor this event bus uses for dispatching events to subscribers. */ + final Executor executor() { + return executor; + } + + /** Handles the given exception thrown by a subscriber with the given context. */ + void handleSubscriberException(Throwable e, SubscriberExceptionContext context) { + checkNotNull(e); + checkNotNull(context); + try { + exceptionHandler.handleException(e, context); + } catch (Throwable e2) { + // if the handler threw an exception... well, just log it + logger.log( + Level.SEVERE, + String.format(Locale.ROOT, "Exception %s thrown while handling exception: %s", e2, e), + e2); + } + } + + /** + * Registers all subscriber methods on {@code object} to receive events. + * + * @param object object whose subscriber methods should be registered. + */ + public void register(Object object) { + subscribers.register(object); + } + + /** + * Unregisters all subscriber methods on a registered {@code object}. + * + * @param object object whose subscriber methods should be unregistered. + * @throws IllegalArgumentException if the object was not previously registered. + */ + public void unregister(Object object) { + subscribers.unregister(object); + } + + /** + * Posts an event to all registered subscribers. This method will return successfully after the + * event has been posted to all subscribers, and regardless of any exceptions thrown by + * subscribers. + * + *

If no subscribers have been subscribed for {@code event}'s class, and {@code event} is not + * already a {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted. + * + * @param event event to post. + */ + public void post(Object event) { + Iterator eventSubscribers = subscribers.getSubscribers(event); + if (eventSubscribers.hasNext()) { + dispatcher.dispatch(event, eventSubscribers); + } else if (!(event instanceof DeadEvent)) { + // the event had no subscribers and was not itself a DeadEvent + post(new DeadEvent(this, event)); + } + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).addValue(identifier).toString(); + } + + /** Simple logging handler for subscriber exceptions. */ + static final class LoggingHandler implements SubscriberExceptionHandler { + static final LoggingHandler INSTANCE = new LoggingHandler(); + + @Override + public void handleException(Throwable exception, SubscriberExceptionContext context) { + Logger logger = logger(context); + if (logger.isLoggable(Level.SEVERE)) { + logger.log(Level.SEVERE, message(context), exception); + } + } + + private static Logger logger(SubscriberExceptionContext context) { + return Logger.getLogger(EventBus.class.getName() + "." + context.getEventBus().identifier()); + } + + private static String message(SubscriberExceptionContext context) { + Method method = context.getSubscriberMethod(); + return "Exception thrown by subscriber method " + + method.getName() + + '(' + + method.getParameterTypes()[0].getName() + + ')' + + " on subscriber " + + context.getSubscriber() + + " when dispatching event: " + + context.getEvent(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/ParametricNullness.java new file mode 100644 index 0000000000000..8b048a9560ce1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Subscribe.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Subscribe.java new file mode 100644 index 0000000000000..a1903f0b39a0c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Subscribe.java @@ -0,0 +1,44 @@ +/* + * 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.iotdb.google.common.eventbus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a method as an event subscriber. + * + *

The type of event will be indicated by the method's first (and only) parameter, which cannot + * be primitive. If this annotation is applied to methods with zero parameters, or more than one + * parameter, the object containing the method will not be able to register for event delivery from + * the {@link EventBus}. + * + *

Unless also annotated with @{@link AllowConcurrentEvents}, event subscriber methods will be + * invoked serially by each event bus that they are registered with. + * + * @author Cliff Biffle + * @since 10.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@ElementTypesAreNonnullByDefault +public @interface Subscribe {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Subscriber.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Subscriber.java new file mode 100644 index 0000000000000..22f0b2b100951 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/Subscriber.java @@ -0,0 +1,150 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.Executor; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A subscriber method on a specific object, plus the executor that should be used for dispatching + * events to it. + * + *

Two subscribers are equivalent when they refer to the same method on the same object (not + * class). This property is used to ensure that no subscriber method is registered more than once. + * + * @author Colin Decker + */ +@ElementTypesAreNonnullByDefault +class Subscriber { + + /** Creates a {@code Subscriber} for {@code method} on {@code listener}. */ + static Subscriber create(EventBus bus, Object listener, Method method) { + return isDeclaredThreadSafe(method) + ? new Subscriber(bus, listener, method) + : new SynchronizedSubscriber(bus, listener, method); + } + + /** The event bus this subscriber belongs to. */ + private EventBus bus; + + /** The object with the subscriber method. */ + @VisibleForTesting final Object target; + + /** Subscriber method. */ + private final Method method; + + /** Executor to use for dispatching events to this subscriber. */ + private final Executor executor; + + private Subscriber(EventBus bus, Object target, Method method) { + this.bus = bus; + this.target = checkNotNull(target); + this.method = method; + method.setAccessible(true); + + this.executor = bus.executor(); + } + + /** Dispatches {@code event} to this subscriber using the proper executor. */ + final void dispatchEvent(Object event) { + executor.execute( + () -> { + try { + invokeSubscriberMethod(event); + } catch (InvocationTargetException e) { + bus.handleSubscriberException(e.getCause(), context(event)); + } + }); + } + + /** + * Invokes the subscriber method. This method can be overridden to make the invocation + * synchronized. + */ + @VisibleForTesting + void invokeSubscriberMethod(Object event) throws InvocationTargetException { + try { + method.invoke(target, checkNotNull(event)); + } catch (IllegalArgumentException e) { + throw new Error("Method rejected target/argument: " + event, e); + } catch (IllegalAccessException e) { + throw new Error("Method became inaccessible: " + event, e); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Error) { + throw (Error) e.getCause(); + } + throw e; + } + } + + /** Gets the context for the given event. */ + private SubscriberExceptionContext context(Object event) { + return new SubscriberExceptionContext(bus, event, target, method); + } + + @Override + public final int hashCode() { + return (31 + method.hashCode()) * 31 + System.identityHashCode(target); + } + + @Override + public final boolean equals(Object obj) { + if (obj instanceof Subscriber) { + Subscriber that = (Subscriber) obj; + // Use == so that different equal instances will still receive events. + // We only guard against the case that the same object is registered + // multiple times + return target == that.target && method.equals(that.method); + } + return false; + } + + /** + * Checks whether {@code method} is thread-safe, as indicated by the presence of the {@link + * AllowConcurrentEvents} annotation. + */ + private static boolean isDeclaredThreadSafe(Method method) { + return method.getAnnotation(AllowConcurrentEvents.class) != null; + } + + /** + * Subscriber that synchronizes invocations of a method to ensure that only one thread may enter + * the method at a time. + */ + @VisibleForTesting + static final class SynchronizedSubscriber extends Subscriber { + + private SynchronizedSubscriber(EventBus bus, Object target, Method method) { + super(bus, target, method); + } + + @Override + void invokeSubscriberMethod(Object event) throws InvocationTargetException { + synchronized (this) { + super.invokeSubscriberMethod(event); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberExceptionContext.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberExceptionContext.java new file mode 100644 index 0000000000000..ed2413d789831 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberExceptionContext.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.google.common.eventbus; + +import java.lang.reflect.Method; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Context for an exception thrown by a subscriber. + * + * @since 16.0 + */ +@ElementTypesAreNonnullByDefault +public class SubscriberExceptionContext { + private final EventBus eventBus; + private final Object event; + private final Object subscriber; + private final Method subscriberMethod; + + /** + * @param eventBus The {@link EventBus} that handled the event and the subscriber. Useful for + * broadcasting a new event based on the error. + * @param event The event object that caused the subscriber to throw. + * @param subscriber The source subscriber context. + * @param subscriberMethod the subscribed method. + */ + SubscriberExceptionContext( + EventBus eventBus, Object event, Object subscriber, Method subscriberMethod) { + this.eventBus = checkNotNull(eventBus); + this.event = checkNotNull(event); + this.subscriber = checkNotNull(subscriber); + this.subscriberMethod = checkNotNull(subscriberMethod); + } + + /** + * @return The {@link EventBus} that handled the event and the subscriber. Useful for broadcasting + * a new event based on the error. + */ + public EventBus getEventBus() { + return eventBus; + } + + /** + * @return The event object that caused the subscriber to throw. + */ + public Object getEvent() { + return event; + } + + /** + * @return The object context that the subscriber was called on. + */ + public Object getSubscriber() { + return subscriber; + } + + /** + * @return The subscribed method that threw the exception. + */ + public Method getSubscriberMethod() { + return subscriberMethod; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberExceptionHandler.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberExceptionHandler.java new file mode 100644 index 0000000000000..970e924e58659 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberExceptionHandler.java @@ -0,0 +1,31 @@ +/* + * 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.iotdb.google.common.eventbus; + +/** + * Handler for exceptions thrown by event subscribers. + * + * @since 16.0 + */ +@ElementTypesAreNonnullByDefault +public interface SubscriberExceptionHandler { + /** Handles exceptions thrown by subscribers. */ + void handleException(Throwable exception, SubscriberExceptionContext context); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberRegistry.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberRegistry.java new file mode 100644 index 0000000000000..5a079771ad80c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/SubscriberRegistry.java @@ -0,0 +1,274 @@ +/* + * 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.iotdb.google.common.eventbus; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Throwables; +import org.apache.iotdb.google.common.cache.CacheBuilder; +import org.apache.iotdb.google.common.cache.CacheLoader; +import org.apache.iotdb.google.common.cache.LoadingCache; +import org.apache.iotdb.google.common.collect.HashMultimap; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Multimap; +import org.apache.iotdb.google.common.primitives.Primitives; +import org.apache.iotdb.google.common.reflect.TypeToken; +import org.apache.iotdb.google.common.util.concurrent.UncheckedExecutionException; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; + +/** + * Registry of subscribers to a single event bus. + * + * @author Colin Decker + */ +@ElementTypesAreNonnullByDefault +final class SubscriberRegistry { + + /** + * All registered subscribers, indexed by event type. + * + *

The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an + * immutable snapshot of all current subscribers to an event without any locking. + */ + private final ConcurrentMap, CopyOnWriteArraySet> subscribers = + Maps.newConcurrentMap(); + + /** The event bus this registry belongs to. */ + private final EventBus bus; + + SubscriberRegistry(EventBus bus) { + this.bus = checkNotNull(bus); + } + + /** Registers all subscriber methods on the given listener object. */ + void register(Object listener) { + Multimap, Subscriber> listenerMethods = findAllSubscribers(listener); + + for (Entry, Collection> entry : listenerMethods.asMap().entrySet()) { + Class eventType = entry.getKey(); + Collection eventMethodsInListener = entry.getValue(); + + CopyOnWriteArraySet eventSubscribers = subscribers.get(eventType); + + if (eventSubscribers == null) { + CopyOnWriteArraySet newSet = new CopyOnWriteArraySet<>(); + eventSubscribers = + MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet); + } + + eventSubscribers.addAll(eventMethodsInListener); + } + } + + /** Unregisters all subscribers on the given listener object. */ + void unregister(Object listener) { + Multimap, Subscriber> listenerMethods = findAllSubscribers(listener); + + for (Entry, Collection> entry : listenerMethods.asMap().entrySet()) { + Class eventType = entry.getKey(); + Collection listenerMethodsForType = entry.getValue(); + + CopyOnWriteArraySet currentSubscribers = subscribers.get(eventType); + if (currentSubscribers == null || !currentSubscribers.removeAll(listenerMethodsForType)) { + // if removeAll returns true, all we really know is that at least one subscriber was + // removed... however, barring something very strange we can assume that if at least one + // subscriber was removed, all subscribers on listener for that event type were... after + // all, the definition of subscribers on a particular class is totally static + throw new IllegalArgumentException( + "missing event subscriber for an annotated method. Is " + listener + " registered?"); + } + + // don't try to remove the set if it's empty; that can't be done safely without a lock + // anyway, if the set is empty it'll just be wrapping an array of length 0 + } + } + + @VisibleForTesting + Set getSubscribersForTesting(Class eventType) { + return MoreObjects.firstNonNull(subscribers.get(eventType), ImmutableSet.of()); + } + + /** + * Gets an iterator representing an immutable snapshot of all subscribers to the given event at + * the time this method is called. + */ + Iterator getSubscribers(Object event) { + ImmutableSet> eventTypes = flattenHierarchy(event.getClass()); + + List> subscriberIterators = + Lists.newArrayListWithCapacity(eventTypes.size()); + + for (Class eventType : eventTypes) { + CopyOnWriteArraySet eventSubscribers = subscribers.get(eventType); + if (eventSubscribers != null) { + // eager no-copy snapshot + subscriberIterators.add(eventSubscribers.iterator()); + } + } + + return Iterators.concat(subscriberIterators.iterator()); + } + + /** + * A thread-safe cache that contains the mapping from each class to all methods in that class and + * all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all + * instances of this class; this greatly improves performance if multiple EventBus instances are + * created and objects of the same class are registered on all of them. + */ + private static final LoadingCache, ImmutableList> subscriberMethodsCache = + CacheBuilder.newBuilder() + .weakKeys() + .build( + new CacheLoader, ImmutableList>() { + @Override + public ImmutableList load(Class concreteClass) throws Exception { + return getAnnotatedMethodsNotCached(concreteClass); + } + }); + + /** + * Returns all subscribers for the given listener grouped by the type of event they subscribe to. + */ + private Multimap, Subscriber> findAllSubscribers(Object listener) { + Multimap, Subscriber> methodsInListener = HashMultimap.create(); + Class clazz = listener.getClass(); + for (Method method : getAnnotatedMethods(clazz)) { + Class[] parameterTypes = method.getParameterTypes(); + Class eventType = parameterTypes[0]; + methodsInListener.put(eventType, Subscriber.create(bus, listener, method)); + } + return methodsInListener; + } + + private static ImmutableList getAnnotatedMethods(Class clazz) { + try { + return subscriberMethodsCache.getUnchecked(clazz); + } catch (UncheckedExecutionException e) { + throwIfUnchecked(e.getCause()); + throw e; + } + } + + private static ImmutableList getAnnotatedMethodsNotCached(Class clazz) { + Set> supertypes = TypeToken.of(clazz).getTypes().rawTypes(); + Map identifiers = Maps.newHashMap(); + for (Class supertype : supertypes) { + for (Method method : supertype.getDeclaredMethods()) { + if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) { + // TODO(cgdecker): Should check for a generic parameter type and error out + Class[] parameterTypes = method.getParameterTypes(); + checkArgument( + parameterTypes.length == 1, + "Method %s has @Subscribe annotation but has %s parameters. " + + "Subscriber methods must have exactly 1 parameter.", + method, + parameterTypes.length); + + checkArgument( + !parameterTypes[0].isPrimitive(), + "@Subscribe method %s's parameter is %s. " + + "Subscriber methods cannot accept primitives. " + + "Consider changing the parameter to %s.", + method, + parameterTypes[0].getName(), + Primitives.wrap(parameterTypes[0]).getSimpleName()); + + MethodIdentifier ident = new MethodIdentifier(method); + if (!identifiers.containsKey(ident)) { + identifiers.put(ident, method); + } + } + } + } + return ImmutableList.copyOf(identifiers.values()); + } + + /** Global cache of classes to their flattened hierarchy of supertypes. */ + private static final LoadingCache, ImmutableSet>> flattenHierarchyCache = + CacheBuilder.newBuilder() + .weakKeys() + .build( + new CacheLoader, ImmutableSet>>() { + // > is actually needed to compile + @SuppressWarnings("RedundantTypeArguments") + @Override + public ImmutableSet> load(Class concreteClass) { + return ImmutableSet.>copyOf( + TypeToken.of(concreteClass).getTypes().rawTypes()); + } + }); + + /** + * Flattens a class's type hierarchy into a set of {@code Class} objects including all + * superclasses (transitively) and all interfaces implemented by these superclasses. + */ + @VisibleForTesting + static ImmutableSet> flattenHierarchy(Class concreteClass) { + try { + return flattenHierarchyCache.getUnchecked(concreteClass); + } catch (UncheckedExecutionException e) { + throw Throwables.propagate(e.getCause()); + } + } + + private static final class MethodIdentifier { + + private final String name; + private final List> parameterTypes; + + MethodIdentifier(Method method) { + this.name = method.getName(); + this.parameterTypes = Arrays.asList(method.getParameterTypes()); + } + + @Override + public int hashCode() { + return Objects.hashCode(name, parameterTypes); + } + + @Override + public boolean equals(Object o) { + if (o instanceof MethodIdentifier) { + MethodIdentifier ident = (MethodIdentifier) o; + return name.equals(ident.name) && parameterTypes.equals(ident.parameterTypes); + } + return false; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/package-info.java new file mode 100644 index 0000000000000..90e198c54a97e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/eventbus/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.eventbus; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractBaseGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractBaseGraph.java new file mode 100644 index 0000000000000..b249370782b77 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractBaseGraph.java @@ -0,0 +1,190 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.util.AbstractSet; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH; + +/** + * This class provides a skeletal implementation of {@link BaseGraph}. + * + *

The methods implemented in this class should not be overridden unless the subclass admits a + * more efficient implementation. + * + * @author James Sexton + * @param Node parameter type + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractBaseGraph implements BaseGraph { + + /** + * Returns the number of edges in this graph; used to calculate the size of {@link Graph#edges()}. + * This implementation requires O(|N|) time. Classes extending this one may manually keep track of + * the number of edges as the graph is updated, and override this method for better performance. + */ + protected long edgeCount() { + long degreeSum = 0L; + for (N node : nodes()) { + degreeSum += degree(node); + } + // According to the degree sum formula, this is equal to twice the number of edges. + checkState((degreeSum & 1) == 0); + return degreeSum >>> 1; + } + + /** + * An implementation of {@link BaseGraph#edges()} defined in terms of {@link Graph#nodes()} and + * {@link #successors(Object)}. + */ + @Override + public Set> edges() { + return new AbstractSet>() { + @Override + public UnmodifiableIterator> iterator() { + return EndpointPairIterator.of(AbstractBaseGraph.this); + } + + @Override + public int size() { + return Ints.saturatedCast(edgeCount()); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + // Mostly safe: We check contains(u) before calling successors(u), so we perform unsafe + // operations only in weird cases like checking for an EndpointPair in a + // Graph. + @SuppressWarnings("unchecked") + @Override + public boolean contains(Object obj) { + if (!(obj instanceof EndpointPair)) { + return false; + } + EndpointPair endpointPair = (EndpointPair) obj; + return isOrderingCompatible(endpointPair) + && nodes().contains(endpointPair.nodeU()) + && successors((N) endpointPair.nodeU()).contains(endpointPair.nodeV()); + } + }; + } + + @Override + public ElementOrder incidentEdgeOrder() { + return ElementOrder.unordered(); + } + + @Override + public Set> incidentEdges(N node) { + checkNotNull(node); + checkArgument(nodes().contains(node), "Node %s is not an element of this graph.", node); + return new IncidentEdgeSet(this, node) { + @Override + public UnmodifiableIterator> iterator() { + if (graph.isDirected()) { + return Iterators.unmodifiableIterator( + Iterators.concat( + Iterators.transform( + graph.predecessors(node).iterator(), + (N predecessor) -> EndpointPair.ordered(predecessor, node)), + Iterators.transform( + // filter out 'node' from successors (already covered by predecessors, above) + Sets.difference(graph.successors(node), ImmutableSet.of(node)).iterator(), + (N successor) -> EndpointPair.ordered(node, successor)))); + } else { + return Iterators.unmodifiableIterator( + Iterators.transform( + graph.adjacentNodes(node).iterator(), + (N adjacentNode) -> EndpointPair.unordered(node, adjacentNode))); + } + } + }; + } + + @Override + public int degree(N node) { + if (isDirected()) { + return IntMath.saturatedAdd(predecessors(node).size(), successors(node).size()); + } else { + Set neighbors = adjacentNodes(node); + int selfLoopCount = (allowsSelfLoops() && neighbors.contains(node)) ? 1 : 0; + return IntMath.saturatedAdd(neighbors.size(), selfLoopCount); + } + } + + @Override + public int inDegree(N node) { + return isDirected() ? predecessors(node).size() : degree(node); + } + + @Override + public int outDegree(N node) { + return isDirected() ? successors(node).size() : degree(node); + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + checkNotNull(nodeU); + checkNotNull(nodeV); + return nodes().contains(nodeU) && successors(nodeU).contains(nodeV); + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + checkNotNull(endpoints); + if (!isOrderingCompatible(endpoints)) { + return false; + } + N nodeU = endpoints.nodeU(); + N nodeV = endpoints.nodeV(); + return nodes().contains(nodeU) && successors(nodeU).contains(nodeV); + } + + /** + * Throws {@code IllegalArgumentException} if the ordering of {@code endpoints} is not compatible + * with the directionality of this graph. + */ + protected final void validateEndpoints(EndpointPair endpoints) { + checkNotNull(endpoints); + checkArgument(isOrderingCompatible(endpoints), ENDPOINTS_MISMATCH); + } + + /** + * Returns {@code true} iff {@code endpoints}' ordering is compatible with the directionality of + * this graph. + */ + protected final boolean isOrderingCompatible(EndpointPair endpoints) { + return endpoints.isOrdered() == this.isDirected(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractDirectedNetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractDirectedNetworkConnections.java new file mode 100644 index 0000000000000..7edf10d69dda1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractDirectedNetworkConnections.java @@ -0,0 +1,147 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Sets; +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; +import org.apache.iotdb.google.common.math.IntMath; + +import java.util.AbstractSet; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; +import static org.apache.iotdb.google.common.graph.Graphs.checkPositive; + +/** + * A base implementation of {@link NetworkConnections} for directed networks. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractDirectedNetworkConnections implements NetworkConnections { + /** Keys are edges incoming to the origin node, values are the source node. */ + final Map inEdgeMap; + + /** Keys are edges outgoing from the origin node, values are the target node. */ + final Map outEdgeMap; + + private int selfLoopCount; + + AbstractDirectedNetworkConnections(Map inEdgeMap, Map outEdgeMap, int selfLoopCount) { + this.inEdgeMap = checkNotNull(inEdgeMap); + this.outEdgeMap = checkNotNull(outEdgeMap); + this.selfLoopCount = checkNonNegative(selfLoopCount); + checkState(selfLoopCount <= inEdgeMap.size() && selfLoopCount <= outEdgeMap.size()); + } + + @Override + public Set adjacentNodes() { + return Sets.union(predecessors(), successors()); + } + + @Override + public Set incidentEdges() { + return new AbstractSet() { + @Override + public UnmodifiableIterator iterator() { + Iterable incidentEdges = + (selfLoopCount == 0) + ? Iterables.concat(inEdgeMap.keySet(), outEdgeMap.keySet()) + : Sets.union(inEdgeMap.keySet(), outEdgeMap.keySet()); + return Iterators.unmodifiableIterator(incidentEdges.iterator()); + } + + @Override + public int size() { + return IntMath.saturatedAdd(inEdgeMap.size(), outEdgeMap.size() - selfLoopCount); + } + + @Override + public boolean contains(Object obj) { + return inEdgeMap.containsKey(obj) || outEdgeMap.containsKey(obj); + } + }; + } + + @Override + public Set inEdges() { + return Collections.unmodifiableSet(inEdgeMap.keySet()); + } + + @Override + public Set outEdges() { + return Collections.unmodifiableSet(outEdgeMap.keySet()); + } + + @Override + public N adjacentNode(E edge) { + // Since the reference node is defined to be 'source' for directed graphs, + // we can assume this edge lives in the set of outgoing edges. + // (We're relying on callers to call this method only with an edge that's in the graph.) + return requireNonNull(outEdgeMap.get(edge)); + } + + @Override + public N removeInEdge(E edge, boolean isSelfLoop) { + if (isSelfLoop) { + checkNonNegative(--selfLoopCount); + } + N previousNode = inEdgeMap.remove(edge); + // We're relying on callers to call this method only with an edge that's in the graph. + return requireNonNull(previousNode); + } + + @Override + public N removeOutEdge(E edge) { + N previousNode = outEdgeMap.remove(edge); + // We're relying on callers to call this method only with an edge that's in the graph. + return requireNonNull(previousNode); + } + + @Override + public void addInEdge(E edge, N node, boolean isSelfLoop) { + checkNotNull(edge); + checkNotNull(node); + + if (isSelfLoop) { + checkPositive(++selfLoopCount); + } + N previousNode = inEdgeMap.put(edge, node); + checkState(previousNode == null); + } + + @Override + public void addOutEdge(E edge, N node) { + checkNotNull(edge); + checkNotNull(node); + + N previousNode = outEdgeMap.put(edge, node); + checkState(previousNode == null); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractGraph.java new file mode 100644 index 0000000000000..43d596615c004 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractGraph.java @@ -0,0 +1,68 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +/** + * This class provides a skeletal implementation of {@link Graph}. It is recommended to extend this + * class rather than implement {@link Graph} directly. + * + * @author James Sexton + * @param Node parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public abstract class AbstractGraph extends AbstractBaseGraph implements Graph { + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Graph)) { + return false; + } + Graph other = (Graph) obj; + + return isDirected() == other.isDirected() + && nodes().equals(other.nodes()) + && edges().equals(other.edges()); + } + + @Override + public final int hashCode() { + return edges().hashCode(); + } + + /** Returns a string representation of this graph. */ + @Override + public String toString() { + return "isDirected: " + + isDirected() + + ", allowsSelfLoops: " + + allowsSelfLoops() + + ", nodes: " + + nodes() + + ", edges: " + + edges(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractGraphBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractGraphBuilder.java new file mode 100644 index 0000000000000..b7958bba9eddc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractGraphBuilder.java @@ -0,0 +1,47 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.base.Optional; + +/** + * A base class for builders that construct graphs with user-defined properties. + * + * @author James Sexton + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractGraphBuilder { + final boolean directed; + boolean allowsSelfLoops = false; + ElementOrder nodeOrder = ElementOrder.insertion(); + ElementOrder incidentEdgeOrder = ElementOrder.unordered(); + + Optional expectedNodeCount = Optional.absent(); + + /** + * Creates a new instance with the specified edge directionality. + * + * @param directed if true, creates an instance for graphs whose edges are each directed; if + * false, creates an instance for graphs whose edges are each undirected. + */ + AbstractGraphBuilder(boolean directed) { + this.directed = directed; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractNetwork.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractNetwork.java new file mode 100644 index 0000000000000..81372713e8253 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractNetwork.java @@ -0,0 +1,291 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Sets; +import org.apache.iotdb.google.common.math.IntMath; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static java.util.Collections.unmodifiableSet; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH; +import static org.apache.iotdb.google.common.graph.GraphConstants.MULTIPLE_EDGES_CONNECTING; + +/** + * This class provides a skeletal implementation of {@link Network}. It is recommended to extend + * this class rather than implement {@link Network} directly. + * + *

The methods implemented in this class should not be overridden unless the subclass admits a + * more efficient implementation. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public abstract class AbstractNetwork implements Network { + + @Override + public Graph asGraph() { + return new AbstractGraph() { + @Override + public Set nodes() { + return AbstractNetwork.this.nodes(); + } + + @Override + public Set> edges() { + if (allowsParallelEdges()) { + return super.edges(); // Defer to AbstractGraph implementation. + } + + // Optimized implementation assumes no parallel edges (1:1 edge to EndpointPair mapping). + return new AbstractSet>() { + @Override + public Iterator> iterator() { + return Iterators.transform( + AbstractNetwork.this.edges().iterator(), edge -> incidentNodes(edge)); + } + + @Override + public int size() { + return AbstractNetwork.this.edges().size(); + } + + // Mostly safe: We check contains(u) before calling successors(u), so we perform unsafe + // operations only in weird cases like checking for an EndpointPair in a + // Network. + @SuppressWarnings("unchecked") + @Override + public boolean contains(Object obj) { + if (!(obj instanceof EndpointPair)) { + return false; + } + EndpointPair endpointPair = (EndpointPair) obj; + return isOrderingCompatible(endpointPair) + && nodes().contains(endpointPair.nodeU()) + && successors((N) endpointPair.nodeU()).contains(endpointPair.nodeV()); + } + }; + } + + @Override + public ElementOrder nodeOrder() { + return AbstractNetwork.this.nodeOrder(); + } + + @Override + public ElementOrder incidentEdgeOrder() { + // TODO(b/142723300): Return AbstractNetwork.this.incidentEdgeOrder() once Network has that + // method. + return ElementOrder.unordered(); + } + + @Override + public boolean isDirected() { + return AbstractNetwork.this.isDirected(); + } + + @Override + public boolean allowsSelfLoops() { + return AbstractNetwork.this.allowsSelfLoops(); + } + + @Override + public Set adjacentNodes(N node) { + return AbstractNetwork.this.adjacentNodes(node); + } + + @Override + public Set predecessors(N node) { + return AbstractNetwork.this.predecessors(node); + } + + @Override + public Set successors(N node) { + return AbstractNetwork.this.successors(node); + } + + // DO NOT override the AbstractGraph *degree() implementations. + }; + } + + @Override + public int degree(N node) { + if (isDirected()) { + return IntMath.saturatedAdd(inEdges(node).size(), outEdges(node).size()); + } else { + return IntMath.saturatedAdd(incidentEdges(node).size(), edgesConnecting(node, node).size()); + } + } + + @Override + public int inDegree(N node) { + return isDirected() ? inEdges(node).size() : degree(node); + } + + @Override + public int outDegree(N node) { + return isDirected() ? outEdges(node).size() : degree(node); + } + + @Override + public Set adjacentEdges(E edge) { + EndpointPair endpointPair = incidentNodes(edge); // Verifies that edge is in this network. + Set endpointPairIncidentEdges = + Sets.union(incidentEdges(endpointPair.nodeU()), incidentEdges(endpointPair.nodeV())); + return Sets.difference(endpointPairIncidentEdges, ImmutableSet.of(edge)); + } + + @Override + public Set edgesConnecting(N nodeU, N nodeV) { + Set outEdgesU = outEdges(nodeU); + Set inEdgesV = inEdges(nodeV); + return outEdgesU.size() <= inEdgesV.size() + ? unmodifiableSet(Sets.filter(outEdgesU, connectedPredicate(nodeU, nodeV))) + : unmodifiableSet(Sets.filter(inEdgesV, connectedPredicate(nodeV, nodeU))); + } + + @Override + public Set edgesConnecting(EndpointPair endpoints) { + validateEndpoints(endpoints); + return edgesConnecting(endpoints.nodeU(), endpoints.nodeV()); + } + + private Predicate connectedPredicate(final N nodePresent, final N nodeToCheck) { + return new Predicate() { + @Override + public boolean apply(E edge) { + return incidentNodes(edge).adjacentNode(nodePresent).equals(nodeToCheck); + } + }; + } + + @Override + public Optional edgeConnecting(N nodeU, N nodeV) { + return Optional.ofNullable(edgeConnectingOrNull(nodeU, nodeV)); + } + + @Override + public Optional edgeConnecting(EndpointPair endpoints) { + validateEndpoints(endpoints); + return edgeConnecting(endpoints.nodeU(), endpoints.nodeV()); + } + + @Override + public E edgeConnectingOrNull(N nodeU, N nodeV) { + Set edgesConnecting = edgesConnecting(nodeU, nodeV); + switch (edgesConnecting.size()) { + case 0: + return null; + case 1: + return edgesConnecting.iterator().next(); + default: + throw new IllegalArgumentException(String.format(MULTIPLE_EDGES_CONNECTING, nodeU, nodeV)); + } + } + + @Override + public E edgeConnectingOrNull(EndpointPair endpoints) { + validateEndpoints(endpoints); + return edgeConnectingOrNull(endpoints.nodeU(), endpoints.nodeV()); + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + checkNotNull(nodeU); + checkNotNull(nodeV); + return nodes().contains(nodeU) && successors(nodeU).contains(nodeV); + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + checkNotNull(endpoints); + if (!isOrderingCompatible(endpoints)) { + return false; + } + return hasEdgeConnecting(endpoints.nodeU(), endpoints.nodeV()); + } + + /** + * Throws an IllegalArgumentException if the ordering of {@code endpoints} is not compatible with + * the directionality of this graph. + */ + protected final void validateEndpoints(EndpointPair endpoints) { + checkNotNull(endpoints); + checkArgument(isOrderingCompatible(endpoints), ENDPOINTS_MISMATCH); + } + + protected final boolean isOrderingCompatible(EndpointPair endpoints) { + return endpoints.isOrdered() == this.isDirected(); + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Network)) { + return false; + } + Network other = (Network) obj; + + return isDirected() == other.isDirected() + && nodes().equals(other.nodes()) + && edgeIncidentNodesMap(this).equals(edgeIncidentNodesMap(other)); + } + + @Override + public final int hashCode() { + return edgeIncidentNodesMap(this).hashCode(); + } + + /** Returns a string representation of this network. */ + @Override + public String toString() { + return "isDirected: " + + isDirected() + + ", allowsParallelEdges: " + + allowsParallelEdges() + + ", allowsSelfLoops: " + + allowsSelfLoops() + + ", nodes: " + + nodes() + + ", edges: " + + edgeIncidentNodesMap(this); + } + + private static Map> edgeIncidentNodesMap(final Network network) { + return Maps.asMap(network.edges(), network::incidentNodes); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractUndirectedNetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractUndirectedNetworkConnections.java new file mode 100644 index 0000000000000..05863b647436c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractUndirectedNetworkConnections.java @@ -0,0 +1,104 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * A base implementation of {@link NetworkConnections} for undirected networks. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractUndirectedNetworkConnections implements NetworkConnections { + /** Keys are edges incident to the origin node, values are the node at the other end. */ + final Map incidentEdgeMap; + + AbstractUndirectedNetworkConnections(Map incidentEdgeMap) { + this.incidentEdgeMap = checkNotNull(incidentEdgeMap); + } + + @Override + public Set predecessors() { + return adjacentNodes(); + } + + @Override + public Set successors() { + return adjacentNodes(); + } + + @Override + public Set incidentEdges() { + return Collections.unmodifiableSet(incidentEdgeMap.keySet()); + } + + @Override + public Set inEdges() { + return incidentEdges(); + } + + @Override + public Set outEdges() { + return incidentEdges(); + } + + @Override + public N adjacentNode(E edge) { + // We're relying on callers to call this method only with an edge that's in the graph. + return requireNonNull(incidentEdgeMap.get(edge)); + } + + @Override + public N removeInEdge(E edge, boolean isSelfLoop) { + if (!isSelfLoop) { + return removeOutEdge(edge); + } + return null; + } + + @Override + public N removeOutEdge(E edge) { + N previousNode = incidentEdgeMap.remove(edge); + // We're relying on callers to call this method only with an edge that's in the graph. + return requireNonNull(previousNode); + } + + @Override + public void addInEdge(E edge, N node, boolean isSelfLoop) { + if (!isSelfLoop) { + addOutEdge(edge, node); + } + } + + @Override + public void addOutEdge(E edge, N node) { + N previousNode = incidentEdgeMap.put(edge, node); + checkState(previousNode == null); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractValueGraph.java new file mode 100644 index 0000000000000..503555389f9b4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/AbstractValueGraph.java @@ -0,0 +1,163 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.collect.Maps; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +/** + * This class provides a skeletal implementation of {@link ValueGraph}. It is recommended to extend + * this class rather than implement {@link ValueGraph} directly. + * + *

The methods implemented in this class should not be overridden unless the subclass admits a + * more efficient implementation. + * + * @author James Sexton + * @param Node parameter type + * @param Value parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public abstract class AbstractValueGraph extends AbstractBaseGraph + implements ValueGraph { + + @Override + public Graph asGraph() { + return new AbstractGraph() { + @Override + public Set nodes() { + return AbstractValueGraph.this.nodes(); + } + + @Override + public Set> edges() { + return AbstractValueGraph.this.edges(); + } + + @Override + public boolean isDirected() { + return AbstractValueGraph.this.isDirected(); + } + + @Override + public boolean allowsSelfLoops() { + return AbstractValueGraph.this.allowsSelfLoops(); + } + + @Override + public ElementOrder nodeOrder() { + return AbstractValueGraph.this.nodeOrder(); + } + + @Override + public ElementOrder incidentEdgeOrder() { + return AbstractValueGraph.this.incidentEdgeOrder(); + } + + @Override + public Set adjacentNodes(N node) { + return AbstractValueGraph.this.adjacentNodes(node); + } + + @Override + public Set predecessors(N node) { + return AbstractValueGraph.this.predecessors(node); + } + + @Override + public Set successors(N node) { + return AbstractValueGraph.this.successors(node); + } + + @Override + public int degree(N node) { + return AbstractValueGraph.this.degree(node); + } + + @Override + public int inDegree(N node) { + return AbstractValueGraph.this.inDegree(node); + } + + @Override + public int outDegree(N node) { + return AbstractValueGraph.this.outDegree(node); + } + }; + } + + @Override + public Optional edgeValue(N nodeU, N nodeV) { + return Optional.ofNullable(edgeValueOrDefault(nodeU, nodeV, null)); + } + + @Override + public Optional edgeValue(EndpointPair endpoints) { + return Optional.ofNullable(edgeValueOrDefault(endpoints, null)); + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ValueGraph)) { + return false; + } + ValueGraph other = (ValueGraph) obj; + + return isDirected() == other.isDirected() + && nodes().equals(other.nodes()) + && edgeValueMap(this).equals(edgeValueMap(other)); + } + + @Override + public final int hashCode() { + return edgeValueMap(this).hashCode(); + } + + /** Returns a string representation of this graph. */ + @Override + public String toString() { + return "isDirected: " + + isDirected() + + ", allowsSelfLoops: " + + allowsSelfLoops() + + ", nodes: " + + nodes() + + ", edges: " + + edgeValueMap(this); + } + + private static Map, V> edgeValueMap(final ValueGraph graph) { + return Maps.asMap( + graph.edges(), + edge -> + // requireNonNull is safe because the endpoint pair comes from the graph. + requireNonNull(graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null))); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/BaseGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/BaseGraph.java new file mode 100644 index 0000000000000..df0f0e8a39dbb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/BaseGraph.java @@ -0,0 +1,179 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Set; + +/** + * A non-public interface for the methods shared between {@link Graph} and {@link ValueGraph}. + * + * @author James Sexton + * @param Node parameter type + */ +@ElementTypesAreNonnullByDefault +interface BaseGraph extends SuccessorsFunction, PredecessorsFunction { + // + // Graph-level accessors + // + + /** Returns all nodes in this graph, in the order specified by {@link #nodeOrder()}. */ + Set nodes(); + + /** Returns all edges in this graph. */ + Set> edges(); + + // + // Graph properties + // + + /** + * Returns true if the edges in this graph are directed. Directed edges connect a {@link + * EndpointPair#source() source node} to a {@link EndpointPair#target() target node}, while + * undirected edges connect a pair of nodes to each other. + */ + boolean isDirected(); + + /** + * Returns true if this graph allows self-loops (edges that connect a node to itself). Attempting + * to add a self-loop to a graph that does not allow them will throw an {@link + * IllegalArgumentException}. + */ + boolean allowsSelfLoops(); + + /** Returns the order of iteration for the elements of {@link #nodes()}. */ + ElementOrder nodeOrder(); + + /** + * Returns an {@link ElementOrder} that specifies the order of iteration for the elements of + * {@link #edges()}, {@link #adjacentNodes(Object)}, {@link #predecessors(Object)}, {@link + * #successors(Object)} and {@link #incidentEdges(Object)}. + * + * @since 29.0 + */ + ElementOrder incidentEdgeOrder(); + + // + // Element-level accessors + // + + /** + * Returns the nodes which have an incident edge in common with {@code node} in this graph. + * + *

This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + Set adjacentNodes(N node); + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s incoming edges against the direction (if any) of the edge. + * + *

In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set predecessors(N node); + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s outgoing edges in the direction (if any) of the edge. + * + *

In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. + * + *

This is not the same as "all nodes reachable from {@code node} by following outgoing + * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set successors(N node); + + /** + * Returns the edges in this graph whose endpoints include {@code node}. + * + *

This is equal to the union of incoming and outgoing edges. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + * @since 24.0 + */ + Set> incidentEdges(N node); + + /** + * Returns the count of {@code node}'s incident edges, counting self-loops twice (equivalently, + * the number of times an edge touches {@code node}). + * + *

For directed graphs, this is equal to {@code inDegree(node) + outDegree(node)}. + * + *

For undirected graphs, this is equal to {@code incidentEdges(node).size()} + (number of + * self-loops incident to {@code node}). + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + int degree(N node); + + /** + * Returns the count of {@code node}'s incoming edges (equal to {@code predecessors(node).size()}) + * in a directed graph. In an undirected graph, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + int inDegree(N node); + + /** + * Returns the count of {@code node}'s outgoing edges (equal to {@code successors(node).size()}) + * in a directed graph. In an undirected graph, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + int outDegree(N node); + + /** + * Returns true if there is an edge that directly connects {@code nodeU} to {@code nodeV}. This is + * equivalent to {@code nodes().contains(nodeU) && successors(nodeU).contains(nodeV)}. + * + *

In an undirected graph, this is equal to {@code hasEdgeConnecting(nodeV, nodeU)}. + * + * @since 23.0 + */ + boolean hasEdgeConnecting(N nodeU, N nodeV); + + /** + * Returns true if there is an edge that directly connects {@code endpoints} (in the order, if + * any, specified by {@code endpoints}). This is equivalent to {@code + * edges().contains(endpoints)}. + * + *

Unlike the other {@code EndpointPair}-accepting methods, this method does not throw if the + * endpoints are unordered; it simply returns false. This is for consistency with the behavior of + * {@link Collection#contains(Object)} (which does not generally throw if the object cannot be + * present in the collection), and the desire to have this method's behavior be compatible with + * {@code edges().contains(endpoints)}. + * + * @since 27.1 + */ + boolean hasEdgeConnecting(EndpointPair endpoints); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedGraphConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedGraphConnections.java new file mode 100644 index 0000000000000..b278ad8476b99 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedGraphConnections.java @@ -0,0 +1,558 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.AbstractIterator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; + +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_CAPACITY; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_LOAD_FACTOR; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; +import static org.apache.iotdb.google.common.graph.Graphs.checkPositive; + +/** + * An implementation of {@link GraphConnections} for directed graphs. + * + * @author James Sexton + * @author Jens Nyman + * @param Node parameter type + * @param Value parameter type + */ +@ElementTypesAreNonnullByDefault +final class DirectedGraphConnections implements GraphConnections { + /** + * A wrapper class to indicate a node is both a predecessor and successor while still providing + * the successor value. + */ + private static final class PredAndSucc { + private final Object successorValue; + + PredAndSucc(Object successorValue) { + this.successorValue = successorValue; + } + } + + /** + * A value class representing single connection between the origin node and another node. + * + *

There can be two types of connections (predecessor and successor), which is represented by + * the two implementations. + */ + private abstract static class NodeConnection { + final N node; + + NodeConnection(N node) { + this.node = checkNotNull(node); + } + + static final class Pred extends NodeConnection { + Pred(N node) { + super(node); + } + + @Override + public boolean equals(Object that) { + if (that instanceof Pred) { + return this.node.equals(((Pred) that).node); + } else { + return false; + } + } + + @Override + public int hashCode() { + // Adding the class hashCode to avoid a clash with Succ instances. + return Pred.class.hashCode() + node.hashCode(); + } + } + + static final class Succ extends NodeConnection { + Succ(N node) { + super(node); + } + + @Override + public boolean equals(Object that) { + if (that instanceof Succ) { + return this.node.equals(((Succ) that).node); + } else { + return false; + } + } + + @Override + public int hashCode() { + // Adding the class hashCode to avoid a clash with Pred instances. + return Succ.class.hashCode() + node.hashCode(); + } + } + } + + private static final Object PRED = new Object(); + + // Every value in this map must either be an instance of PredAndSucc with a successorValue of + // type V, PRED (representing predecessor), or an instance of type V (representing successor). + private final Map adjacentNodeValues; + + /** + * All node connections in this graph, in edge insertion order. + * + *

Note: This field and {@link #adjacentNodeValues} cannot be combined into a single + * LinkedHashMap because one target node may be mapped to both a predecessor and a successor. A + * LinkedHashMap combines two such edges into a single node-value pair, even though the edges may + * not have been inserted consecutively. + */ + private final List> orderedNodeConnections; + + private int predecessorCount; + private int successorCount; + + private DirectedGraphConnections( + Map adjacentNodeValues, + List> orderedNodeConnections, + int predecessorCount, + int successorCount) { + this.adjacentNodeValues = checkNotNull(adjacentNodeValues); + this.orderedNodeConnections = orderedNodeConnections; + this.predecessorCount = checkNonNegative(predecessorCount); + this.successorCount = checkNonNegative(successorCount); + checkState( + predecessorCount <= adjacentNodeValues.size() + && successorCount <= adjacentNodeValues.size()); + } + + static DirectedGraphConnections of(ElementOrder incidentEdgeOrder) { + // We store predecessors and successors in the same map, so double the initial capacity. + int initialCapacity = INNER_CAPACITY * 2; + + List> orderedNodeConnections; + switch (incidentEdgeOrder.type()) { + case UNORDERED: + orderedNodeConnections = null; + break; + case STABLE: + orderedNodeConnections = new ArrayList<>(); + break; + default: + throw new AssertionError(incidentEdgeOrder.type()); + } + + return new DirectedGraphConnections<>( + /* adjacentNodeValues= */ new HashMap(initialCapacity, INNER_LOAD_FACTOR), + orderedNodeConnections, + /* predecessorCount= */ 0, + /* successorCount= */ 0); + } + + static DirectedGraphConnections ofImmutable( + N thisNode, Iterable> incidentEdges, Function successorNodeToValueFn) { + checkNotNull(thisNode); + checkNotNull(successorNodeToValueFn); + + Map adjacentNodeValues = new HashMap<>(); + ImmutableList.Builder> orderedNodeConnectionsBuilder = + ImmutableList.builder(); + int predecessorCount = 0; + int successorCount = 0; + + for (EndpointPair incidentEdge : incidentEdges) { + if (incidentEdge.nodeU().equals(thisNode) && incidentEdge.nodeV().equals(thisNode)) { + // incidentEdge is a self-loop + + adjacentNodeValues.put(thisNode, new PredAndSucc(successorNodeToValueFn.apply(thisNode))); + + orderedNodeConnectionsBuilder.add(new NodeConnection.Pred<>(thisNode)); + orderedNodeConnectionsBuilder.add(new NodeConnection.Succ<>(thisNode)); + predecessorCount++; + successorCount++; + } else if (incidentEdge.nodeV().equals(thisNode)) { // incidentEdge is an inEdge + N predecessor = incidentEdge.nodeU(); + + Object existingValue = adjacentNodeValues.put(predecessor, PRED); + if (existingValue != null) { + adjacentNodeValues.put(predecessor, new PredAndSucc(existingValue)); + } + + orderedNodeConnectionsBuilder.add(new NodeConnection.Pred<>(predecessor)); + predecessorCount++; + } else { // incidentEdge is an outEdge + checkArgument(incidentEdge.nodeU().equals(thisNode)); + + N successor = incidentEdge.nodeV(); + V value = successorNodeToValueFn.apply(successor); + + Object existingValue = adjacentNodeValues.put(successor, value); + if (existingValue != null) { + checkArgument(existingValue == PRED); + adjacentNodeValues.put(successor, new PredAndSucc(value)); + } + + orderedNodeConnectionsBuilder.add(new NodeConnection.Succ<>(successor)); + successorCount++; + } + } + + return new DirectedGraphConnections<>( + adjacentNodeValues, + orderedNodeConnectionsBuilder.build(), + predecessorCount, + successorCount); + } + + @Override + public Set adjacentNodes() { + if (orderedNodeConnections == null) { + return Collections.unmodifiableSet(adjacentNodeValues.keySet()); + } else { + return new AbstractSet() { + @Override + public UnmodifiableIterator iterator() { + Iterator> nodeConnections = orderedNodeConnections.iterator(); + Set seenNodes = new HashSet<>(); + return new AbstractIterator() { + @Override + protected N computeNext() { + while (nodeConnections.hasNext()) { + NodeConnection nodeConnection = nodeConnections.next(); + boolean added = seenNodes.add(nodeConnection.node); + if (added) { + return nodeConnection.node; + } + } + return endOfData(); + } + }; + } + + @Override + public int size() { + return adjacentNodeValues.size(); + } + + @Override + public boolean contains(Object obj) { + return adjacentNodeValues.containsKey(obj); + } + }; + } + } + + @Override + public Set predecessors() { + return new AbstractSet() { + @Override + public UnmodifiableIterator iterator() { + if (orderedNodeConnections == null) { + Iterator> entries = adjacentNodeValues.entrySet().iterator(); + return new AbstractIterator() { + @Override + protected N computeNext() { + while (entries.hasNext()) { + Entry entry = entries.next(); + if (isPredecessor(entry.getValue())) { + return entry.getKey(); + } + } + return endOfData(); + } + }; + } else { + Iterator> nodeConnections = orderedNodeConnections.iterator(); + return new AbstractIterator() { + @Override + protected N computeNext() { + while (nodeConnections.hasNext()) { + NodeConnection nodeConnection = nodeConnections.next(); + if (nodeConnection instanceof NodeConnection.Pred) { + return nodeConnection.node; + } + } + return endOfData(); + } + }; + } + } + + @Override + public int size() { + return predecessorCount; + } + + @Override + public boolean contains(Object obj) { + return isPredecessor(adjacentNodeValues.get(obj)); + } + }; + } + + @Override + public Set successors() { + return new AbstractSet() { + @Override + public UnmodifiableIterator iterator() { + if (orderedNodeConnections == null) { + Iterator> entries = adjacentNodeValues.entrySet().iterator(); + return new AbstractIterator() { + @Override + protected N computeNext() { + while (entries.hasNext()) { + Entry entry = entries.next(); + if (isSuccessor(entry.getValue())) { + return entry.getKey(); + } + } + return endOfData(); + } + }; + } else { + Iterator> nodeConnections = orderedNodeConnections.iterator(); + return new AbstractIterator() { + @Override + protected N computeNext() { + while (nodeConnections.hasNext()) { + NodeConnection nodeConnection = nodeConnections.next(); + if (nodeConnection instanceof NodeConnection.Succ) { + return nodeConnection.node; + } + } + return endOfData(); + } + }; + } + } + + @Override + public int size() { + return successorCount; + } + + @Override + public boolean contains(Object obj) { + return isSuccessor(adjacentNodeValues.get(obj)); + } + }; + } + + @Override + public Iterator> incidentEdgeIterator(N thisNode) { + checkNotNull(thisNode); + + Iterator> resultWithDoubleSelfLoop; + if (orderedNodeConnections == null) { + resultWithDoubleSelfLoop = + Iterators.concat( + Iterators.transform( + predecessors().iterator(), + (N predecessor) -> EndpointPair.ordered(predecessor, thisNode)), + Iterators.transform( + successors().iterator(), + (N successor) -> EndpointPair.ordered(thisNode, successor))); + } else { + resultWithDoubleSelfLoop = + Iterators.transform( + orderedNodeConnections.iterator(), + (NodeConnection connection) -> { + if (connection instanceof NodeConnection.Succ) { + return EndpointPair.ordered(thisNode, connection.node); + } else { + return EndpointPair.ordered(connection.node, thisNode); + } + }); + } + + AtomicBoolean alreadySeenSelfLoop = new AtomicBoolean(false); + return new AbstractIterator>() { + @Override + protected EndpointPair computeNext() { + while (resultWithDoubleSelfLoop.hasNext()) { + EndpointPair edge = resultWithDoubleSelfLoop.next(); + if (edge.nodeU().equals(edge.nodeV())) { + if (!alreadySeenSelfLoop.getAndSet(true)) { + return edge; + } + } else { + return edge; + } + } + return endOfData(); + } + }; + } + + @SuppressWarnings("unchecked") + @Override + public V value(N node) { + checkNotNull(node); + Object value = adjacentNodeValues.get(node); + if (value == PRED) { + return null; + } + if (value instanceof PredAndSucc) { + return (V) ((PredAndSucc) value).successorValue; + } + return (V) value; + } + + @SuppressWarnings("unchecked") + @Override + public void removePredecessor(N node) { + checkNotNull(node); + + Object previousValue = adjacentNodeValues.get(node); + boolean removedPredecessor; + + if (previousValue == PRED) { + adjacentNodeValues.remove(node); + removedPredecessor = true; + } else if (previousValue instanceof PredAndSucc) { + adjacentNodeValues.put((N) node, ((PredAndSucc) previousValue).successorValue); + removedPredecessor = true; + } else { + removedPredecessor = false; + } + + if (removedPredecessor) { + checkNonNegative(--predecessorCount); + + if (orderedNodeConnections != null) { + orderedNodeConnections.remove(new NodeConnection.Pred<>(node)); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public V removeSuccessor(Object node) { + checkNotNull(node); + Object previousValue = adjacentNodeValues.get(node); + Object removedValue; + + if (previousValue == null || previousValue == PRED) { + removedValue = null; + } else if (previousValue instanceof PredAndSucc) { + adjacentNodeValues.put((N) node, PRED); + removedValue = ((PredAndSucc) previousValue).successorValue; + } else { // successor + adjacentNodeValues.remove(node); + removedValue = previousValue; + } + + if (removedValue != null) { + checkNonNegative(--successorCount); + + if (orderedNodeConnections != null) { + orderedNodeConnections.remove(new NodeConnection.Succ<>((N) node)); + } + } + + /* + * TODO(cpovirk): `return (V) removedValue` once our checker permits that. + * + * (We promoted a class of warnings into errors because sometimes they indicate real problems. + * But now we need to "undo" some instance of spurious errors, as discussed in + * https://github.com/jspecify/checker-framework/issues/8.) + */ + return removedValue == null ? null : (V) removedValue; + } + + @Override + public void addPredecessor(N node, V unused) { + Object previousValue = adjacentNodeValues.put(node, PRED); + boolean addedPredecessor; + + if (previousValue == null) { + addedPredecessor = true; + } else if (previousValue instanceof PredAndSucc) { + // Restore previous PredAndSucc object. + adjacentNodeValues.put(node, previousValue); + addedPredecessor = false; + } else if (previousValue != PRED) { // successor + // Do NOT use method parameter value 'unused'. In directed graphs, successors store the value. + adjacentNodeValues.put(node, new PredAndSucc(previousValue)); + addedPredecessor = true; + } else { + addedPredecessor = false; + } + + if (addedPredecessor) { + checkPositive(++predecessorCount); + + if (orderedNodeConnections != null) { + orderedNodeConnections.add(new NodeConnection.Pred<>(node)); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public V addSuccessor(N node, V value) { + Object previousValue = adjacentNodeValues.put(node, value); + Object previousSuccessor; + + if (previousValue == null) { + previousSuccessor = null; + } else if (previousValue instanceof PredAndSucc) { + adjacentNodeValues.put(node, new PredAndSucc(value)); + previousSuccessor = ((PredAndSucc) previousValue).successorValue; + } else if (previousValue == PRED) { + adjacentNodeValues.put(node, new PredAndSucc(value)); + previousSuccessor = null; + } else { // successor + previousSuccessor = previousValue; + } + + if (previousSuccessor == null) { + checkPositive(++successorCount); + + if (orderedNodeConnections != null) { + orderedNodeConnections.add(new NodeConnection.Succ<>(node)); + } + } + + // See the comment on the similar cast in removeSuccessor. + return previousSuccessor == null ? null : (V) previousSuccessor; + } + + private static boolean isPredecessor(Object value) { + return (value == PRED) || (value instanceof PredAndSucc); + } + + private static boolean isSuccessor(Object value) { + return (value != PRED) && (value != null); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedMultiNetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedMultiNetworkConnections.java new file mode 100644 index 0000000000000..564acab9b30d6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedMultiNetworkConnections.java @@ -0,0 +1,148 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.HashMultiset; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Multiset; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_CAPACITY; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_LOAD_FACTOR; + +/** + * An implementation of {@link NetworkConnections} for directed networks with parallel edges. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +final class DirectedMultiNetworkConnections extends AbstractDirectedNetworkConnections { + + private DirectedMultiNetworkConnections( + Map inEdges, Map outEdges, int selfLoopCount) { + super(inEdges, outEdges, selfLoopCount); + } + + static DirectedMultiNetworkConnections of() { + return new DirectedMultiNetworkConnections<>( + new HashMap(INNER_CAPACITY, INNER_LOAD_FACTOR), + new HashMap(INNER_CAPACITY, INNER_LOAD_FACTOR), + 0); + } + + static DirectedMultiNetworkConnections ofImmutable( + Map inEdges, Map outEdges, int selfLoopCount) { + return new DirectedMultiNetworkConnections<>( + ImmutableMap.copyOf(inEdges), ImmutableMap.copyOf(outEdges), selfLoopCount); + } + + private transient Reference> predecessorsReference; + + @Override + public Set predecessors() { + return Collections.unmodifiableSet(predecessorsMultiset().elementSet()); + } + + private Multiset predecessorsMultiset() { + Multiset predecessors = getReference(predecessorsReference); + if (predecessors == null) { + predecessors = HashMultiset.create(inEdgeMap.values()); + predecessorsReference = new SoftReference<>(predecessors); + } + return predecessors; + } + + private transient Reference> successorsReference; + + @Override + public Set successors() { + return Collections.unmodifiableSet(successorsMultiset().elementSet()); + } + + private Multiset successorsMultiset() { + Multiset successors = getReference(successorsReference); + if (successors == null) { + successors = HashMultiset.create(outEdgeMap.values()); + successorsReference = new SoftReference<>(successors); + } + return successors; + } + + @Override + public Set edgesConnecting(N node) { + return new MultiEdgesConnecting(outEdgeMap, node) { + @Override + public int size() { + return successorsMultiset().count(node); + } + }; + } + + @Override + public N removeInEdge(E edge, boolean isSelfLoop) { + N node = super.removeInEdge(edge, isSelfLoop); + Multiset predecessors = getReference(predecessorsReference); + if (predecessors != null) { + checkState(predecessors.remove(node)); + } + return node; + } + + @Override + public N removeOutEdge(E edge) { + N node = super.removeOutEdge(edge); + Multiset successors = getReference(successorsReference); + if (successors != null) { + checkState(successors.remove(node)); + } + return node; + } + + @Override + public void addInEdge(E edge, N node, boolean isSelfLoop) { + super.addInEdge(edge, node, isSelfLoop); + Multiset predecessors = getReference(predecessorsReference); + if (predecessors != null) { + checkState(predecessors.add(node)); + } + } + + @Override + public void addOutEdge(E edge, N node) { + super.addOutEdge(edge, node); + Multiset successors = getReference(successorsReference); + if (successors != null) { + checkState(successors.add(node)); + } + } + + private static T getReference(Reference reference) { + return (reference == null) ? null : reference.get(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedNetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedNetworkConnections.java new file mode 100644 index 0000000000000..cbfe1938dc508 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/DirectedNetworkConnections.java @@ -0,0 +1,71 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.BiMap; +import org.apache.iotdb.google.common.collect.HashBiMap; +import org.apache.iotdb.google.common.collect.ImmutableBiMap; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.graph.GraphConstants.EXPECTED_DEGREE; + +/** + * An implementation of {@link NetworkConnections} for directed networks. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +final class DirectedNetworkConnections extends AbstractDirectedNetworkConnections { + + DirectedNetworkConnections(Map inEdgeMap, Map outEdgeMap, int selfLoopCount) { + super(inEdgeMap, outEdgeMap, selfLoopCount); + } + + static DirectedNetworkConnections of() { + return new DirectedNetworkConnections<>( + HashBiMap.create(EXPECTED_DEGREE), HashBiMap.create(EXPECTED_DEGREE), 0); + } + + static DirectedNetworkConnections ofImmutable( + Map inEdges, Map outEdges, int selfLoopCount) { + return new DirectedNetworkConnections<>( + ImmutableBiMap.copyOf(inEdges), ImmutableBiMap.copyOf(outEdges), selfLoopCount); + } + + @Override + public Set predecessors() { + return Collections.unmodifiableSet(((BiMap) inEdgeMap).values()); + } + + @Override + public Set successors() { + return Collections.unmodifiableSet(((BiMap) outEdgeMap).values()); + } + + @Override + public Set edgesConnecting(N node) { + return new EdgesConnecting<>(((BiMap) outEdgeMap).inverse(), node); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EdgesConnecting.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EdgesConnecting.java new file mode 100644 index 0000000000000..74254037ae925 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EdgesConnecting.java @@ -0,0 +1,73 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; + +import java.util.AbstractSet; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A class to represent the set of edges connecting an (implicit) origin node to a target node. + * + *

The {@link #nodeToOutEdge} map means this class only works on networks without parallel edges. + * See {@link MultiEdgesConnecting} for a class that works with parallel edges. + * + * @author James Sexton + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +final class EdgesConnecting extends AbstractSet { + + private final Map nodeToOutEdge; + private final Object targetNode; + + EdgesConnecting(Map nodeToEdgeMap, Object targetNode) { + this.nodeToOutEdge = checkNotNull(nodeToEdgeMap); + this.targetNode = checkNotNull(targetNode); + } + + @Override + public UnmodifiableIterator iterator() { + E connectingEdge = getConnectingEdge(); + return (connectingEdge == null) + ? ImmutableSet.of().iterator() + : Iterators.singletonIterator(connectingEdge); + } + + @Override + public int size() { + return getConnectingEdge() == null ? 0 : 1; + } + + @Override + public boolean contains(Object edge) { + E connectingEdge = getConnectingEdge(); + return (connectingEdge != null && connectingEdge.equals(edge)); + } + + private E getConnectingEdge() { + return nodeToOutEdge.get(targetNode); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ElementOrder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ElementOrder.java new file mode 100644 index 0000000000000..9b0c4d1d40123 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ElementOrder.java @@ -0,0 +1,209 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.MoreObjects.ToStringHelper; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Ordering; + +import java.util.Comparator; +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * Used to represent the order of elements in a data structure that supports different options for + * iteration order guarantees. + * + *

Example usage: + * + *

{@code
+ * MutableGraph graph =
+ *     GraphBuilder.directed().nodeOrder(ElementOrder.natural()).build();
+ * }
+ * + * @author Joshua O'Madadhain + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class ElementOrder { + private final Type type; + + @SuppressWarnings("Immutable") // Hopefully the comparator provided is immutable! + private final Comparator comparator; + + /** + * The type of ordering that this object specifies. + * + *
    + *
  • UNORDERED: no order is guaranteed. + *
  • STABLE: ordering is guaranteed to follow a pattern that won't change between releases. + * Some methods may have stronger guarantees. + *
  • INSERTION: insertion ordering is guaranteed. + *
  • SORTED: ordering according to a supplied comparator is guaranteed. + *
+ */ + public enum Type { + UNORDERED, + STABLE, + INSERTION, + SORTED + } + + private ElementOrder(Type type, Comparator comparator) { + this.type = checkNotNull(type); + this.comparator = comparator; + checkState((type == Type.SORTED) == (comparator != null)); + } + + /** Returns an instance which specifies that no ordering is guaranteed. */ + public static ElementOrder unordered() { + return new ElementOrder<>(Type.UNORDERED, null); + } + + /** + * Returns an instance which specifies that ordering is guaranteed to be always be the same across + * iterations, and across releases. Some methods may have stronger guarantees. + * + *

This instance is only useful in combination with {@code incidentEdgeOrder}, e.g. {@code + * graphBuilder.incidentEdgeOrder(ElementOrder.stable())}. + * + *

In combination with {@code incidentEdgeOrder}

+ * + *

{@code incidentEdgeOrder(ElementOrder.stable())} guarantees the ordering of the returned + * collections of the following methods: + * + *

    + *
  • For {@link Graph} and {@link ValueGraph}: + *
      + *
    • {@code edges()}: Stable order + *
    • {@code adjacentNodes(node)}: Connecting edge insertion order + *
    • {@code predecessors(node)}: Connecting edge insertion order + *
    • {@code successors(node)}: Connecting edge insertion order + *
    • {@code incidentEdges(node)}: Edge insertion order + *
    + *
  • For {@link Network}: + *
      + *
    • {@code adjacentNodes(node)}: Stable order + *
    • {@code predecessors(node)}: Connecting edge insertion order + *
    • {@code successors(node)}: Connecting edge insertion order + *
    • {@code incidentEdges(node)}: Stable order + *
    • {@code inEdges(node)}: Edge insertion order + *
    • {@code outEdges(node)}: Edge insertion order + *
    • {@code adjacentEdges(edge)}: Stable order + *
    • {@code edgesConnecting(nodeU, nodeV)}: Edge insertion order + *
    + *
+ * + * @since 29.0 + */ + public static ElementOrder stable() { + return new ElementOrder<>(Type.STABLE, null); + } + + /** Returns an instance which specifies that insertion ordering is guaranteed. */ + public static ElementOrder insertion() { + return new ElementOrder<>(Type.INSERTION, null); + } + + /** + * Returns an instance which specifies that the natural ordering of the elements is guaranteed. + */ + public static > ElementOrder natural() { + return new ElementOrder<>(Type.SORTED, Ordering.natural()); + } + + /** + * Returns an instance which specifies that the ordering of the elements is guaranteed to be + * determined by {@code comparator}. + */ + public static ElementOrder sorted(Comparator comparator) { + return new ElementOrder<>(Type.SORTED, checkNotNull(comparator)); + } + + /** Returns the type of ordering used. */ + public Type type() { + return type; + } + + /** + * Returns the {@link Comparator} used. + * + * @throws UnsupportedOperationException if comparator is not defined + */ + public Comparator comparator() { + if (comparator != null) { + return comparator; + } + throw new UnsupportedOperationException("This ordering does not define a comparator."); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ElementOrder)) { + return false; + } + + ElementOrder other = (ElementOrder) obj; + return (type == other.type) && Objects.equal(comparator, other.comparator); + } + + @Override + public int hashCode() { + return Objects.hashCode(type, comparator); + } + + @Override + public String toString() { + ToStringHelper helper = MoreObjects.toStringHelper(this).add("type", type); + if (comparator != null) { + helper.add("comparator", comparator); + } + return helper.toString(); + } + + /** Returns an empty mutable map whose keys will respect this {@link ElementOrder}. */ + Map createMap(int expectedSize) { + switch (type) { + case UNORDERED: + return Maps.newHashMapWithExpectedSize(expectedSize); + case INSERTION: + case STABLE: + return Maps.newLinkedHashMapWithExpectedSize(expectedSize); + case SORTED: + return Maps.newTreeMap(comparator()); + default: + throw new AssertionError(); + } + } + + @SuppressWarnings("unchecked") + ElementOrder cast() { + return (ElementOrder) this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..1f39b4d575627 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EndpointPair.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EndpointPair.java new file mode 100644 index 0000000000000..ee1f20d96f050 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EndpointPair.java @@ -0,0 +1,254 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.GraphConstants.NOT_AVAILABLE_ON_UNDIRECTED; + +/** + * An immutable pair representing the two endpoints of an edge in a graph. The {@link EndpointPair} + * of a directed edge is an ordered pair of nodes ({@link #source()} and {@link #target()}). The + * {@link EndpointPair} of an undirected edge is an unordered pair of nodes ({@link #nodeU()} and + * {@link #nodeV()}). + * + *

The edge is a self-loop if, and only if, the two endpoints are equal. + * + * @author James Sexton + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public abstract class EndpointPair implements Iterable { + private final N nodeU; + private final N nodeV; + + private EndpointPair(N nodeU, N nodeV) { + this.nodeU = checkNotNull(nodeU); + this.nodeV = checkNotNull(nodeV); + } + + /** Returns an {@link EndpointPair} representing the endpoints of a directed edge. */ + public static EndpointPair ordered(N source, N target) { + return new Ordered<>(source, target); + } + + /** Returns an {@link EndpointPair} representing the endpoints of an undirected edge. */ + public static EndpointPair unordered(N nodeU, N nodeV) { + // Swap nodes on purpose to prevent callers from relying on the "ordering" of an unordered pair. + return new Unordered<>(nodeV, nodeU); + } + + /** Returns an {@link EndpointPair} representing the endpoints of an edge in {@code graph}. */ + static EndpointPair of(Graph graph, N nodeU, N nodeV) { + return graph.isDirected() ? ordered(nodeU, nodeV) : unordered(nodeU, nodeV); + } + + /** Returns an {@link EndpointPair} representing the endpoints of an edge in {@code network}. */ + static EndpointPair of(Network network, N nodeU, N nodeV) { + return network.isDirected() ? ordered(nodeU, nodeV) : unordered(nodeU, nodeV); + } + + /** + * If this {@link EndpointPair} {@link #isOrdered()}, returns the node which is the source. + * + * @throws UnsupportedOperationException if this {@link EndpointPair} is not ordered + */ + public abstract N source(); + + /** + * If this {@link EndpointPair} {@link #isOrdered()}, returns the node which is the target. + * + * @throws UnsupportedOperationException if this {@link EndpointPair} is not ordered + */ + public abstract N target(); + + /** + * If this {@link EndpointPair} {@link #isOrdered()} returns the {@link #source()}; otherwise, + * returns an arbitrary (but consistent) endpoint of the origin edge. + */ + public final N nodeU() { + return nodeU; + } + + /** + * Returns the node {@link #adjacentNode(Object) adjacent} to {@link #nodeU()} along the origin + * edge. If this {@link EndpointPair} {@link #isOrdered()}, this is equal to {@link #target()}. + */ + public final N nodeV() { + return nodeV; + } + + /** + * Returns the node that is adjacent to {@code node} along the origin edge. + * + * @throws IllegalArgumentException if this {@link EndpointPair} does not contain {@code node} + * @since 20.0 (but the argument type was changed from {@code Object} to {@code N} in 31.0) + */ + public final N adjacentNode(N node) { + if (node.equals(nodeU)) { + return nodeV; + } else if (node.equals(nodeV)) { + return nodeU; + } else { + throw new IllegalArgumentException("EndpointPair " + this + " does not contain node " + node); + } + } + + /** + * Returns {@code true} if this {@link EndpointPair} is an ordered pair (i.e. represents the + * endpoints of a directed edge). + */ + public abstract boolean isOrdered(); + + /** Iterates in the order {@link #nodeU()}, {@link #nodeV()}. */ + @Override + public final UnmodifiableIterator iterator() { + return Iterators.forArray(nodeU, nodeV); + } + + /** + * Two ordered {@link EndpointPair}s are equal if their {@link #source()} and {@link #target()} + * are equal. Two unordered {@link EndpointPair}s are equal if they contain the same nodes. An + * ordered {@link EndpointPair} is never equal to an unordered {@link EndpointPair}. + */ + @Override + public abstract boolean equals(Object obj); + + /** + * The hashcode of an ordered {@link EndpointPair} is equal to {@code Objects.hashCode(source(), + * target())}. The hashcode of an unordered {@link EndpointPair} is equal to {@code + * nodeU().hashCode() + nodeV().hashCode()}. + */ + @Override + public abstract int hashCode(); + + private static final class Ordered extends EndpointPair { + private Ordered(N source, N target) { + super(source, target); + } + + @Override + public N source() { + return nodeU(); + } + + @Override + public N target() { + return nodeV(); + } + + @Override + public boolean isOrdered() { + return true; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof EndpointPair)) { + return false; + } + + EndpointPair other = (EndpointPair) obj; + if (isOrdered() != other.isOrdered()) { + return false; + } + + return source().equals(other.source()) && target().equals(other.target()); + } + + @Override + public int hashCode() { + return Objects.hashCode(source(), target()); + } + + @Override + public String toString() { + return "<" + source() + " -> " + target() + ">"; + } + } + + private static final class Unordered extends EndpointPair { + private Unordered(N nodeU, N nodeV) { + super(nodeU, nodeV); + } + + @Override + public N source() { + throw new UnsupportedOperationException(NOT_AVAILABLE_ON_UNDIRECTED); + } + + @Override + public N target() { + throw new UnsupportedOperationException(NOT_AVAILABLE_ON_UNDIRECTED); + } + + @Override + public boolean isOrdered() { + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof EndpointPair)) { + return false; + } + + EndpointPair other = (EndpointPair) obj; + if (isOrdered() != other.isOrdered()) { + return false; + } + + // Equivalent to the following simple implementation: + // boolean condition1 = nodeU().equals(other.nodeU()) && nodeV().equals(other.nodeV()); + // boolean condition2 = nodeU().equals(other.nodeV()) && nodeV().equals(other.nodeU()); + // return condition1 || condition2; + if (nodeU().equals(other.nodeU())) { // check condition1 + // Here's the tricky bit. We don't have to explicitly check for condition2 in this case. + // Why? The second half of condition2 requires that nodeV equals other.nodeU. + // We already know that nodeU equals other.nodeU. Combined with the earlier statement, + // and the transitive property of equality, this implies that nodeU equals nodeV. + // If nodeU equals nodeV, condition1 == condition2, so checking condition1 is sufficient. + return nodeV().equals(other.nodeV()); + } + return nodeU().equals(other.nodeV()) && nodeV().equals(other.nodeU()); // check condition2 + } + + @Override + public int hashCode() { + return nodeU().hashCode() + nodeV().hashCode(); + } + + @Override + public String toString() { + return "[" + nodeU() + ", " + nodeV() + "]"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EndpointPairIterator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EndpointPairIterator.java new file mode 100644 index 0000000000000..cdbb8662be1d4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/EndpointPairIterator.java @@ -0,0 +1,151 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.AbstractIterator; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Sets; + +import java.util.Iterator; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * A class to facilitate the set returned by {@link Graph#edges()}. + * + * @author James Sexton + */ +@ElementTypesAreNonnullByDefault +abstract class EndpointPairIterator extends AbstractIterator> { + private final BaseGraph graph; + private final Iterator nodeIterator; + + N node = null; // null is safe as an initial value because graphs don't allow null nodes + + Iterator successorIterator = ImmutableSet.of().iterator(); + + static EndpointPairIterator of(BaseGraph graph) { + return graph.isDirected() ? new Directed(graph) : new Undirected(graph); + } + + private EndpointPairIterator(BaseGraph graph) { + this.graph = graph; + this.nodeIterator = graph.nodes().iterator(); + } + + /** + * Called after {@link #successorIterator} is exhausted. Advances {@link #node} to the next node + * and updates {@link #successorIterator} to iterate through the successors of {@link #node}. + */ + final boolean advance() { + checkState(!successorIterator.hasNext()); + if (!nodeIterator.hasNext()) { + return false; + } + node = nodeIterator.next(); + successorIterator = graph.successors(node).iterator(); + return true; + } + + /** + * If the graph is directed, each ordered [source, target] pair will be visited once if there is + * an edge connecting them. + */ + private static final class Directed extends EndpointPairIterator { + private Directed(BaseGraph graph) { + super(graph); + } + + @Override + protected EndpointPair computeNext() { + while (true) { + if (successorIterator.hasNext()) { + // requireNonNull is safe because successorIterator is empty until we set this.node. + return EndpointPair.ordered(requireNonNull(node), successorIterator.next()); + } + if (!advance()) { + return endOfData(); + } + } + } + } + + /** + * If the graph is undirected, each unordered [node, otherNode] pair (except self-loops) will be + * visited twice if there is an edge connecting them. To avoid returning duplicate {@link + * EndpointPair}s, we keep track of the nodes that we have visited. When processing endpoint + * pairs, we skip if the "other node" is in the visited set, as shown below: + * + *

+   * Nodes = {N1, N2, N3, N4}
+   *    N2           __
+   *   /  \         |  |
+   * N1----N3      N4__|
+   *
+   * Visited Nodes = {}
+   * EndpointPair [N1, N2] - return
+   * EndpointPair [N1, N3] - return
+   * Visited Nodes = {N1}
+   * EndpointPair [N2, N1] - skip
+   * EndpointPair [N2, N3] - return
+   * Visited Nodes = {N1, N2}
+   * EndpointPair [N3, N1] - skip
+   * EndpointPair [N3, N2] - skip
+   * Visited Nodes = {N1, N2, N3}
+   * EndpointPair [N4, N4] - return
+   * Visited Nodes = {N1, N2, N3, N4}
+   * 
+ */ + private static final class Undirected extends EndpointPairIterator { + // It's a little weird that we add `null` to this set, but it makes for slightly simpler code. + private Set visitedNodes; + + private Undirected(BaseGraph graph) { + super(graph); + this.visitedNodes = Sets.newHashSetWithExpectedSize(graph.nodes().size() + 1); + } + + @Override + protected EndpointPair computeNext() { + while (true) { + /* + * requireNonNull is safe because visitedNodes isn't cleared until this method calls + * endOfData() (after which this method is never called again). + */ + requireNonNull(visitedNodes); + while (successorIterator.hasNext()) { + N otherNode = successorIterator.next(); + if (!visitedNodes.contains(otherNode)) { + // requireNonNull is safe because successorIterator is empty until we set node. + return EndpointPair.unordered(requireNonNull(node), otherNode); + } + } + // Add to visited set *after* processing neighbors so we still include self-loops. + visitedNodes.add(node); + if (!advance()) { + visitedNodes = null; + return endOfData(); + } + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingGraph.java new file mode 100644 index 0000000000000..ca708ad246744 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingGraph.java @@ -0,0 +1,113 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Set; + +/** + * A class to allow {@link Graph} implementations to be backed by a {@link BaseGraph}. This is not + * currently planned to be released as a general-purpose forwarding class. + * + * @author James Sexton + */ +@ElementTypesAreNonnullByDefault +abstract class ForwardingGraph extends AbstractGraph { + + abstract BaseGraph delegate(); + + @Override + public Set nodes() { + return delegate().nodes(); + } + + /** + * Defer to {@link AbstractGraph#edges()} (based on {@link #successors(Object)}) for full edges() + * implementation. + */ + @Override + protected long edgeCount() { + return delegate().edges().size(); + } + + @Override + public boolean isDirected() { + return delegate().isDirected(); + } + + @Override + public boolean allowsSelfLoops() { + return delegate().allowsSelfLoops(); + } + + @Override + public ElementOrder nodeOrder() { + return delegate().nodeOrder(); + } + + @Override + public ElementOrder incidentEdgeOrder() { + return delegate().incidentEdgeOrder(); + } + + @Override + public Set adjacentNodes(N node) { + return delegate().adjacentNodes(node); + } + + @Override + public Set predecessors(N node) { + return delegate().predecessors(node); + } + + @Override + public Set successors(N node) { + return delegate().successors(node); + } + + @Override + public Set> incidentEdges(N node) { + return delegate().incidentEdges(node); + } + + @Override + public int degree(N node) { + return delegate().degree(node); + } + + @Override + public int inDegree(N node) { + return delegate().inDegree(node); + } + + @Override + public int outDegree(N node) { + return delegate().outDegree(node); + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return delegate().hasEdgeConnecting(nodeU, nodeV); + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + return delegate().hasEdgeConnecting(endpoints); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingNetwork.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingNetwork.java new file mode 100644 index 0000000000000..c3bd3395b57d2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingNetwork.java @@ -0,0 +1,166 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Optional; +import java.util.Set; + +/** + * A class to allow {@link Network} implementations to be backed by a provided delegate. This is not + * currently planned to be released as a general-purpose forwarding class. + * + * @author James Sexton + * @author Joshua O'Madadhain + */ +@ElementTypesAreNonnullByDefault +abstract class ForwardingNetwork extends AbstractNetwork { + + abstract Network delegate(); + + @Override + public Set nodes() { + return delegate().nodes(); + } + + @Override + public Set edges() { + return delegate().edges(); + } + + @Override + public boolean isDirected() { + return delegate().isDirected(); + } + + @Override + public boolean allowsParallelEdges() { + return delegate().allowsParallelEdges(); + } + + @Override + public boolean allowsSelfLoops() { + return delegate().allowsSelfLoops(); + } + + @Override + public ElementOrder nodeOrder() { + return delegate().nodeOrder(); + } + + @Override + public ElementOrder edgeOrder() { + return delegate().edgeOrder(); + } + + @Override + public Set adjacentNodes(N node) { + return delegate().adjacentNodes(node); + } + + @Override + public Set predecessors(N node) { + return delegate().predecessors(node); + } + + @Override + public Set successors(N node) { + return delegate().successors(node); + } + + @Override + public Set incidentEdges(N node) { + return delegate().incidentEdges(node); + } + + @Override + public Set inEdges(N node) { + return delegate().inEdges(node); + } + + @Override + public Set outEdges(N node) { + return delegate().outEdges(node); + } + + @Override + public EndpointPair incidentNodes(E edge) { + return delegate().incidentNodes(edge); + } + + @Override + public Set adjacentEdges(E edge) { + return delegate().adjacentEdges(edge); + } + + @Override + public int degree(N node) { + return delegate().degree(node); + } + + @Override + public int inDegree(N node) { + return delegate().inDegree(node); + } + + @Override + public int outDegree(N node) { + return delegate().outDegree(node); + } + + @Override + public Set edgesConnecting(N nodeU, N nodeV) { + return delegate().edgesConnecting(nodeU, nodeV); + } + + @Override + public Set edgesConnecting(EndpointPair endpoints) { + return delegate().edgesConnecting(endpoints); + } + + @Override + public Optional edgeConnecting(N nodeU, N nodeV) { + return delegate().edgeConnecting(nodeU, nodeV); + } + + @Override + public Optional edgeConnecting(EndpointPair endpoints) { + return delegate().edgeConnecting(endpoints); + } + + @Override + public E edgeConnectingOrNull(N nodeU, N nodeV) { + return delegate().edgeConnectingOrNull(nodeU, nodeV); + } + + @Override + public E edgeConnectingOrNull(EndpointPair endpoints) { + return delegate().edgeConnectingOrNull(endpoints); + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return delegate().hasEdgeConnecting(nodeU, nodeV); + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + return delegate().hasEdgeConnecting(endpoints); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingValueGraph.java new file mode 100644 index 0000000000000..fe51f4b528478 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ForwardingValueGraph.java @@ -0,0 +1,130 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Optional; +import java.util.Set; + +/** + * A class to allow {@link ValueGraph} implementations to be backed by a provided delegate. This is + * not currently planned to be released as a general-purpose forwarding class. + * + * @author James Sexton + * @author Joshua O'Madadhain + */ +@ElementTypesAreNonnullByDefault +abstract class ForwardingValueGraph extends AbstractValueGraph { + + abstract ValueGraph delegate(); + + @Override + public Set nodes() { + return delegate().nodes(); + } + + /** + * Defer to {@link AbstractValueGraph#edges()} (based on {@link #successors(Object)}) for full + * edges() implementation. + */ + @Override + protected long edgeCount() { + return delegate().edges().size(); + } + + @Override + public boolean isDirected() { + return delegate().isDirected(); + } + + @Override + public boolean allowsSelfLoops() { + return delegate().allowsSelfLoops(); + } + + @Override + public ElementOrder nodeOrder() { + return delegate().nodeOrder(); + } + + @Override + public ElementOrder incidentEdgeOrder() { + return delegate().incidentEdgeOrder(); + } + + @Override + public Set adjacentNodes(N node) { + return delegate().adjacentNodes(node); + } + + @Override + public Set predecessors(N node) { + return delegate().predecessors(node); + } + + @Override + public Set successors(N node) { + return delegate().successors(node); + } + + @Override + public int degree(N node) { + return delegate().degree(node); + } + + @Override + public int inDegree(N node) { + return delegate().inDegree(node); + } + + @Override + public int outDegree(N node) { + return delegate().outDegree(node); + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return delegate().hasEdgeConnecting(nodeU, nodeV); + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + return delegate().hasEdgeConnecting(endpoints); + } + + @Override + public Optional edgeValue(N nodeU, N nodeV) { + return delegate().edgeValue(nodeU, nodeV); + } + + @Override + public Optional edgeValue(EndpointPair endpoints) { + return delegate().edgeValue(endpoints); + } + + @Override + public V edgeValueOrDefault(N nodeU, N nodeV, V defaultValue) { + return delegate().edgeValueOrDefault(nodeU, nodeV, defaultValue); + } + + @Override + public V edgeValueOrDefault(EndpointPair endpoints, V defaultValue) { + return delegate().edgeValueOrDefault(endpoints, defaultValue); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Graph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Graph.java new file mode 100644 index 0000000000000..981e5afd2e5ba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Graph.java @@ -0,0 +1,304 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.util.Collection; +import java.util.Set; + +/** + * An interface for graph-structured data, + * whose edges are anonymous entities with no identity or information of their own. + * + *

A graph is composed of a set of nodes and a set of edges connecting pairs of nodes. + * + *

There are three primary interfaces provided to represent graphs. In order of increasing + * complexity they are: {@link Graph}, {@link ValueGraph}, and {@link Network}. You should generally + * prefer the simplest interface that satisfies your use case. See the + * "Choosing the right graph type" section of the Guava User Guide for more details. + * + *

Capabilities

+ * + *

{@code Graph} supports the following use cases (definitions of + * terms): + * + *

    + *
  • directed graphs + *
  • undirected graphs + *
  • graphs that do/don't allow self-loops + *
  • graphs whose nodes/edges are insertion-ordered, sorted, or unordered + *
+ * + *

{@code Graph} explicitly does not support parallel edges, and forbids implementations or + * extensions with parallel edges. If you need parallel edges, use {@link Network}. + * + *

Building a {@code Graph}

+ * + *

The implementation classes that {@code common.graph} provides are not public, by design. To + * create an instance of one of the built-in implementations of {@code Graph}, use the {@link + * GraphBuilder} class: + * + *

{@code
+ * MutableGraph graph = GraphBuilder.undirected().build();
+ * }
+ * + *

{@link GraphBuilder#build()} returns an instance of {@link MutableGraph}, which is a subtype + * of {@code Graph} that provides methods for adding and removing nodes and edges. If you do not + * need to mutate a graph (e.g. if you write a method than runs a read-only algorithm on the graph), + * you should use the non-mutating {@link Graph} interface, or an {@link ImmutableGraph}. + * + *

You can create an immutable copy of an existing {@code Graph} using {@link + * ImmutableGraph#copyOf(Graph)}: + * + *

{@code
+ * ImmutableGraph immutableGraph = ImmutableGraph.copyOf(graph);
+ * }
+ * + *

Instances of {@link ImmutableGraph} do not implement {@link MutableGraph} (obviously!) and are + * contractually guaranteed to be unmodifiable and thread-safe. + * + *

The Guava User Guide has more + * information on (and examples of) building graphs. + * + *

Additional documentation

+ * + *

See the Guava User Guide for the {@code common.graph} package ("Graphs Explained") for + * additional documentation, including: + * + *

+ * + * @author James Sexton + * @author Joshua O'Madadhain + * @param Node parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface Graph extends BaseGraph { + // + // Graph-level accessors + // + + /** Returns all nodes in this graph, in the order specified by {@link #nodeOrder()}. */ + @Override + Set nodes(); + + /** Returns all edges in this graph. */ + @Override + Set> edges(); + + // + // Graph properties + // + + /** + * Returns true if the edges in this graph are directed. Directed edges connect a {@link + * EndpointPair#source() source node} to a {@link EndpointPair#target() target node}, while + * undirected edges connect a pair of nodes to each other. + */ + @Override + boolean isDirected(); + + /** + * Returns true if this graph allows self-loops (edges that connect a node to itself). Attempting + * to add a self-loop to a graph that does not allow them will throw an {@link + * IllegalArgumentException}. + */ + @Override + boolean allowsSelfLoops(); + + /** Returns the order of iteration for the elements of {@link #nodes()}. */ + @Override + ElementOrder nodeOrder(); + + /** + * Returns an {@link ElementOrder} that specifies the order of iteration for the elements of + * {@link #edges()}, {@link #adjacentNodes(Object)}, {@link #predecessors(Object)}, {@link + * #successors(Object)} and {@link #incidentEdges(Object)}. + * + * @since 29.0 + */ + @Override + ElementOrder incidentEdgeOrder(); + + // + // Element-level accessors + // + + /** + * Returns the nodes which have an incident edge in common with {@code node} in this graph. + * + *

This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set adjacentNodes(N node); + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s incoming edges against the direction (if any) of the edge. + * + *

In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set predecessors(N node); + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s outgoing edges in the direction (if any) of the edge. + * + *

In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. + * + *

This is not the same as "all nodes reachable from {@code node} by following outgoing + * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set successors(N node); + + /** + * Returns the edges in this graph whose endpoints include {@code node}. + * + *

This is equal to the union of incoming and outgoing edges. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + * @since 24.0 + */ + @Override + Set> incidentEdges(N node); + + /** + * Returns the count of {@code node}'s incident edges, counting self-loops twice (equivalently, + * the number of times an edge touches {@code node}). + * + *

For directed graphs, this is equal to {@code inDegree(node) + outDegree(node)}. + * + *

For undirected graphs, this is equal to {@code incidentEdges(node).size()} + (number of + * self-loops incident to {@code node}). + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + int degree(N node); + + /** + * Returns the count of {@code node}'s incoming edges (equal to {@code predecessors(node).size()}) + * in a directed graph. In an undirected graph, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + int inDegree(N node); + + /** + * Returns the count of {@code node}'s outgoing edges (equal to {@code successors(node).size()}) + * in a directed graph. In an undirected graph, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + int outDegree(N node); + + /** + * Returns true if there is an edge that directly connects {@code nodeU} to {@code nodeV}. This is + * equivalent to {@code nodes().contains(nodeU) && successors(nodeU).contains(nodeV)}. + * + *

In an undirected graph, this is equal to {@code hasEdgeConnecting(nodeV, nodeU)}. + * + * @since 23.0 + */ + @Override + boolean hasEdgeConnecting(N nodeU, N nodeV); + + /** + * Returns true if there is an edge that directly connects {@code endpoints} (in the order, if + * any, specified by {@code endpoints}). This is equivalent to {@code + * edges().contains(endpoints)}. + * + *

Unlike the other {@code EndpointPair}-accepting methods, this method does not throw if the + * endpoints are unordered and the graph is directed; it simply returns {@code false}. This is for + * consistency with the behavior of {@link Collection#contains(Object)} (which does not generally + * throw if the object cannot be present in the collection), and the desire to have this method's + * behavior be compatible with {@code edges().contains(endpoints)}. + * + * @since 27.1 + */ + @Override + boolean hasEdgeConnecting(EndpointPair endpoints); + + // + // Graph identity + // + + /** + * Returns {@code true} iff {@code object} is a {@link Graph} that has the same elements and the + * same structural relationships as those in this graph. + * + *

Thus, two graphs A and B are equal if all of the following are true: + * + *

    + *
  • A and B have equal {@link #isDirected() directedness}. + *
  • A and B have equal {@link #nodes() node sets}. + *
  • A and B have equal {@link #edges() edge sets}. + *
+ * + *

Graph properties besides {@link #isDirected() directedness} do not affect equality. + * For example, two graphs may be considered equal even if one allows self-loops and the other + * doesn't. Additionally, the order in which nodes or edges are added to the graph, and the order + * in which they are iterated over, are irrelevant. + * + *

A reference implementation of this is provided by {@link AbstractGraph#equals(Object)}. + */ + @Override + boolean equals(Object object); + + /** + * Returns the hash code for this graph. The hash code of a graph is defined as the hash code of + * the set returned by {@link #edges()}. + * + *

A reference implementation of this is provided by {@link AbstractGraph#hashCode()}. + */ + @Override + int hashCode(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphBuilder.java new file mode 100644 index 0000000000000..e3d6fa9273539 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphBuilder.java @@ -0,0 +1,197 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Optional; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; + +/** + * A builder for constructing instances of {@link MutableGraph} or {@link ImmutableGraph} with + * user-defined properties. + * + *

A {@code Graph} built by this class has the following default properties: + * + *

    + *
  • does not allow self-loops + *
  • orders {@link Graph#nodes()} in the order in which the elements were added (insertion + * order) + *
+ * + *

{@code Graph}s built by this class also guarantee that each collection-returning accessor + * returns a (live) unmodifiable view; see the external + * documentation for details. + * + *

Examples of use: + * + *

{@code
+ * // Building a mutable graph
+ * MutableGraph graph = GraphBuilder.undirected().allowsSelfLoops(true).build();
+ * graph.putEdge("bread", "bread");
+ * graph.putEdge("chocolate", "peanut butter");
+ * graph.putEdge("peanut butter", "jelly");
+ *
+ * // Building an immutable graph
+ * ImmutableGraph immutableGraph =
+ *     GraphBuilder.undirected()
+ *         .allowsSelfLoops(true)
+ *         .immutable()
+ *         .putEdge("bread", "bread")
+ *         .putEdge("chocolate", "peanut butter")
+ *         .putEdge("peanut butter", "jelly")
+ *         .build();
+ * }
+ * + * @author James Sexton + * @author Joshua O'Madadhain + * @param The most general node type this builder will support. This is normally {@code Object} + * unless it is constrained by using a method like {@link #nodeOrder}, or the builder is + * constructed based on an existing {@code Graph} using {@link #from(Graph)}. + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class GraphBuilder extends AbstractGraphBuilder { + + /** Creates a new instance with the specified edge directionality. */ + private GraphBuilder(boolean directed) { + super(directed); + } + + /** Returns a {@link GraphBuilder} for building directed graphs. */ + public static GraphBuilder directed() { + return new GraphBuilder<>(true); + } + + /** Returns a {@link GraphBuilder} for building undirected graphs. */ + public static GraphBuilder undirected() { + return new GraphBuilder<>(false); + } + + /** + * Returns a {@link GraphBuilder} initialized with all properties queryable from {@code graph}. + * + *

The "queryable" properties are those that are exposed through the {@link Graph} interface, + * such as {@link Graph#isDirected()}. Other properties, such as {@link #expectedNodeCount(int)}, + * are not set in the new builder. + */ + public static GraphBuilder from(Graph graph) { + return new GraphBuilder(graph.isDirected()) + .allowsSelfLoops(graph.allowsSelfLoops()) + .nodeOrder(graph.nodeOrder()) + .incidentEdgeOrder(graph.incidentEdgeOrder()); + } + + /** + * Returns an {@link ImmutableGraph.Builder} with the properties of this {@link GraphBuilder}. + * + *

The returned builder can be used for populating an {@link ImmutableGraph}. + * + *

Note that the returned builder will always have {@link #incidentEdgeOrder} set to {@link + * ElementOrder#stable()}, regardless of the value that was set in this builder. + * + * @since 28.0 + */ + public ImmutableGraph.Builder immutable() { + GraphBuilder castBuilder = cast(); + return new ImmutableGraph.Builder<>(castBuilder); + } + + /** + * Specifies whether the graph will allow self-loops (edges that connect a node to itself). + * Attempting to add a self-loop to a graph that does not allow them will throw an {@link + * UnsupportedOperationException}. + * + *

The default value is {@code false}. + */ + public GraphBuilder allowsSelfLoops(boolean allowsSelfLoops) { + this.allowsSelfLoops = allowsSelfLoops; + return this; + } + + /** + * Specifies the expected number of nodes in the graph. + * + * @throws IllegalArgumentException if {@code expectedNodeCount} is negative + */ + public GraphBuilder expectedNodeCount(int expectedNodeCount) { + this.expectedNodeCount = Optional.of(checkNonNegative(expectedNodeCount)); + return this; + } + + /** + * Specifies the order of iteration for the elements of {@link Graph#nodes()}. + * + *

The default value is {@link ElementOrder#insertion() insertion order}. + */ + public GraphBuilder nodeOrder(ElementOrder nodeOrder) { + GraphBuilder newBuilder = cast(); + newBuilder.nodeOrder = checkNotNull(nodeOrder); + return newBuilder; + } + + /** + * Specifies the order of iteration for the elements of {@link Graph#edges()}, {@link + * Graph#adjacentNodes(Object)}, {@link Graph#predecessors(Object)}, {@link + * Graph#successors(Object)} and {@link Graph#incidentEdges(Object)}. + * + *

The default value is {@link ElementOrder#unordered() unordered} for mutable graphs. For + * immutable graphs, this value is ignored; they always have a {@link ElementOrder#stable() + * stable} order. + * + * @throws IllegalArgumentException if {@code incidentEdgeOrder} is not either {@code + * ElementOrder.unordered()} or {@code ElementOrder.stable()}. + * @since 29.0 + */ + public GraphBuilder incidentEdgeOrder(ElementOrder incidentEdgeOrder) { + checkArgument( + incidentEdgeOrder.type() == ElementOrder.Type.UNORDERED + || incidentEdgeOrder.type() == ElementOrder.Type.STABLE, + "The given elementOrder (%s) is unsupported. incidentEdgeOrder() only supports" + + " ElementOrder.unordered() and ElementOrder.stable().", + incidentEdgeOrder); + GraphBuilder newBuilder = cast(); + newBuilder.incidentEdgeOrder = checkNotNull(incidentEdgeOrder); + return newBuilder; + } + + /** Returns an empty {@link MutableGraph} with the properties of this {@link GraphBuilder}. */ + public MutableGraph build() { + return new StandardMutableGraph<>(this); + } + + GraphBuilder copy() { + GraphBuilder newBuilder = new GraphBuilder<>(directed); + newBuilder.allowsSelfLoops = allowsSelfLoops; + newBuilder.nodeOrder = nodeOrder; + newBuilder.expectedNodeCount = expectedNodeCount; + newBuilder.incidentEdgeOrder = incidentEdgeOrder; + return newBuilder; + } + + @SuppressWarnings("unchecked") + private GraphBuilder cast() { + return (GraphBuilder) this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphConnections.java new file mode 100644 index 0000000000000..92abbcbb1b7a7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphConnections.java @@ -0,0 +1,76 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Iterator; +import java.util.Set; + +/** + * An interface for representing and manipulating an origin node's adjacent nodes and edge values in + * a {@link Graph}. + * + * @author James Sexton + * @param Node parameter type + * @param Value parameter type + */ +@ElementTypesAreNonnullByDefault +interface GraphConnections { + + Set adjacentNodes(); + + Set predecessors(); + + Set successors(); + + /** + * Returns an iterator over the incident edges. + * + * @param thisNode The node that this all of the connections in this class are connected to. + */ + Iterator> incidentEdgeIterator(N thisNode); + + /** + * Returns the value associated with the edge connecting the origin node to {@code node}, or null + * if there is no such edge. + */ + V value(N node); + + /** Remove {@code node} from the set of predecessors. */ + void removePredecessor(N node); + + /** + * Remove {@code node} from the set of successors. Returns the value previously associated with + * the edge connecting the two nodes. + */ + V removeSuccessor(N node); + + /** + * Add {@code node} as a predecessor to the origin node. In the case of an undirected graph, it + * also becomes a successor. Associates {@code value} with the edge connecting the two nodes. + */ + void addPredecessor(N node, V value); + + /** + * Add {@code node} as a successor to the origin node. In the case of an undirected graph, it also + * becomes a predecessor. Associates {@code value} with the edge connecting the two nodes. Returns + * the value previously associated with the edge connecting the two nodes. + */ + V addSuccessor(N node, V value); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphConstants.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphConstants.java new file mode 100644 index 0000000000000..276e3a51b8228 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/GraphConstants.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.google.common.graph; + +/** A utility class to hold various constants used by the Guava Graph library. */ +@ElementTypesAreNonnullByDefault +final class GraphConstants { + + private GraphConstants() {} + + static final int EXPECTED_DEGREE = 2; + + static final int DEFAULT_NODE_COUNT = 10; + static final int DEFAULT_EDGE_COUNT = DEFAULT_NODE_COUNT * EXPECTED_DEGREE; + + // Load factor and capacity for "inner" (i.e. per node/edge element) hash sets or maps + static final float INNER_LOAD_FACTOR = 1.0f; + static final int INNER_CAPACITY = 2; // ceiling(EXPECTED_DEGREE / INNER_LOAD_FACTOR) + + // Error messages + static final String NODE_NOT_IN_GRAPH = "Node %s is not an element of this graph."; + static final String EDGE_NOT_IN_GRAPH = "Edge %s is not an element of this graph."; + static final String REUSING_EDGE = + "Edge %s already exists between the following nodes: %s, " + + "so it cannot be reused to connect the following nodes: %s."; + static final String MULTIPLE_EDGES_CONNECTING = + "Cannot call edgeConnecting() when parallel edges exist between %s and %s. Consider calling " + + "edgesConnecting() instead."; + static final String PARALLEL_EDGES_NOT_ALLOWED = + "Nodes %s and %s are already connected by a different edge. To construct a graph " + + "that allows parallel edges, call allowsParallelEdges(true) on the Builder."; + static final String SELF_LOOPS_NOT_ALLOWED = + "Cannot add self-loop edge on node %s, as self-loops are not allowed. To construct a graph " + + "that allows self-loops, call allowsSelfLoops(true) on the Builder."; + static final String NOT_AVAILABLE_ON_UNDIRECTED = + "Cannot call source()/target() on a EndpointPair from an undirected graph. Consider calling " + + "adjacentNode(node) if you already have a node, or nodeU()/nodeV() if you don't."; + static final String EDGE_ALREADY_EXISTS = "Edge %s already exists in the graph."; + static final String ENDPOINTS_MISMATCH = + "Mismatch: endpoints' ordering is not compatible with directionality of the graph"; + + /** Singleton edge value for {@link Graph} implementations backed by {@link ValueGraph}s. */ + enum Presence { + EDGE_EXISTS + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Graphs.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Graphs.java new file mode 100644 index 0000000000000..46327c43ca169 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Graphs.java @@ -0,0 +1,631 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Iterables; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Maps; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.graph.GraphConstants.NODE_NOT_IN_GRAPH; + +/** + * Static utility methods for {@link Graph}, {@link ValueGraph}, and {@link Network} instances. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class Graphs { + + private Graphs() {} + + // Graph query methods + + /** + * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset + * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting + * and ending with the same node. + * + *

This method will detect any non-empty cycle, including self-loops (a cycle of length 1). + */ + public static boolean hasCycle(Graph graph) { + int numEdges = graph.edges().size(); + if (numEdges == 0) { + return false; // An edge-free graph is acyclic by definition. + } + if (!graph.isDirected() && numEdges >= graph.nodes().size()) { + return true; // Optimization for the undirected case: at least one cycle must exist. + } + + Map visitedNodes = + Maps.newHashMapWithExpectedSize(graph.nodes().size()); + for (N node : graph.nodes()) { + if (subgraphHasCycle(graph, visitedNodes, node, null)) { + return true; + } + } + return false; + } + + /** + * Returns true if {@code network} has at least one cycle. A cycle is defined as a non-empty + * subset of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) + * starting and ending with the same node. + * + *

This method will detect any non-empty cycle, including self-loops (a cycle of length 1). + */ + public static boolean hasCycle(Network network) { + // In a directed graph, parallel edges cannot introduce a cycle in an acyclic graph. + // However, in an undirected graph, any parallel edge induces a cycle in the graph. + if (!network.isDirected() + && network.allowsParallelEdges() + && network.edges().size() > network.asGraph().edges().size()) { + return true; + } + return hasCycle(network.asGraph()); + } + + /** + * Performs a traversal of the nodes reachable from {@code node}. If we ever reach a node we've + * already visited (following only outgoing edges and without reusing edges), we know there's a + * cycle in the graph. + */ + private static boolean subgraphHasCycle( + Graph graph, Map visitedNodes, N node, N previousNode) { + NodeVisitState state = visitedNodes.get(node); + if (state == NodeVisitState.COMPLETE) { + return false; + } + if (state == NodeVisitState.PENDING) { + return true; + } + + visitedNodes.put(node, NodeVisitState.PENDING); + for (N nextNode : graph.successors(node)) { + if (canTraverseWithoutReusingEdge(graph, nextNode, previousNode) + && subgraphHasCycle(graph, visitedNodes, nextNode, node)) { + return true; + } + } + visitedNodes.put(node, NodeVisitState.COMPLETE); + return false; + } + + /** + * Determines whether an edge has already been used during traversal. In the directed case a cycle + * is always detected before reusing an edge, so no special logic is required. In the undirected + * case, we must take care not to "backtrack" over an edge (i.e. going from A to B and then going + * from B to A). + */ + private static boolean canTraverseWithoutReusingEdge( + Graph graph, Object nextNode, Object previousNode) { + if (graph.isDirected() || !Objects.equal(previousNode, nextNode)) { + return true; + } + // This falls into the undirected A->B->A case. The Graph interface does not support parallel + // edges, so this traversal would require reusing the undirected AB edge. + return false; + } + + /** + * Returns the transitive closure of {@code graph}. The transitive closure of a graph is another + * graph with an edge connecting node A to node B if node B is {@link #reachableNodes(Graph, + * Object) reachable} from node A. + * + *

This is a "snapshot" based on the current topology of {@code graph}, rather than a live view + * of the transitive closure of {@code graph}. In other words, the returned {@link Graph} will not + * be updated after modifications to {@code graph}. + */ + // TODO(b/31438252): Consider potential optimizations for this algorithm. + public static Graph transitiveClosure(Graph graph) { + MutableGraph transitiveClosure = GraphBuilder.from(graph).allowsSelfLoops(true).build(); + // Every node is, at a minimum, reachable from itself. Since the resulting transitive closure + // will have no isolated nodes, we can skip adding nodes explicitly and let putEdge() do it. + + if (graph.isDirected()) { + // Note: works for both directed and undirected graphs, but we only use in the directed case. + for (N node : graph.nodes()) { + for (N reachableNode : reachableNodes(graph, node)) { + transitiveClosure.putEdge(node, reachableNode); + } + } + } else { + // An optimization for the undirected case: for every node B reachable from node A, + // node A and node B have the same reachability set. + Set visitedNodes = new HashSet(); + for (N node : graph.nodes()) { + if (!visitedNodes.contains(node)) { + Set reachableNodes = reachableNodes(graph, node); + visitedNodes.addAll(reachableNodes); + int pairwiseMatch = 1; // start at 1 to include self-loops + for (N nodeU : reachableNodes) { + for (N nodeV : Iterables.limit(reachableNodes, pairwiseMatch++)) { + transitiveClosure.putEdge(nodeU, nodeV); + } + } + } + } + } + + return transitiveClosure; + } + + /** + * Returns the set of nodes that are reachable from {@code node}. Node B is defined as reachable + * from node A if there exists a path (a sequence of adjacent outgoing edges) starting at node A + * and ending at node B. Note that a node is always reachable from itself via a zero-length path. + * + *

This is a "snapshot" based on the current topology of {@code graph}, rather than a live view + * of the set of nodes reachable from {@code node}. In other words, the returned {@link Set} will + * not be updated after modifications to {@code graph}. + * + * @throws IllegalArgumentException if {@code node} is not present in {@code graph} + */ + public static Set reachableNodes(Graph graph, N node) { + checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node); + return ImmutableSet.copyOf(Traverser.forGraph(graph).breadthFirst(node)); + } + + // Graph mutation methods + + // Graph view methods + + /** + * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other + * properties remain intact, and further updates to {@code graph} will be reflected in the view. + */ + public static Graph transpose(Graph graph) { + if (!graph.isDirected()) { + return graph; // the transpose of an undirected graph is an identical graph + } + + if (graph instanceof TransposedGraph) { + return ((TransposedGraph) graph).graph; + } + + return new TransposedGraph(graph); + } + + /** + * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other + * properties remain intact, and further updates to {@code graph} will be reflected in the view. + */ + public static ValueGraph transpose(ValueGraph graph) { + if (!graph.isDirected()) { + return graph; // the transpose of an undirected graph is an identical graph + } + + if (graph instanceof TransposedValueGraph) { + return ((TransposedValueGraph) graph).graph; + } + + return new TransposedValueGraph<>(graph); + } + + /** + * Returns a view of {@code network} with the direction (if any) of every edge reversed. All other + * properties remain intact, and further updates to {@code network} will be reflected in the view. + */ + public static Network transpose(Network network) { + if (!network.isDirected()) { + return network; // the transpose of an undirected network is an identical network + } + + if (network instanceof TransposedNetwork) { + return ((TransposedNetwork) network).network; + } + + return new TransposedNetwork<>(network); + } + + static EndpointPair transpose(EndpointPair endpoints) { + if (endpoints.isOrdered()) { + return EndpointPair.ordered(endpoints.target(), endpoints.source()); + } + return endpoints; + } + + // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of + // AbstractGraph) derives its behavior from calling successors(). + private static class TransposedGraph extends ForwardingGraph { + private final Graph graph; + + TransposedGraph(Graph graph) { + this.graph = graph; + } + + @Override + Graph delegate() { + return graph; + } + + @Override + public Set predecessors(N node) { + return delegate().successors(node); // transpose + } + + @Override + public Set successors(N node) { + return delegate().predecessors(node); // transpose + } + + @Override + public Set> incidentEdges(N node) { + return new IncidentEdgeSet(this, node) { + @Override + public Iterator> iterator() { + return Iterators.transform( + delegate().incidentEdges(node).iterator(), + edge -> EndpointPair.of(delegate(), edge.nodeV(), edge.nodeU())); + } + }; + } + + @Override + public int inDegree(N node) { + return delegate().outDegree(node); // transpose + } + + @Override + public int outDegree(N node) { + return delegate().inDegree(node); // transpose + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + return delegate().hasEdgeConnecting(transpose(endpoints)); + } + } + + // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of + // AbstractValueGraph) derives its behavior from calling successors(). + private static class TransposedValueGraph extends ForwardingValueGraph { + private final ValueGraph graph; + + TransposedValueGraph(ValueGraph graph) { + this.graph = graph; + } + + @Override + ValueGraph delegate() { + return graph; + } + + @Override + public Set predecessors(N node) { + return delegate().successors(node); // transpose + } + + @Override + public Set successors(N node) { + return delegate().predecessors(node); // transpose + } + + @Override + public int inDegree(N node) { + return delegate().outDegree(node); // transpose + } + + @Override + public int outDegree(N node) { + return delegate().inDegree(node); // transpose + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + return delegate().hasEdgeConnecting(transpose(endpoints)); + } + + @Override + public Optional edgeValue(N nodeU, N nodeV) { + return delegate().edgeValue(nodeV, nodeU); // transpose + } + + @Override + public Optional edgeValue(EndpointPair endpoints) { + return delegate().edgeValue(transpose(endpoints)); + } + + @Override + public V edgeValueOrDefault(N nodeU, N nodeV, V defaultValue) { + return delegate().edgeValueOrDefault(nodeV, nodeU, defaultValue); // transpose + } + + @Override + public V edgeValueOrDefault(EndpointPair endpoints, V defaultValue) { + return delegate().edgeValueOrDefault(transpose(endpoints), defaultValue); + } + } + + private static class TransposedNetwork extends ForwardingNetwork { + private final Network network; + + TransposedNetwork(Network network) { + this.network = network; + } + + @Override + Network delegate() { + return network; + } + + @Override + public Set predecessors(N node) { + return delegate().successors(node); // transpose + } + + @Override + public Set successors(N node) { + return delegate().predecessors(node); // transpose + } + + @Override + public int inDegree(N node) { + return delegate().outDegree(node); // transpose + } + + @Override + public int outDegree(N node) { + return delegate().inDegree(node); // transpose + } + + @Override + public Set inEdges(N node) { + return delegate().outEdges(node); // transpose + } + + @Override + public Set outEdges(N node) { + return delegate().inEdges(node); // transpose + } + + @Override + public EndpointPair incidentNodes(E edge) { + EndpointPair endpointPair = delegate().incidentNodes(edge); + return EndpointPair.of(network, endpointPair.nodeV(), endpointPair.nodeU()); // transpose + } + + @Override + public Set edgesConnecting(N nodeU, N nodeV) { + return delegate().edgesConnecting(nodeV, nodeU); // transpose + } + + @Override + public Set edgesConnecting(EndpointPair endpoints) { + return delegate().edgesConnecting(transpose(endpoints)); + } + + @Override + public Optional edgeConnecting(N nodeU, N nodeV) { + return delegate().edgeConnecting(nodeV, nodeU); // transpose + } + + @Override + public Optional edgeConnecting(EndpointPair endpoints) { + return delegate().edgeConnecting(transpose(endpoints)); + } + + @Override + public E edgeConnectingOrNull(N nodeU, N nodeV) { + return delegate().edgeConnectingOrNull(nodeV, nodeU); // transpose + } + + @Override + public E edgeConnectingOrNull(EndpointPair endpoints) { + return delegate().edgeConnectingOrNull(transpose(endpoints)); + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + return delegate().hasEdgeConnecting(transpose(endpoints)); + } + } + + // Graph copy methods + + /** + * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph + * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges} + * from {@code graph} for which both nodes are contained by {@code nodes}. + * + * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph + */ + public static MutableGraph inducedSubgraph(Graph graph, Iterable nodes) { + MutableGraph subgraph = + (nodes instanceof Collection) + ? GraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build() + : GraphBuilder.from(graph).build(); + for (N node : nodes) { + subgraph.addNode(node); + } + for (N node : subgraph.nodes()) { + for (N successorNode : graph.successors(node)) { + if (subgraph.nodes().contains(successorNode)) { + subgraph.putEdge(node, successorNode); + } + } + } + return subgraph; + } + + /** + * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph + * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges} + * (and associated edge values) from {@code graph} for which both nodes are contained by {@code + * nodes}. + * + * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph + */ + public static MutableValueGraph inducedSubgraph( + ValueGraph graph, Iterable nodes) { + MutableValueGraph subgraph = + (nodes instanceof Collection) + ? ValueGraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build() + : ValueGraphBuilder.from(graph).build(); + for (N node : nodes) { + subgraph.addNode(node); + } + for (N node : subgraph.nodes()) { + for (N successorNode : graph.successors(node)) { + if (subgraph.nodes().contains(successorNode)) { + // requireNonNull is safe because the endpoint pair comes from the graph. + subgraph.putEdgeValue( + node, + successorNode, + requireNonNull(graph.edgeValueOrDefault(node, successorNode, null))); + } + } + } + return subgraph; + } + + /** + * Returns the subgraph of {@code network} induced by {@code nodes}. This subgraph is a new graph + * that contains all of the nodes in {@code nodes}, and all of the {@link Network#edges() edges} + * from {@code network} for which the {@link Network#incidentNodes(Object) incident nodes} are + * both contained by {@code nodes}. + * + * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph + */ + public static MutableNetwork inducedSubgraph( + Network network, Iterable nodes) { + MutableNetwork subgraph = + (nodes instanceof Collection) + ? NetworkBuilder.from(network).expectedNodeCount(((Collection) nodes).size()).build() + : NetworkBuilder.from(network).build(); + for (N node : nodes) { + subgraph.addNode(node); + } + for (N node : subgraph.nodes()) { + for (E edge : network.outEdges(node)) { + N successorNode = network.incidentNodes(edge).adjacentNode(node); + if (subgraph.nodes().contains(successorNode)) { + subgraph.addEdge(node, successorNode, edge); + } + } + } + return subgraph; + } + + /** Creates a mutable copy of {@code graph} with the same nodes and edges. */ + public static MutableGraph copyOf(Graph graph) { + MutableGraph copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); + for (N node : graph.nodes()) { + copy.addNode(node); + } + for (EndpointPair edge : graph.edges()) { + copy.putEdge(edge.nodeU(), edge.nodeV()); + } + return copy; + } + + /** Creates a mutable copy of {@code graph} with the same nodes, edges, and edge values. */ + public static MutableValueGraph copyOf(ValueGraph graph) { + MutableValueGraph copy = + ValueGraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); + for (N node : graph.nodes()) { + copy.addNode(node); + } + for (EndpointPair edge : graph.edges()) { + // requireNonNull is safe because the endpoint pair comes from the graph. + copy.putEdgeValue( + edge.nodeU(), + edge.nodeV(), + requireNonNull(graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null))); + } + return copy; + } + + /** Creates a mutable copy of {@code network} with the same nodes and edges. */ + public static MutableNetwork copyOf(Network network) { + MutableNetwork copy = + NetworkBuilder.from(network) + .expectedNodeCount(network.nodes().size()) + .expectedEdgeCount(network.edges().size()) + .build(); + for (N node : network.nodes()) { + copy.addNode(node); + } + for (E edge : network.edges()) { + EndpointPair endpointPair = network.incidentNodes(edge); + copy.addEdge(endpointPair.nodeU(), endpointPair.nodeV(), edge); + } + return copy; + } + + static int checkNonNegative(int value) { + checkArgument(value >= 0, "Not true that %s is non-negative.", value); + return value; + } + + static long checkNonNegative(long value) { + checkArgument(value >= 0, "Not true that %s is non-negative.", value); + return value; + } + + static int checkPositive(int value) { + checkArgument(value > 0, "Not true that %s is positive.", value); + return value; + } + + static long checkPositive(long value) { + checkArgument(value > 0, "Not true that %s is positive.", value); + return value; + } + + /** + * An enum representing the state of a node during DFS. {@code PENDING} means that the node is on + * the stack of the DFS, while {@code COMPLETE} means that the node and all its successors have + * been already explored. Any node that has not been explored will not have a state at all. + */ + private enum NodeVisitState { + PENDING, + COMPLETE + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableGraph.java new file mode 100644 index 0000000000000..acd380475e988 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableGraph.java @@ -0,0 +1,198 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.Functions; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.graph.GraphConstants.Presence; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Graph} whose elements and structural relationships will never change. Instances of this + * class may be obtained with {@link #copyOf(Graph)}. + * + *

See the Guava User's Guide's discussion + * of the {@code Immutable*} types for more information on the properties and guarantees + * provided by this class. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @author Omar Darwish + * @author Jens Nyman + * @param Node parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public class ImmutableGraph extends ForwardingGraph { + @SuppressWarnings("Immutable") // The backing graph must be immutable. + private final BaseGraph backingGraph; + + ImmutableGraph(BaseGraph backingGraph) { + this.backingGraph = backingGraph; + } + + /** Returns an immutable copy of {@code graph}. */ + public static ImmutableGraph copyOf(Graph graph) { + return (graph instanceof ImmutableGraph) + ? (ImmutableGraph) graph + : new ImmutableGraph( + new StandardValueGraph( + GraphBuilder.from(graph), getNodeConnections(graph), graph.edges().size())); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + */ + @Deprecated + public static ImmutableGraph copyOf(ImmutableGraph graph) { + return checkNotNull(graph); + } + + @Override + public ElementOrder incidentEdgeOrder() { + return ElementOrder.stable(); + } + + private static ImmutableMap> getNodeConnections( + Graph graph) { + // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have + // whatever ordering the graph's nodes do, so ImmutableSortedMap is unnecessary even if the + // input nodes are sorted. + ImmutableMap.Builder> nodeConnections = ImmutableMap.builder(); + for (N node : graph.nodes()) { + nodeConnections.put(node, connectionsOf(graph, node)); + } + return nodeConnections.buildOrThrow(); + } + + @SuppressWarnings("unchecked") + private static GraphConnections connectionsOf(Graph graph, N node) { + Function edgeValueFn = + (Function) Functions.constant(Presence.EDGE_EXISTS); + return graph.isDirected() + ? DirectedGraphConnections.ofImmutable(node, graph.incidentEdges(node), edgeValueFn) + : UndirectedGraphConnections.ofImmutable( + Maps.asMap(graph.adjacentNodes(node), edgeValueFn)); + } + + @Override + BaseGraph delegate() { + return backingGraph; + } + + /** + * A builder for creating {@link ImmutableGraph} instances, especially {@code static final} + * graphs. Example: + * + *

{@code
+   * static final ImmutableGraph COUNTRY_ADJACENCY_GRAPH =
+   *     GraphBuilder.undirected()
+   *         .immutable()
+   *         .putEdge(FRANCE, GERMANY)
+   *         .putEdge(FRANCE, BELGIUM)
+   *         .putEdge(GERMANY, BELGIUM)
+   *         .addNode(ICELAND)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple graphs in series. Each new graph contains all the elements of the ones created before + * it. + * + * @since 28.0 + */ + public static class Builder { + + private final MutableGraph mutableGraph; + + Builder(GraphBuilder graphBuilder) { + // The incidentEdgeOrder for immutable graphs is always stable. However, we don't want to + // modify this builder, so we make a copy instead. + this.mutableGraph = graphBuilder.copy().incidentEdgeOrder(ElementOrder.stable()).build(); + } + + /** + * Adds {@code node} if it is not already present. + * + *

Nodes must be unique, just as {@code Map} keys must be. They must also be non-null. + * + * @return this {@code Builder} object + */ + public Builder addNode(N node) { + mutableGraph.addNode(node); + return this; + } + + /** + * Adds an edge connecting {@code nodeU} to {@code nodeV} if one is not already present. + * + *

If the graph is directed, the resultant edge will be directed; otherwise, it will be + * undirected. + * + *

If {@code nodeU} and {@code nodeV} are not already present in this graph, this method will + * silently {@link #addNode(Object) add} {@code nodeU} and {@code nodeV} to the graph. + * + * @return this {@code Builder} object + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + */ + public Builder putEdge(N nodeU, N nodeV) { + mutableGraph.putEdge(nodeU, nodeV); + return this; + } + + /** + * Adds an edge connecting {@code endpoints} (in the order, if any, specified by {@code + * endpoints}) if one is not already present. + * + *

If this graph is directed, {@code endpoints} must be ordered and the added edge will be + * directed; if it is undirected, the added edge will be undirected. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + *

If either or both endpoints are not already present in this graph, this method will + * silently {@link #addNode(Object) add} each missing endpoint to the graph. + * + * @return this {@code Builder} object + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + */ + public Builder putEdge(EndpointPair endpoints) { + mutableGraph.putEdge(endpoints); + return this; + } + + /** + * Returns a newly-created {@code ImmutableGraph} based on the contents of this {@code Builder}. + */ + public ImmutableGraph build() { + return ImmutableGraph.copyOf(mutableGraph); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableNetwork.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableNetwork.java new file mode 100644 index 0000000000000..874ac740efc0e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableNetwork.java @@ -0,0 +1,238 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Maps; + +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Network} whose elements and structural relationships will never change. Instances of + * this class may be obtained with {@link #copyOf(Network)}. + * + *

See the Guava User's Guide's discussion + * of the {@code Immutable*} types for more information on the properties and guarantees + * provided by this class. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @author Omar Darwish + * @author Jens Nyman + * @param Node parameter type + * @param Edge parameter type + * @since 20.0 + */ +@Beta +@SuppressWarnings("Immutable") // Extends StandardNetwork but uses ImmutableMaps. +@ElementTypesAreNonnullByDefault +public final class ImmutableNetwork extends StandardNetwork { + + private ImmutableNetwork(Network network) { + super( + NetworkBuilder.from(network), getNodeConnections(network), getEdgeToReferenceNode(network)); + } + + /** Returns an immutable copy of {@code network}. */ + public static ImmutableNetwork copyOf(Network network) { + return (network instanceof ImmutableNetwork) + ? (ImmutableNetwork) network + : new ImmutableNetwork(network); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + */ + @Deprecated + public static ImmutableNetwork copyOf(ImmutableNetwork network) { + return checkNotNull(network); + } + + @Override + public ImmutableGraph asGraph() { + return new ImmutableGraph<>(super.asGraph()); // safe because the view is effectively immutable + } + + private static Map> getNodeConnections(Network network) { + // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have + // whatever ordering the network's nodes do, so ImmutableSortedMap is unnecessary even if the + // input nodes are sorted. + ImmutableMap.Builder> nodeConnections = ImmutableMap.builder(); + for (N node : network.nodes()) { + nodeConnections.put(node, connectionsOf(network, node)); + } + return nodeConnections.buildOrThrow(); + } + + private static Map getEdgeToReferenceNode(Network network) { + // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have + // whatever ordering the network's edges do, so ImmutableSortedMap is unnecessary even if the + // input edges are sorted. + ImmutableMap.Builder edgeToReferenceNode = ImmutableMap.builder(); + for (E edge : network.edges()) { + edgeToReferenceNode.put(edge, network.incidentNodes(edge).nodeU()); + } + return edgeToReferenceNode.buildOrThrow(); + } + + private static NetworkConnections connectionsOf(Network network, N node) { + if (network.isDirected()) { + Map inEdgeMap = Maps.asMap(network.inEdges(node), sourceNodeFn(network)); + Map outEdgeMap = Maps.asMap(network.outEdges(node), targetNodeFn(network)); + int selfLoopCount = network.edgesConnecting(node, node).size(); + return network.allowsParallelEdges() + ? DirectedMultiNetworkConnections.ofImmutable(inEdgeMap, outEdgeMap, selfLoopCount) + : DirectedNetworkConnections.ofImmutable(inEdgeMap, outEdgeMap, selfLoopCount); + } else { + Map incidentEdgeMap = + Maps.asMap(network.incidentEdges(node), adjacentNodeFn(network, node)); + return network.allowsParallelEdges() + ? UndirectedMultiNetworkConnections.ofImmutable(incidentEdgeMap) + : UndirectedNetworkConnections.ofImmutable(incidentEdgeMap); + } + } + + private static Function sourceNodeFn(Network network) { + return (E edge) -> network.incidentNodes(edge).source(); + } + + private static Function targetNodeFn(Network network) { + return (E edge) -> network.incidentNodes(edge).target(); + } + + private static Function adjacentNodeFn(Network network, N node) { + return (E edge) -> network.incidentNodes(edge).adjacentNode(node); + } + + /** + * A builder for creating {@link ImmutableNetwork} instances, especially {@code static final} + * networks. Example: + * + *

{@code
+   * static final ImmutableNetwork TRAIN_NETWORK =
+   *     NetworkBuilder.undirected()
+   *         .allowsParallelEdges(true)
+   *         .immutable()
+   *         .addEdge(PARIS, BRUSSELS, Thalys.trainNumber("1111"))
+   *         .addEdge(PARIS, BRUSSELS, RegionalTrain.trainNumber("2222"))
+   *         .addEdge(LONDON, PARIS, Eurostar.trainNumber("3333"))
+   *         .addEdge(LONDON, BRUSSELS, Eurostar.trainNumber("4444"))
+   *         .addNode(REYKJAVIK)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple networks in series. Each new network contains all the elements of the ones created + * before it. + * + * @since 28.0 + */ + public static class Builder { + + private final MutableNetwork mutableNetwork; + + Builder(NetworkBuilder networkBuilder) { + this.mutableNetwork = networkBuilder.build(); + } + + /** + * Adds {@code node} if it is not already present. + * + *

Nodes must be unique, just as {@code Map} keys must be. They must also be non-null. + * + * @return this {@code Builder} object + */ + public ImmutableNetwork.Builder addNode(N node) { + mutableNetwork.addNode(node); + return this; + } + + /** + * Adds {@code edge} connecting {@code nodeU} to {@code nodeV}. + * + *

If the network is directed, {@code edge} will be directed in this network; otherwise, it + * will be undirected. + * + *

{@code edge} must be unique to this network, just as a {@code Map} key must be. It + * must also be non-null. + * + *

If {@code nodeU} and {@code nodeV} are not already present in this network, this method + * will silently {@link #addNode(Object) add} {@code nodeU} and {@code nodeV} to the network. + * + *

If {@code edge} already connects {@code nodeU} to {@code nodeV} (in the specified order if + * this network {@link #isDirected()}, else in any order), then this method will have no effect. + * + * @return this {@code Builder} object + * @throws IllegalArgumentException if {@code edge} already exists in the network and does not + * connect {@code nodeU} to {@code nodeV} + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsParallelEdges()} or {@link #allowsSelfLoops()} + */ + public ImmutableNetwork.Builder addEdge(N nodeU, N nodeV, E edge) { + mutableNetwork.addEdge(nodeU, nodeV, edge); + return this; + } + + /** + * Adds {@code edge} connecting {@code endpoints}. In an undirected network, {@code edge} will + * also connect {@code nodeV} to {@code nodeU}. + * + *

If this network is directed, {@code edge} will be directed in this network; if it is + * undirected, {@code edge} will be undirected in this network. + * + *

If this network is directed, {@code endpoints} must be ordered. + * + *

{@code edge} must be unique to this network, just as a {@code Map} key must be. It + * must also be non-null. + * + *

If either or both endpoints are not already present in this network, this method will + * silently {@link #addNode(Object) add} each missing endpoint to the network. + * + *

If {@code edge} already connects an endpoint pair equal to {@code endpoints}, then this + * method will have no effect. + * + * @return this {@code Builder} object + * @throws IllegalArgumentException if {@code edge} already exists in the network and connects + * some other endpoint pair that is not equal to {@code endpoints} + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsParallelEdges()} or {@link #allowsSelfLoops()} + * @throws IllegalArgumentException if the endpoints are unordered and the network is directed + */ + public ImmutableNetwork.Builder addEdge(EndpointPair endpoints, E edge) { + mutableNetwork.addEdge(endpoints, edge); + return this; + } + + /** + * Returns a newly-created {@code ImmutableNetwork} based on the contents of this {@code + * Builder}. + */ + public ImmutableNetwork build() { + return ImmutableNetwork.copyOf(mutableNetwork); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableValueGraph.java new file mode 100644 index 0000000000000..ca9d603f02450 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ImmutableValueGraph.java @@ -0,0 +1,202 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Maps; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link ValueGraph} whose elements and structural relationships will never change. Instances of + * this class may be obtained with {@link #copyOf(ValueGraph)}. + * + *

See the Guava User's Guide's discussion + * of the {@code Immutable*} types for more information on the properties and guarantees + * provided by this class. + * + * @author James Sexton + * @author Jens Nyman + * @param Node parameter type + * @param Value parameter type + * @since 20.0 + */ +@Beta +@SuppressWarnings("Immutable") // Extends StandardValueGraph but uses ImmutableMaps. +@ElementTypesAreNonnullByDefault +public final class ImmutableValueGraph extends StandardValueGraph { + + private ImmutableValueGraph(ValueGraph graph) { + super(ValueGraphBuilder.from(graph), getNodeConnections(graph), graph.edges().size()); + } + + /** Returns an immutable copy of {@code graph}. */ + public static ImmutableValueGraph copyOf(ValueGraph graph) { + return (graph instanceof ImmutableValueGraph) + ? (ImmutableValueGraph) graph + : new ImmutableValueGraph(graph); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + */ + @Deprecated + public static ImmutableValueGraph copyOf(ImmutableValueGraph graph) { + return checkNotNull(graph); + } + + @Override + public ElementOrder incidentEdgeOrder() { + return ElementOrder.stable(); + } + + @Override + public ImmutableGraph asGraph() { + return new ImmutableGraph<>(this); // safe because the view is effectively immutable + } + + private static ImmutableMap> getNodeConnections( + ValueGraph graph) { + // ImmutableMap.Builder maintains the order of the elements as inserted, so the map will have + // whatever ordering the graph's nodes do, so ImmutableSortedMap is unnecessary even if the + // input nodes are sorted. + ImmutableMap.Builder> nodeConnections = ImmutableMap.builder(); + for (N node : graph.nodes()) { + nodeConnections.put(node, connectionsOf(graph, node)); + } + return nodeConnections.buildOrThrow(); + } + + private static GraphConnections connectionsOf(ValueGraph graph, N node) { + Function successorNodeToValueFn = + (N successorNode) -> + // requireNonNull is safe because the endpoint pair comes from the graph. + requireNonNull(graph.edgeValueOrDefault(node, successorNode, null)); + return graph.isDirected() + ? DirectedGraphConnections.ofImmutable( + node, graph.incidentEdges(node), successorNodeToValueFn) + : UndirectedGraphConnections.ofImmutable( + Maps.asMap(graph.adjacentNodes(node), successorNodeToValueFn)); + } + + /** + * A builder for creating {@link ImmutableValueGraph} instances, especially {@code static final} + * graphs. Example: + * + *

{@code
+   * static final ImmutableValueGraph CITY_ROAD_DISTANCE_GRAPH =
+   *     ValueGraphBuilder.undirected()
+   *         .immutable()
+   *         .putEdgeValue(PARIS, BERLIN, kilometers(1060))
+   *         .putEdgeValue(PARIS, BRUSSELS, kilometers(317))
+   *         .putEdgeValue(BERLIN, BRUSSELS, kilometers(764))
+   *         .addNode(REYKJAVIK)
+   *         .build();
+   * }
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build + * multiple graphs in series. Each new graph contains all the elements of the ones created before + * it. + * + * @since 28.0 + */ + public static class Builder { + + private final MutableValueGraph mutableValueGraph; + + Builder(ValueGraphBuilder graphBuilder) { + // The incidentEdgeOrder for immutable graphs is always stable. However, we don't want to + // modify this builder, so we make a copy instead. + this.mutableValueGraph = + graphBuilder.copy().incidentEdgeOrder(ElementOrder.stable()).build(); + } + + /** + * Adds {@code node} if it is not already present. + * + *

Nodes must be unique, just as {@code Map} keys must be. They must also be non-null. + * + * @return this {@code Builder} object + */ + public ImmutableValueGraph.Builder addNode(N node) { + mutableValueGraph.addNode(node); + return this; + } + + /** + * Adds an edge connecting {@code nodeU} to {@code nodeV} if one is not already present, and + * sets a value for that edge to {@code value} (overwriting the existing value, if any). + * + *

If the graph is directed, the resultant edge will be directed; otherwise, it will be + * undirected. + * + *

Values do not have to be unique. However, values must be non-null. + * + *

If {@code nodeU} and {@code nodeV} are not already present in this graph, this method will + * silently {@link #addNode(Object) add} {@code nodeU} and {@code nodeV} to the graph. + * + * @return this {@code Builder} object + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + */ + public ImmutableValueGraph.Builder putEdgeValue(N nodeU, N nodeV, V value) { + mutableValueGraph.putEdgeValue(nodeU, nodeV, value); + return this; + } + + /** + * Adds an edge connecting {@code endpoints} if one is not already present, and sets a value for + * that edge to {@code value} (overwriting the existing value, if any). + * + *

If the graph is directed, the resultant edge will be directed; otherwise, it will be + * undirected. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + *

Values do not have to be unique. However, values must be non-null. + * + *

If either or both endpoints are not already present in this graph, this method will + * silently {@link #addNode(Object) add} each missing endpoint to the graph. + * + * @return this {@code Builder} object + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + */ + public ImmutableValueGraph.Builder putEdgeValue(EndpointPair endpoints, V value) { + mutableValueGraph.putEdgeValue(endpoints, value); + return this; + } + + /** + * Returns a newly-created {@code ImmutableValueGraph} based on the contents of this {@code + * Builder}. + */ + public ImmutableValueGraph build() { + return ImmutableValueGraph.copyOf(mutableValueGraph); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/IncidentEdgeSet.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/IncidentEdgeSet.java new file mode 100644 index 0000000000000..5e2d3d420329f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/IncidentEdgeSet.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.AbstractSet; +import java.util.Set; + +/** + * Abstract base class for an incident edges set that allows different implementations of {@link + * AbstractSet#iterator()}. + */ +@ElementTypesAreNonnullByDefault +abstract class IncidentEdgeSet extends AbstractSet> { + final N node; + final BaseGraph graph; + + IncidentEdgeSet(BaseGraph graph, N node) { + this.graph = graph; + this.node = node; + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + if (graph.isDirected()) { + return graph.inDegree(node) + + graph.outDegree(node) + - (graph.successors(node).contains(node) ? 1 : 0); + } else { + return graph.adjacentNodes(node).size(); + } + } + + @Override + public boolean contains(Object obj) { + if (!(obj instanceof EndpointPair)) { + return false; + } + EndpointPair endpointPair = (EndpointPair) obj; + + if (graph.isDirected()) { + if (!endpointPair.isOrdered()) { + return false; + } + + Object source = endpointPair.source(); + Object target = endpointPair.target(); + return (node.equals(source) && graph.successors(node).contains(target)) + || (node.equals(target) && graph.predecessors(node).contains(source)); + } else { + if (endpointPair.isOrdered()) { + return false; + } + Set adjacent = graph.adjacentNodes(node); + Object nodeU = endpointPair.nodeU(); + Object nodeV = endpointPair.nodeV(); + + return (node.equals(nodeV) && adjacent.contains(nodeU)) + || (node.equals(nodeU) && adjacent.contains(nodeV)); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MapIteratorCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MapIteratorCache.java new file mode 100644 index 0000000000000..ff0d474317bb6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MapIteratorCache.java @@ -0,0 +1,151 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A map-like data structure that wraps a backing map and caches values while iterating through + * {@link #unmodifiableKeySet()}. By design, the cache is cleared when this structure is mutated. If + * this structure is never mutated, it provides a thread-safe view of the backing map. + * + *

The {@link MapIteratorCache} assumes ownership of the backing map, and cannot guarantee + * correctness in the face of external mutations to the backing map. As such, it is strongly + * recommended that the caller does not persist a reference to the backing map (unless the backing + * map is immutable). + * + *

This class is tailored toward use cases in common.graph. It is *NOT* a general purpose map. + * + * @author James Sexton + */ +@ElementTypesAreNonnullByDefault +class MapIteratorCache { + private final Map backingMap; + + /* + * Per JDK: "the behavior of a map entry is undefined if the backing map has been modified after + * the entry was returned by the iterator, except through the setValue operation on the map entry" + * As such, this field must be cleared before every map mutation. + * + * Note about volatile: volatile doesn't make it safe to read from a mutable graph in one thread + * while writing to it in another. All it does is help with _reading_ from multiple threads + * concurrently. For more information, see AbstractNetworkTest.concurrentIteration. + */ + private transient volatile Entry cacheEntry; + + MapIteratorCache(Map backingMap) { + this.backingMap = checkNotNull(backingMap); + } + + final V put(K key, V value) { + checkNotNull(key); + checkNotNull(value); + clearCache(); + return backingMap.put(key, value); + } + + final V remove(Object key) { + checkNotNull(key); + clearCache(); + return backingMap.remove(key); + } + + final void clear() { + clearCache(); + backingMap.clear(); + } + + V get(Object key) { + checkNotNull(key); + V value = getIfCached(key); + // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. + if (value == null) { + return getWithoutCaching(key); + } else { + return value; + } + } + + final V getWithoutCaching(Object key) { + checkNotNull(key); + return backingMap.get(key); + } + + final boolean containsKey(Object key) { + return getIfCached(key) != null || backingMap.containsKey(key); + } + + final Set unmodifiableKeySet() { + return new AbstractSet() { + @Override + public UnmodifiableIterator iterator() { + Iterator> entryIterator = backingMap.entrySet().iterator(); + + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public K next() { + Entry entry = entryIterator.next(); // store local reference for thread-safety + cacheEntry = entry; + return entry.getKey(); + } + }; + } + + @Override + public int size() { + return backingMap.size(); + } + + @Override + public boolean contains(Object key) { + return containsKey(key); + } + }; + } + + // Internal methods (package-visible, but treat as only subclass-visible) + + V getIfCached(Object key) { + Entry entry = cacheEntry; // store local reference for thread-safety + + // Check cache. We use == on purpose because it's cheaper and a cache miss is ok. + if (entry != null && entry.getKey() == key) { + return entry.getValue(); + } + return null; + } + + void clearCache() { + cacheEntry = null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MapRetrievalCache.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MapRetrievalCache.java new file mode 100644 index 0000000000000..8b993c06f7075 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MapRetrievalCache.java @@ -0,0 +1,112 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Map; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link MapIteratorCache} that adds additional caching. In addition to the caching provided by + * {@link MapIteratorCache}, this structure caches values for the two most recently retrieved keys. + * + * @author James Sexton + */ +@ElementTypesAreNonnullByDefault +final class MapRetrievalCache extends MapIteratorCache { + // See the note about volatile in the superclass. + private transient volatile CacheEntry cacheEntry1; + private transient volatile CacheEntry cacheEntry2; + + MapRetrievalCache(Map backingMap) { + super(backingMap); + } + + @SuppressWarnings("unchecked") // Safe because we only cast if key is found in map. + @Override + V get(Object key) { + checkNotNull(key); + V value = getIfCached(key); + if (value != null) { + return value; + } + + value = getWithoutCaching(key); + if (value != null) { + addToCache((K) key, value); + } + return value; + } + + // Internal methods (package-visible, but treat as only subclass-visible) + + @Override + V getIfCached(Object key) { + V value = super.getIfCached(key); + if (value != null) { + return value; + } + + // Store a local reference to the cache entry. If the backing map is immutable, this, + // in combination with immutable cache entries, will ensure a thread-safe cache. + CacheEntry entry; + + // Check cache. We use == on purpose because it's cheaper and a cache miss is ok. + entry = cacheEntry1; + if (entry != null && entry.key == key) { + return entry.value; + } + entry = cacheEntry2; + if (entry != null && entry.key == key) { + // Promote second cache entry to first so the access pattern + // [K1, K2, K1, K3, K1, K4...] still hits the cache half the time. + addToCache(entry); + return entry.value; + } + return null; + } + + @Override + void clearCache() { + super.clearCache(); + cacheEntry1 = null; + cacheEntry2 = null; + } + + private void addToCache(K key, V value) { + addToCache(new CacheEntry(key, value)); + } + + private void addToCache(CacheEntry entry) { + // Slide new entry into first cache position. Drop previous entry in second cache position. + cacheEntry2 = cacheEntry1; + cacheEntry1 = entry; + } + + private static final class CacheEntry { + final K key; + final V value; + + CacheEntry(K key, V value) { + this.key = key; + this.value = value; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MultiEdgesConnecting.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MultiEdgesConnecting.java new file mode 100644 index 0000000000000..270737e9588ba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MultiEdgesConnecting.java @@ -0,0 +1,73 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.AbstractIterator; +import org.apache.iotdb.google.common.collect.UnmodifiableIterator; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A class to represent the set of edges connecting an (implicit) origin node to a target node. + * + *

The {@link #outEdgeToNode} map allows this class to work on networks with parallel edges. See + * {@link EdgesConnecting} for a class that is more efficient but forbids parallel edges. + * + * @author James Sexton + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +abstract class MultiEdgesConnecting extends AbstractSet { + + private final Map outEdgeToNode; + private final Object targetNode; + + MultiEdgesConnecting(Map outEdgeToNode, Object targetNode) { + this.outEdgeToNode = checkNotNull(outEdgeToNode); + this.targetNode = checkNotNull(targetNode); + } + + @Override + public UnmodifiableIterator iterator() { + Iterator> entries = outEdgeToNode.entrySet().iterator(); + return new AbstractIterator() { + @Override + protected E computeNext() { + while (entries.hasNext()) { + Entry entry = entries.next(); + if (targetNode.equals(entry.getValue())) { + return entry.getKey(); + } + } + return endOfData(); + } + }; + } + + @Override + public boolean contains(Object edge) { + return targetNode.equals(outEdgeToNode.get(edge)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableGraph.java new file mode 100644 index 0000000000000..3ae74430e8581 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableGraph.java @@ -0,0 +1,105 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +/** + * A subinterface of {@link Graph} which adds mutation methods. When mutation is not required, users + * should prefer the {@link Graph} interface. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @param Node parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface MutableGraph extends Graph { + + /** + * Adds {@code node} if it is not already present. + * + *

Nodes must be unique, just as {@code Map} keys must be. They must also be non-null. + * + * @return {@code true} if the graph was modified as a result of this call + */ + boolean addNode(N node); + + /** + * Adds an edge connecting {@code nodeU} to {@code nodeV} if one is not already present. + * + *

If the graph is directed, the resultant edge will be directed; otherwise, it will be + * undirected. + * + *

If {@code nodeU} and {@code nodeV} are not already present in this graph, this method will + * silently {@link #addNode(Object) add} {@code nodeU} and {@code nodeV} to the graph. + * + * @return {@code true} if the graph was modified as a result of this call + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + */ + boolean putEdge(N nodeU, N nodeV); + + /** + * Adds an edge connecting {@code endpoints} (in the order, if any, specified by {@code + * endpoints}) if one is not already present. + * + *

If this graph is directed, {@code endpoints} must be ordered and the added edge will be + * directed; if it is undirected, the added edge will be undirected. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + *

If either or both endpoints are not already present in this graph, this method will silently + * {@link #addNode(Object) add} each missing endpoint to the graph. + * + * @return {@code true} if the graph was modified as a result of this call + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + boolean putEdge(EndpointPair endpoints); + + /** + * Removes {@code node} if it is present; all edges incident to {@code node} will also be removed. + * + * @return {@code true} if the graph was modified as a result of this call + */ + boolean removeNode(N node); + + /** + * Removes the edge connecting {@code nodeU} to {@code nodeV}, if it is present. + * + * @return {@code true} if the graph was modified as a result of this call + */ + boolean removeEdge(N nodeU, N nodeV); + + /** + * Removes the edge connecting {@code endpoints}, if it is present. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @return {@code true} if the graph was modified as a result of this call + * @since 27.1 + */ + boolean removeEdge(EndpointPair endpoints); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableNetwork.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableNetwork.java new file mode 100644 index 0000000000000..2c5480fe58872 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableNetwork.java @@ -0,0 +1,111 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +/** + * A subinterface of {@link Network} which adds mutation methods. When mutation is not required, + * users should prefer the {@link Network} interface. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @param Node parameter type + * @param Edge parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface MutableNetwork extends Network { + + /** + * Adds {@code node} if it is not already present. + * + *

Nodes must be unique, just as {@code Map} keys must be. They must also be non-null. + * + * @return {@code true} if the network was modified as a result of this call + */ + boolean addNode(N node); + + /** + * Adds {@code edge} connecting {@code nodeU} to {@code nodeV}. + * + *

If the graph is directed, {@code edge} will be directed in this graph; otherwise, it will be + * undirected. + * + *

{@code edge} must be unique to this graph, just as a {@code Map} key must be. It must + * also be non-null. + * + *

If {@code nodeU} and {@code nodeV} are not already present in this graph, this method will + * silently {@link #addNode(Object) add} {@code nodeU} and {@code nodeV} to the graph. + * + *

If {@code edge} already connects {@code nodeU} to {@code nodeV} (in the specified order if + * this network {@link #isDirected()}, else in any order), then this method will have no effect. + * + * @return {@code true} if the network was modified as a result of this call + * @throws IllegalArgumentException if {@code edge} already exists in the graph and does not + * connect {@code nodeU} to {@code nodeV} + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsParallelEdges()} or {@link #allowsSelfLoops()} + */ + boolean addEdge(N nodeU, N nodeV, E edge); + + /** + * Adds {@code edge} connecting {@code endpoints}. In an undirected network, {@code edge} will + * also connect {@code nodeV} to {@code nodeU}. + * + *

If this graph is directed, {@code edge} will be directed in this graph; if it is undirected, + * {@code edge} will be undirected in this graph. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + *

{@code edge} must be unique to this graph, just as a {@code Map} key must be. It must + * also be non-null. + * + *

If either or both endpoints are not already present in this graph, this method will silently + * {@link #addNode(Object) add} each missing endpoint to the graph. + * + *

If {@code edge} already connects an endpoint pair equal to {@code endpoints}, then this + * method will have no effect. + * + * @return {@code true} if the network was modified as a result of this call + * @throws IllegalArgumentException if {@code edge} already exists in the graph and connects some + * other endpoint pair that is not equal to {@code endpoints} + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsParallelEdges()} or {@link #allowsSelfLoops()} + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + boolean addEdge(EndpointPair endpoints, E edge); + + /** + * Removes {@code node} if it is present; all edges incident to {@code node} will also be removed. + * + * @return {@code true} if the network was modified as a result of this call + */ + boolean removeNode(N node); + + /** + * Removes {@code edge} from this network, if it is present. + * + * @return {@code true} if the network was modified as a result of this call + */ + boolean removeEdge(E edge); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableValueGraph.java new file mode 100644 index 0000000000000..dbd61323e9bd7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/MutableValueGraph.java @@ -0,0 +1,113 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +/** + * A subinterface of {@link ValueGraph} which adds mutation methods. When mutation is not required, + * users should prefer the {@link ValueGraph} interface. + * + * @author James Sexton + * @param Node parameter type + * @param Value parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface MutableValueGraph extends ValueGraph { + + /** + * Adds {@code node} if it is not already present. + * + *

Nodes must be unique, just as {@code Map} keys must be. They must also be non-null. + * + * @return {@code true} if the graph was modified as a result of this call + */ + boolean addNode(N node); + + /** + * Adds an edge connecting {@code nodeU} to {@code nodeV} if one is not already present, and sets + * a value for that edge to {@code value} (overwriting the existing value, if any). + * + *

If the graph is directed, the resultant edge will be directed; otherwise, it will be + * undirected. + * + *

Values do not have to be unique. However, values must be non-null. + * + *

If {@code nodeU} and {@code nodeV} are not already present in this graph, this method will + * silently {@link #addNode(Object) add} {@code nodeU} and {@code nodeV} to the graph. + * + * @return the value previously associated with the edge connecting {@code nodeU} to {@code + * nodeV}, or null if there was no such edge. + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + */ + V putEdgeValue(N nodeU, N nodeV, V value); + + /** + * Adds an edge connecting {@code endpoints} if one is not already present, and sets a value for + * that edge to {@code value} (overwriting the existing value, if any). + * + *

If the graph is directed, the resultant edge will be directed; otherwise, it will be + * undirected. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + *

Values do not have to be unique. However, values must be non-null. + * + *

If either or both endpoints are not already present in this graph, this method will silently + * {@link #addNode(Object) add} each missing endpoint to the graph. + * + * @return the value previously associated with the edge connecting {@code nodeU} to {@code + * nodeV}, or null if there was no such edge. + * @throws IllegalArgumentException if the introduction of the edge would violate {@link + * #allowsSelfLoops()} + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + V putEdgeValue(EndpointPair endpoints, V value); + + /** + * Removes {@code node} if it is present; all edges incident to {@code node} will also be removed. + * + * @return {@code true} if the graph was modified as a result of this call + */ + boolean removeNode(N node); + + /** + * Removes the edge connecting {@code nodeU} to {@code nodeV}, if it is present. + * + * @return the value previously associated with the edge connecting {@code nodeU} to {@code + * nodeV}, or null if there was no such edge. + */ + V removeEdge(N nodeU, N nodeV); + + /** + * Removes the edge connecting {@code endpoints}, if it is present. + * + *

If this graph is directed, {@code endpoints} must be ordered. + * + * @return the value previously associated with the edge connecting {@code endpoints}, or null if + * there was no such edge. + * @since 27.1 + */ + V removeEdge(EndpointPair endpoints); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Network.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Network.java new file mode 100644 index 0000000000000..2f56094a2bea6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Network.java @@ -0,0 +1,431 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.util.Optional; +import java.util.Set; + +/** + * An interface for graph-structured data, + * whose edges are unique objects. + * + *

A graph is composed of a set of nodes and a set of edges connecting pairs of nodes. + * + *

There are three primary interfaces provided to represent graphs. In order of increasing + * complexity they are: {@link Graph}, {@link ValueGraph}, and {@link Network}. You should generally + * prefer the simplest interface that satisfies your use case. See the + * "Choosing the right graph type" section of the Guava User Guide for more details. + * + *

Capabilities

+ * + *

{@code Network} supports the following use cases (definitions of + * terms): + * + *

    + *
  • directed graphs + *
  • undirected graphs + *
  • graphs that do/don't allow parallel edges + *
  • graphs that do/don't allow self-loops + *
  • graphs whose nodes/edges are insertion-ordered, sorted, or unordered + *
  • graphs whose edges are unique objects + *
+ * + *

Building a {@code Network}

+ * + *

The implementation classes that {@code common.graph} provides are not public, by design. To + * create an instance of one of the built-in implementations of {@code Network}, use the {@link + * NetworkBuilder} class: + * + *

{@code
+ * MutableNetwork graph = NetworkBuilder.directed().build();
+ * }
+ * + *

{@link NetworkBuilder#build()} returns an instance of {@link MutableNetwork}, which is a + * subtype of {@code Network} that provides methods for adding and removing nodes and edges. If you + * do not need to mutate a graph (e.g. if you write a method than runs a read-only algorithm on the + * graph), you should use the non-mutating {@link Network} interface, or an {@link + * ImmutableNetwork}. + * + *

You can create an immutable copy of an existing {@code Network} using {@link + * ImmutableNetwork#copyOf(Network)}: + * + *

{@code
+ * ImmutableNetwork immutableGraph = ImmutableNetwork.copyOf(graph);
+ * }
+ * + *

Instances of {@link ImmutableNetwork} do not implement {@link MutableNetwork} (obviously!) and + * are contractually guaranteed to be unmodifiable and thread-safe. + * + *

The Guava User Guide has more + * information on (and examples of) building graphs. + * + *

Additional documentation

+ * + *

See the Guava User Guide for the {@code common.graph} package ("Graphs Explained") for + * additional documentation, including: + * + *

+ * + * @author James Sexton + * @author Joshua O'Madadhain + * @param Node parameter type + * @param Edge parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface Network extends SuccessorsFunction, PredecessorsFunction { + // + // Network-level accessors + // + + /** Returns all nodes in this network, in the order specified by {@link #nodeOrder()}. */ + Set nodes(); + + /** Returns all edges in this network, in the order specified by {@link #edgeOrder()}. */ + Set edges(); + + /** + * Returns a live view of this network as a {@link Graph}. The resulting {@link Graph} will have + * an edge connecting node A to node B if this {@link Network} has an edge connecting A to B. + * + *

If this network {@link #allowsParallelEdges() allows parallel edges}, parallel edges will be + * treated as if collapsed into a single edge. For example, the {@link #degree(Object)} of a node + * in the {@link Graph} view may be less than the degree of the same node in this {@link Network}. + */ + Graph asGraph(); + + // + // Network properties + // + + /** + * Returns true if the edges in this network are directed. Directed edges connect a {@link + * EndpointPair#source() source node} to a {@link EndpointPair#target() target node}, while + * undirected edges connect a pair of nodes to each other. + */ + boolean isDirected(); + + /** + * Returns true if this network allows parallel edges. Attempting to add a parallel edge to a + * network that does not allow them will throw an {@link IllegalArgumentException}. + */ + boolean allowsParallelEdges(); + + /** + * Returns true if this network allows self-loops (edges that connect a node to itself). + * Attempting to add a self-loop to a network that does not allow them will throw an {@link + * IllegalArgumentException}. + */ + boolean allowsSelfLoops(); + + /** Returns the order of iteration for the elements of {@link #nodes()}. */ + ElementOrder nodeOrder(); + + /** Returns the order of iteration for the elements of {@link #edges()}. */ + ElementOrder edgeOrder(); + + // + // Element-level accessors + // + + /** + * Returns the nodes which have an incident edge in common with {@code node} in this network. + * + *

This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + Set adjacentNodes(N node); + + /** + * Returns all nodes in this network adjacent to {@code node} which can be reached by traversing + * {@code node}'s incoming edges against the direction (if any) of the edge. + * + *

In an undirected network, this is equivalent to {@link #adjacentNodes(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + @Override + Set predecessors(N node); + + /** + * Returns all nodes in this network adjacent to {@code node} which can be reached by traversing + * {@code node}'s outgoing edges in the direction (if any) of the edge. + * + *

In an undirected network, this is equivalent to {@link #adjacentNodes(Object)}. + * + *

This is not the same as "all nodes reachable from {@code node} by following outgoing + * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + @Override + Set successors(N node); + + /** + * Returns the edges whose {@link #incidentNodes(Object) incident nodes} in this network include + * {@code node}. + * + *

This is equal to the union of {@link #inEdges(Object)} and {@link #outEdges(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + Set incidentEdges(N node); + + /** + * Returns all edges in this network which can be traversed in the direction (if any) of the edge + * to end at {@code node}. + * + *

In a directed network, an incoming edge's {@link EndpointPair#target()} equals {@code node}. + * + *

In an undirected network, this is equivalent to {@link #incidentEdges(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + Set inEdges(N node); + + /** + * Returns all edges in this network which can be traversed in the direction (if any) of the edge + * starting from {@code node}. + * + *

In a directed network, an outgoing edge's {@link EndpointPair#source()} equals {@code node}. + * + *

In an undirected network, this is equivalent to {@link #incidentEdges(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + Set outEdges(N node); + + /** + * Returns the count of {@code node}'s {@link #incidentEdges(Object) incident edges}, counting + * self-loops twice (equivalently, the number of times an edge touches {@code node}). + * + *

For directed networks, this is equal to {@code inDegree(node) + outDegree(node)}. + * + *

For undirected networks, this is equal to {@code incidentEdges(node).size()} + (number of + * self-loops incident to {@code node}). + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + int degree(N node); + + /** + * Returns the count of {@code node}'s {@link #inEdges(Object) incoming edges} in a directed + * network. In an undirected network, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + int inDegree(N node); + + /** + * Returns the count of {@code node}'s {@link #outEdges(Object) outgoing edges} in a directed + * network. In an undirected network, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this network + */ + int outDegree(N node); + + /** + * Returns the nodes which are the endpoints of {@code edge} in this network. + * + * @throws IllegalArgumentException if {@code edge} is not an element of this network + */ + EndpointPair incidentNodes(E edge); + + /** + * Returns the edges which have an {@link #incidentNodes(Object) incident node} in common with + * {@code edge}. An edge is not considered adjacent to itself. + * + * @throws IllegalArgumentException if {@code edge} is not an element of this network + */ + Set adjacentEdges(E edge); + + /** + * Returns the set of edges that each directly connect {@code nodeU} to {@code nodeV}. + * + *

In an undirected network, this is equal to {@code edgesConnecting(nodeV, nodeU)}. + * + *

The resulting set of edges will be parallel (i.e. have equal {@link + * #incidentNodes(Object)}). If this network does not {@link #allowsParallelEdges() allow parallel + * edges}, the resulting set will contain at most one edge (equivalent to {@code + * edgeConnecting(nodeU, nodeV).asSet()}). + * + * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this + * network + */ + Set edgesConnecting(N nodeU, N nodeV); + + /** + * Returns the set of edges that each directly connect {@code endpoints} (in the order, if any, + * specified by {@code endpoints}). + * + *

The resulting set of edges will be parallel (i.e. have equal {@link + * #incidentNodes(Object)}). If this network does not {@link #allowsParallelEdges() allow parallel + * edges}, the resulting set will contain at most one edge (equivalent to {@code + * edgeConnecting(endpoints).asSet()}). + * + *

If this network is directed, {@code endpoints} must be ordered. + * + * @throws IllegalArgumentException if either endpoint is not an element of this network + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + Set edgesConnecting(EndpointPair endpoints); + + /** + * Returns the single edge that directly connects {@code nodeU} to {@code nodeV}, if one is + * present, or {@code Optional.empty()} if no such edge exists. + * + *

In an undirected network, this is equal to {@code edgeConnecting(nodeV, nodeU)}. + * + * @throws IllegalArgumentException if there are multiple parallel edges connecting {@code nodeU} + * to {@code nodeV} + * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this + * network + * @since 23.0 + */ + Optional edgeConnecting(N nodeU, N nodeV); + + /** + * Returns the single edge that directly connects {@code endpoints} (in the order, if any, + * specified by {@code endpoints}), if one is present, or {@code Optional.empty()} if no such edge + * exists. + * + *

If this graph is directed, the endpoints must be ordered. + * + * @throws IllegalArgumentException if there are multiple parallel edges connecting {@code nodeU} + * to {@code nodeV} + * @throws IllegalArgumentException if either endpoint is not an element of this network + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + Optional edgeConnecting(EndpointPair endpoints); + + /** + * Returns the single edge that directly connects {@code nodeU} to {@code nodeV}, if one is + * present, or {@code null} if no such edge exists. + * + *

In an undirected network, this is equal to {@code edgeConnectingOrNull(nodeV, nodeU)}. + * + * @throws IllegalArgumentException if there are multiple parallel edges connecting {@code nodeU} + * to {@code nodeV} + * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this + * network + * @since 23.0 + */ + E edgeConnectingOrNull(N nodeU, N nodeV); + + /** + * Returns the single edge that directly connects {@code endpoints} (in the order, if any, + * specified by {@code endpoints}), if one is present, or {@code null} if no such edge exists. + * + *

If this graph is directed, the endpoints must be ordered. + * + * @throws IllegalArgumentException if there are multiple parallel edges connecting {@code nodeU} + * to {@code nodeV} + * @throws IllegalArgumentException if either endpoint is not an element of this network + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + E edgeConnectingOrNull(EndpointPair endpoints); + + /** + * Returns true if there is an edge that directly connects {@code nodeU} to {@code nodeV}. This is + * equivalent to {@code nodes().contains(nodeU) && successors(nodeU).contains(nodeV)}, and to + * {@code edgeConnectingOrNull(nodeU, nodeV) != null}. + * + *

In an undirected graph, this is equal to {@code hasEdgeConnecting(nodeV, nodeU)}. + * + * @since 23.0 + */ + boolean hasEdgeConnecting(N nodeU, N nodeV); + + /** + * Returns true if there is an edge that directly connects {@code endpoints} (in the order, if + * any, specified by {@code endpoints}). + * + *

Unlike the other {@code EndpointPair}-accepting methods, this method does not throw if the + * endpoints are unordered and the graph is directed; it simply returns {@code false}. This is for + * consistency with {@link Graph#hasEdgeConnecting(EndpointPair)} and {@link + * ValueGraph#hasEdgeConnecting(EndpointPair)}. + * + * @since 27.1 + */ + boolean hasEdgeConnecting(EndpointPair endpoints); + + // + // Network identity + // + + /** + * Returns {@code true} iff {@code object} is a {@link Network} that has the same elements and the + * same structural relationships as those in this network. + * + *

Thus, two networks A and B are equal if all of the following are true: + * + *

    + *
  • A and B have equal {@link #isDirected() directedness}. + *
  • A and B have equal {@link #nodes() node sets}. + *
  • A and B have equal {@link #edges() edge sets}. + *
  • Every edge in A and B connects the same nodes in the same direction (if any). + *
+ * + *

Network properties besides {@link #isDirected() directedness} do not affect equality. + * For example, two networks may be considered equal even if one allows parallel edges and the + * other doesn't. Additionally, the order in which nodes or edges are added to the network, and + * the order in which they are iterated over, are irrelevant. + * + *

A reference implementation of this is provided by {@link AbstractNetwork#equals(Object)}. + */ + @Override + boolean equals(Object object); + + /** + * Returns the hash code for this network. The hash code of a network is defined as the hash code + * of a map from each of its {@link #edges() edges} to their {@link #incidentNodes(Object) + * incident nodes}. + * + *

A reference implementation of this is provided by {@link AbstractNetwork#hashCode()}. + */ + @Override + int hashCode(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/NetworkBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/NetworkBuilder.java new file mode 100644 index 0000000000000..37ce332058f2c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/NetworkBuilder.java @@ -0,0 +1,201 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Optional; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; + +/** + * A builder for constructing instances of {@link MutableNetwork} or {@link ImmutableNetwork} with + * user-defined properties. + * + *

A {@code Network} built by this class has the following default properties: + * + *

    + *
  • does not allow parallel edges + *
  • does not allow self-loops + *
  • orders {@link Network#nodes()} and {@link Network#edges()} in the order in which the + * elements were added (insertion order) + *
+ * + *

{@code Network}s built by this class also guarantee that each collection-returning accessor + * returns a (live) unmodifiable view; see the external + * documentation for details. + * + *

Examples of use: + * + *

{@code
+ * // Building a mutable network
+ * MutableNetwork network =
+ *     NetworkBuilder.directed().allowsParallelEdges(true).build();
+ * flightNetwork.addEdge("LAX", "ATL", 3025);
+ * flightNetwork.addEdge("LAX", "ATL", 1598);
+ * flightNetwork.addEdge("ATL", "LAX", 2450);
+ *
+ * // Building a immutable network
+ * ImmutableNetwork immutableNetwork =
+ *     NetworkBuilder.directed()
+ *         .allowsParallelEdges(true)
+ *         .immutable()
+ *         .addEdge("LAX", "ATL", 3025)
+ *         .addEdge("LAX", "ATL", 1598)
+ *         .addEdge("ATL", "LAX", 2450)
+ *         .build();
+ * }
+ * + * @author James Sexton + * @author Joshua O'Madadhain + * @param The most general node type this builder will support. This is normally {@code Object} + * unless it is constrained by using a method like {@link #nodeOrder}, or the builder is + * constructed based on an existing {@code Network} using {@link #from(Network)}. + * @param The most general edge type this builder will support. This is normally {@code Object} + * unless it is constrained by using a method like {@link #edgeOrder}, or the builder is + * constructed based on an existing {@code Network} using {@link #from(Network)}. + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class NetworkBuilder extends AbstractGraphBuilder { + boolean allowsParallelEdges = false; + ElementOrder edgeOrder = ElementOrder.insertion(); + Optional expectedEdgeCount = Optional.absent(); + + /** Creates a new instance with the specified edge directionality. */ + private NetworkBuilder(boolean directed) { + super(directed); + } + + /** Returns a {@link NetworkBuilder} for building directed networks. */ + public static NetworkBuilder directed() { + return new NetworkBuilder<>(true); + } + + /** Returns a {@link NetworkBuilder} for building undirected networks. */ + public static NetworkBuilder undirected() { + return new NetworkBuilder<>(false); + } + + /** + * Returns a {@link NetworkBuilder} initialized with all properties queryable from {@code + * network}. + * + *

The "queryable" properties are those that are exposed through the {@link Network} interface, + * such as {@link Network#isDirected()}. Other properties, such as {@link + * #expectedNodeCount(int)}, are not set in the new builder. + */ + public static NetworkBuilder from(Network network) { + return new NetworkBuilder(network.isDirected()) + .allowsParallelEdges(network.allowsParallelEdges()) + .allowsSelfLoops(network.allowsSelfLoops()) + .nodeOrder(network.nodeOrder()) + .edgeOrder(network.edgeOrder()); + } + + /** + * Returns an {@link ImmutableNetwork.Builder} with the properties of this {@link NetworkBuilder}. + * + *

The returned builder can be used for populating an {@link ImmutableNetwork}. + * + * @since 28.0 + */ + public ImmutableNetwork.Builder immutable() { + NetworkBuilder castBuilder = cast(); + return new ImmutableNetwork.Builder<>(castBuilder); + } + + /** + * Specifies whether the network will allow parallel edges. Attempting to add a parallel edge to a + * network that does not allow them will throw an {@link UnsupportedOperationException}. + * + *

The default value is {@code false}. + */ + public NetworkBuilder allowsParallelEdges(boolean allowsParallelEdges) { + this.allowsParallelEdges = allowsParallelEdges; + return this; + } + + /** + * Specifies whether the network will allow self-loops (edges that connect a node to itself). + * Attempting to add a self-loop to a network that does not allow them will throw an {@link + * UnsupportedOperationException}. + * + *

The default value is {@code false}. + */ + public NetworkBuilder allowsSelfLoops(boolean allowsSelfLoops) { + this.allowsSelfLoops = allowsSelfLoops; + return this; + } + + /** + * Specifies the expected number of nodes in the network. + * + * @throws IllegalArgumentException if {@code expectedNodeCount} is negative + */ + public NetworkBuilder expectedNodeCount(int expectedNodeCount) { + this.expectedNodeCount = Optional.of(checkNonNegative(expectedNodeCount)); + return this; + } + + /** + * Specifies the expected number of edges in the network. + * + * @throws IllegalArgumentException if {@code expectedEdgeCount} is negative + */ + public NetworkBuilder expectedEdgeCount(int expectedEdgeCount) { + this.expectedEdgeCount = Optional.of(checkNonNegative(expectedEdgeCount)); + return this; + } + + /** + * Specifies the order of iteration for the elements of {@link Network#nodes()}. + * + *

The default value is {@link ElementOrder#insertion() insertion order}. + */ + public NetworkBuilder nodeOrder(ElementOrder nodeOrder) { + NetworkBuilder newBuilder = cast(); + newBuilder.nodeOrder = checkNotNull(nodeOrder); + return newBuilder; + } + + /** + * Specifies the order of iteration for the elements of {@link Network#edges()}. + * + *

The default value is {@link ElementOrder#insertion() insertion order}. + */ + public NetworkBuilder edgeOrder(ElementOrder edgeOrder) { + NetworkBuilder newBuilder = cast(); + newBuilder.edgeOrder = checkNotNull(edgeOrder); + return newBuilder; + } + + /** Returns an empty {@link MutableNetwork} with the properties of this {@link NetworkBuilder}. */ + public MutableNetwork build() { + return new StandardMutableNetwork<>(this); + } + + @SuppressWarnings("unchecked") + private NetworkBuilder cast() { + return (NetworkBuilder) this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/NetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/NetworkConnections.java new file mode 100644 index 0000000000000..47f93833f6fb4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/NetworkConnections.java @@ -0,0 +1,77 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Set; + +/** + * An interface for representing and manipulating an origin node's adjacent nodes and incident edges + * in a {@link Network}. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +interface NetworkConnections { + + Set adjacentNodes(); + + Set predecessors(); + + Set successors(); + + Set incidentEdges(); + + Set inEdges(); + + Set outEdges(); + + /** + * Returns the set of edges connecting the origin node to {@code node}. For networks without + * parallel edges, this set cannot be of size greater than one. + */ + Set edgesConnecting(N node); + + /** + * Returns the node that is adjacent to the origin node along {@code edge}. + * + *

In the directed case, {@code edge} is assumed to be an outgoing edge. + */ + N adjacentNode(E edge); + + /** + * Remove {@code edge} from the set of incoming edges. Returns the former predecessor node. + * + *

In the undirected case, returns {@code null} if {@code isSelfLoop} is true. + */ + N removeInEdge(E edge, boolean isSelfLoop); + + /** Remove {@code edge} from the set of outgoing edges. Returns the former successor node. */ + N removeOutEdge(E edge); + + /** + * Add {@code edge} to the set of incoming edges. Implicitly adds {@code node} as a predecessor. + */ + void addInEdge(E edge, N node, boolean isSelfLoop); + + /** Add {@code edge} to the set of outgoing edges. Implicitly adds {@code node} as a successor. */ + void addOutEdge(E edge, N node); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ParametricNullness.java new file mode 100644 index 0000000000000..d59f2f8d64426 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/PredecessorsFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/PredecessorsFunction.java new file mode 100644 index 0000000000000..3be2d1a8eea22 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/PredecessorsFunction.java @@ -0,0 +1,107 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +/** + * A functional interface for graph-structured data. + * + *

This interface is meant to be used as the type of a parameter to graph algorithms (such as + * topological sort) that only need a way of accessing the predecessors of a node in a graph. + * + *

Usage

+ * + * Given an algorithm, for example: + * + *
{@code
+ * public  someGraphAlgorithm(N startNode, PredecessorsFunction predecessorsFunction);
+ * }
+ * + * you will invoke it depending on the graph representation you're using. + * + *

If you have an instance of one of the primary {@code common.graph} types ({@link Graph}, + * {@link ValueGraph}, and {@link Network}): + * + *

{@code
+ * someGraphAlgorithm(startNode, graph);
+ * }
+ * + * This works because those types each implement {@code PredecessorsFunction}. It will also work + * with any other implementation of this interface. + * + *

If you have your own graph implementation based around a custom node type {@code MyNode}, + * which has a method {@code getParents()} that retrieves its predecessors in a graph: + * + *

{@code
+ * someGraphAlgorithm(startNode, MyNode::getParents);
+ * }
+ * + *

If you have some other mechanism for returning the predecessors of a node, or one that doesn't + * return a {@code Iterable}, then you can use a lambda to perform a more general + * transformation: + * + *

{@code
+ * someGraphAlgorithm(startNode, node -> ImmutableList.of(node.mother(), node.father()));
+ * }
+ * + *

Graph algorithms that need additional capabilities (accessing both predecessors and + * successors, iterating over the edges, etc.) should declare their input to be of a type that + * provides those capabilities, such as {@link Graph}, {@link ValueGraph}, or {@link Network}. + * + *

Additional documentation

+ * + *

See the Guava User Guide for the {@code common.graph} package ("Graphs Explained") for + * additional documentation, including notes for + * implementors + * + * @author Joshua O'Madadhain + * @author Jens Nyman + * @param Node parameter type + * @since 23.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface PredecessorsFunction { + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s incoming edges against the direction (if any) of the edge. + * + *

Some algorithms that operate on a {@code PredecessorsFunction} may produce undesired results + * if the returned {@link Iterable} contains duplicate elements. Implementations of such + * algorithms should document their behavior in the presence of duplicates. + * + *

The elements of the returned {@code Iterable} must each be: + * + *

    + *
  • Non-null + *
  • Usable as {@code Map} keys (see the Guava User Guide's section on + * graph elements for details) + *
+ * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + Iterable predecessors(N node); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableGraph.java new file mode 100644 index 0000000000000..b0526f195d8ca --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableGraph.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.graph.GraphConstants.Presence; + +/** + * Standard implementation of {@link MutableGraph} that supports both directed and undirected + * graphs. Instances of this class should be constructed with {@link GraphBuilder}. + * + *

Time complexities for mutation methods are all O(1) except for {@code removeNode(N node)}, + * which is in O(d_node) where d_node is the degree of {@code node}. + * + * @author James Sexton + * @param Node parameter type + */ +@ElementTypesAreNonnullByDefault +final class StandardMutableGraph extends ForwardingGraph implements MutableGraph { + private final MutableValueGraph backingValueGraph; + + /** Constructs a {@link MutableGraph} with the properties specified in {@code builder}. */ + StandardMutableGraph(AbstractGraphBuilder builder) { + this.backingValueGraph = new StandardMutableValueGraph<>(builder); + } + + @Override + BaseGraph delegate() { + return backingValueGraph; + } + + @Override + public boolean addNode(N node) { + return backingValueGraph.addNode(node); + } + + @Override + public boolean putEdge(N nodeU, N nodeV) { + return backingValueGraph.putEdgeValue(nodeU, nodeV, Presence.EDGE_EXISTS) == null; + } + + @Override + public boolean putEdge(EndpointPair endpoints) { + validateEndpoints(endpoints); + return putEdge(endpoints.nodeU(), endpoints.nodeV()); + } + + @Override + public boolean removeNode(N node) { + return backingValueGraph.removeNode(node); + } + + @Override + public boolean removeEdge(N nodeU, N nodeV) { + return backingValueGraph.removeEdge(nodeU, nodeV) != null; + } + + @Override + public boolean removeEdge(EndpointPair endpoints) { + validateEndpoints(endpoints); + return removeEdge(endpoints.nodeU(), endpoints.nodeV()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableNetwork.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableNetwork.java new file mode 100644 index 0000000000000..95cc7c2e6d967 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableNetwork.java @@ -0,0 +1,172 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.ImmutableList; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.GraphConstants.PARALLEL_EDGES_NOT_ALLOWED; +import static org.apache.iotdb.google.common.graph.GraphConstants.REUSING_EDGE; +import static org.apache.iotdb.google.common.graph.GraphConstants.SELF_LOOPS_NOT_ALLOWED; + +/** + * Standard implementation of {@link MutableNetwork} that supports both directed and undirected + * graphs. Instances of this class should be constructed with {@link NetworkBuilder}. + * + *

Time complexities for mutation methods are all O(1) except for {@code removeNode(N node)}, + * which is in O(d_node) where d_node is the degree of {@code node}. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @author Omar Darwish + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +final class StandardMutableNetwork extends StandardNetwork + implements MutableNetwork { + + /** Constructs a mutable graph with the properties specified in {@code builder}. */ + StandardMutableNetwork(NetworkBuilder builder) { + super(builder); + } + + @Override + public boolean addNode(N node) { + checkNotNull(node, "node"); + + if (containsNode(node)) { + return false; + } + + addNodeInternal(node); + return true; + } + + /** + * Adds {@code node} to the graph and returns the associated {@link NetworkConnections}. + * + * @throws IllegalStateException if {@code node} is already present + */ + private NetworkConnections addNodeInternal(N node) { + NetworkConnections connections = newConnections(); + checkState(nodeConnections.put(node, connections) == null); + return connections; + } + + @Override + public boolean addEdge(N nodeU, N nodeV, E edge) { + checkNotNull(nodeU, "nodeU"); + checkNotNull(nodeV, "nodeV"); + checkNotNull(edge, "edge"); + + if (containsEdge(edge)) { + EndpointPair existingIncidentNodes = incidentNodes(edge); + EndpointPair newIncidentNodes = EndpointPair.of(this, nodeU, nodeV); + checkArgument( + existingIncidentNodes.equals(newIncidentNodes), + REUSING_EDGE, + edge, + existingIncidentNodes, + newIncidentNodes); + return false; + } + NetworkConnections connectionsU = nodeConnections.get(nodeU); + if (!allowsParallelEdges()) { + checkArgument( + !(connectionsU != null && connectionsU.successors().contains(nodeV)), + PARALLEL_EDGES_NOT_ALLOWED, + nodeU, + nodeV); + } + boolean isSelfLoop = nodeU.equals(nodeV); + if (!allowsSelfLoops()) { + checkArgument(!isSelfLoop, SELF_LOOPS_NOT_ALLOWED, nodeU); + } + + if (connectionsU == null) { + connectionsU = addNodeInternal(nodeU); + } + connectionsU.addOutEdge(edge, nodeV); + NetworkConnections connectionsV = nodeConnections.get(nodeV); + if (connectionsV == null) { + connectionsV = addNodeInternal(nodeV); + } + connectionsV.addInEdge(edge, nodeU, isSelfLoop); + edgeToReferenceNode.put(edge, nodeU); + return true; + } + + @Override + public boolean addEdge(EndpointPair endpoints, E edge) { + validateEndpoints(endpoints); + return addEdge(endpoints.nodeU(), endpoints.nodeV(), edge); + } + + @Override + public boolean removeNode(N node) { + checkNotNull(node, "node"); + + NetworkConnections connections = nodeConnections.get(node); + if (connections == null) { + return false; + } + + // Since views are returned, we need to copy the edges that will be removed. + // Thus we avoid modifying the underlying view while iterating over it. + for (E edge : ImmutableList.copyOf(connections.incidentEdges())) { + removeEdge(edge); + } + nodeConnections.remove(node); + return true; + } + + @Override + public boolean removeEdge(E edge) { + checkNotNull(edge, "edge"); + + N nodeU = edgeToReferenceNode.get(edge); + if (nodeU == null) { + return false; + } + + // requireNonNull is safe because of the edgeToReferenceNode check above. + NetworkConnections connectionsU = requireNonNull(nodeConnections.get(nodeU)); + N nodeV = connectionsU.adjacentNode(edge); + NetworkConnections connectionsV = requireNonNull(nodeConnections.get(nodeV)); + connectionsU.removeOutEdge(edge); + connectionsV.removeInEdge(edge, allowsSelfLoops() && nodeU.equals(nodeV)); + edgeToReferenceNode.remove(edge); + return true; + } + + private NetworkConnections newConnections() { + return isDirected() + ? allowsParallelEdges() + ? DirectedMultiNetworkConnections.of() + : DirectedNetworkConnections.of() + : allowsParallelEdges() + ? UndirectedMultiNetworkConnections.of() + : UndirectedNetworkConnections.of(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableValueGraph.java new file mode 100644 index 0000000000000..cfb8e802be1d9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardMutableValueGraph.java @@ -0,0 +1,181 @@ +/* + * 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.iotdb.google.common.graph; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.GraphConstants.SELF_LOOPS_NOT_ALLOWED; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; +import static org.apache.iotdb.google.common.graph.Graphs.checkPositive; + +/** + * Standard implementation of {@link MutableValueGraph} that supports both directed and undirected + * graphs. Instances of this class should be constructed with {@link ValueGraphBuilder}. + * + *

Time complexities for mutation methods are all O(1) except for {@code removeNode(N node)}, + * which is in O(d_node) where d_node is the degree of {@code node}. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @author Omar Darwish + * @param Node parameter type + * @param Value parameter type + */ +@ElementTypesAreNonnullByDefault +final class StandardMutableValueGraph extends StandardValueGraph + implements MutableValueGraph { + + private final ElementOrder incidentEdgeOrder; + + /** Constructs a mutable graph with the properties specified in {@code builder}. */ + StandardMutableValueGraph(AbstractGraphBuilder builder) { + super(builder); + incidentEdgeOrder = builder.incidentEdgeOrder.cast(); + } + + @Override + public ElementOrder incidentEdgeOrder() { + return incidentEdgeOrder; + } + + @Override + public boolean addNode(N node) { + checkNotNull(node, "node"); + + if (containsNode(node)) { + return false; + } + + addNodeInternal(node); + return true; + } + + /** + * Adds {@code node} to the graph and returns the associated {@link GraphConnections}. + * + * @throws IllegalStateException if {@code node} is already present + */ + private GraphConnections addNodeInternal(N node) { + GraphConnections connections = newConnections(); + checkState(nodeConnections.put(node, connections) == null); + return connections; + } + + @Override + public V putEdgeValue(N nodeU, N nodeV, V value) { + checkNotNull(nodeU, "nodeU"); + checkNotNull(nodeV, "nodeV"); + checkNotNull(value, "value"); + + if (!allowsSelfLoops()) { + checkArgument(!nodeU.equals(nodeV), SELF_LOOPS_NOT_ALLOWED, nodeU); + } + + GraphConnections connectionsU = nodeConnections.get(nodeU); + if (connectionsU == null) { + connectionsU = addNodeInternal(nodeU); + } + V previousValue = connectionsU.addSuccessor(nodeV, value); + GraphConnections connectionsV = nodeConnections.get(nodeV); + if (connectionsV == null) { + connectionsV = addNodeInternal(nodeV); + } + connectionsV.addPredecessor(nodeU, value); + if (previousValue == null) { + checkPositive(++edgeCount); + } + return previousValue; + } + + @Override + public V putEdgeValue(EndpointPair endpoints, V value) { + validateEndpoints(endpoints); + return putEdgeValue(endpoints.nodeU(), endpoints.nodeV(), value); + } + + @Override + public boolean removeNode(N node) { + checkNotNull(node, "node"); + + GraphConnections connections = nodeConnections.get(node); + if (connections == null) { + return false; + } + + if (allowsSelfLoops()) { + // Remove self-loop (if any) first, so we don't get CME while removing incident edges. + if (connections.removeSuccessor(node) != null) { + connections.removePredecessor(node); + --edgeCount; + } + } + + for (N successor : connections.successors()) { + // requireNonNull is safe because the node is a successor. + requireNonNull(nodeConnections.getWithoutCaching(successor)).removePredecessor(node); + --edgeCount; + } + if (isDirected()) { // In undirected graphs, the successor and predecessor sets are equal. + for (N predecessor : connections.predecessors()) { + // requireNonNull is safe because the node is a predecessor. + checkState( + requireNonNull(nodeConnections.getWithoutCaching(predecessor)).removeSuccessor(node) + != null); + --edgeCount; + } + } + nodeConnections.remove(node); + checkNonNegative(edgeCount); + return true; + } + + @Override + public V removeEdge(N nodeU, N nodeV) { + checkNotNull(nodeU, "nodeU"); + checkNotNull(nodeV, "nodeV"); + + GraphConnections connectionsU = nodeConnections.get(nodeU); + GraphConnections connectionsV = nodeConnections.get(nodeV); + if (connectionsU == null || connectionsV == null) { + return null; + } + + V previousValue = connectionsU.removeSuccessor(nodeV); + if (previousValue != null) { + connectionsV.removePredecessor(nodeU); + checkNonNegative(--edgeCount); + } + return previousValue; + } + + @Override + public V removeEdge(EndpointPair endpoints) { + validateEndpoints(endpoints); + return removeEdge(endpoints.nodeU(), endpoints.nodeV()); + } + + private GraphConnections newConnections() { + return isDirected() + ? DirectedGraphConnections.of(incidentEdgeOrder) + : UndirectedGraphConnections.of(incidentEdgeOrder); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardNetwork.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardNetwork.java new file mode 100644 index 0000000000000..b95e87765783f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardNetwork.java @@ -0,0 +1,208 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.ImmutableSet; + +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.GraphConstants.DEFAULT_EDGE_COUNT; +import static org.apache.iotdb.google.common.graph.GraphConstants.DEFAULT_NODE_COUNT; +import static org.apache.iotdb.google.common.graph.GraphConstants.EDGE_NOT_IN_GRAPH; +import static org.apache.iotdb.google.common.graph.GraphConstants.NODE_NOT_IN_GRAPH; + +/** + * Standard implementation of {@link Network} that supports the options supplied by {@link + * NetworkBuilder}. + * + *

This class maintains a map of nodes to {@link NetworkConnections}. This class also maintains a + * map of edges to reference nodes. The reference node is defined to be the edge's source node on + * directed graphs, and an arbitrary endpoint of the edge on undirected graphs. + * + *

Collection-returning accessors return unmodifiable views: the view returned will reflect + * changes to the graph (if the graph is mutable) but may not be modified by the user. + * + *

The time complexity of all collection-returning accessors is O(1), since views are returned. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @author Omar Darwish + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +class StandardNetwork extends AbstractNetwork { + private final boolean isDirected; + private final boolean allowsParallelEdges; + private final boolean allowsSelfLoops; + private final ElementOrder nodeOrder; + private final ElementOrder edgeOrder; + + final MapIteratorCache> nodeConnections; + + // We could make this a Map>. It would make incidentNodes(edge) slightly + // faster, but also make Networks consume 5 to 20+% (increasing with average degree) more memory. + final MapIteratorCache edgeToReferenceNode; // referenceNode == source if directed + + /** Constructs a graph with the properties specified in {@code builder}. */ + StandardNetwork(NetworkBuilder builder) { + this( + builder, + builder.nodeOrder.>createMap( + builder.expectedNodeCount.or(DEFAULT_NODE_COUNT)), + builder.edgeOrder.createMap(builder.expectedEdgeCount.or(DEFAULT_EDGE_COUNT))); + } + + /** + * Constructs a graph with the properties specified in {@code builder}, initialized with the given + * node and edge maps. + */ + StandardNetwork( + NetworkBuilder builder, + Map> nodeConnections, + Map edgeToReferenceNode) { + this.isDirected = builder.directed; + this.allowsParallelEdges = builder.allowsParallelEdges; + this.allowsSelfLoops = builder.allowsSelfLoops; + this.nodeOrder = builder.nodeOrder.cast(); + this.edgeOrder = builder.edgeOrder.cast(); + // Prefer the heavier "MapRetrievalCache" for nodes if lookup is expensive. This optimizes + // methods that access the same node(s) repeatedly, such as Graphs.removeEdgesConnecting(). + this.nodeConnections = + (nodeConnections instanceof TreeMap) + ? new MapRetrievalCache>(nodeConnections) + : new MapIteratorCache>(nodeConnections); + this.edgeToReferenceNode = new MapIteratorCache<>(edgeToReferenceNode); + } + + @Override + public Set nodes() { + return nodeConnections.unmodifiableKeySet(); + } + + @Override + public Set edges() { + return edgeToReferenceNode.unmodifiableKeySet(); + } + + @Override + public boolean isDirected() { + return isDirected; + } + + @Override + public boolean allowsParallelEdges() { + return allowsParallelEdges; + } + + @Override + public boolean allowsSelfLoops() { + return allowsSelfLoops; + } + + @Override + public ElementOrder nodeOrder() { + return nodeOrder; + } + + @Override + public ElementOrder edgeOrder() { + return edgeOrder; + } + + @Override + public Set incidentEdges(N node) { + return checkedConnections(node).incidentEdges(); + } + + @Override + public EndpointPair incidentNodes(E edge) { + N nodeU = checkedReferenceNode(edge); + // requireNonNull is safe because checkedReferenceNode made sure the edge is in the network. + N nodeV = requireNonNull(nodeConnections.get(nodeU)).adjacentNode(edge); + return EndpointPair.of(this, nodeU, nodeV); + } + + @Override + public Set adjacentNodes(N node) { + return checkedConnections(node).adjacentNodes(); + } + + @Override + public Set edgesConnecting(N nodeU, N nodeV) { + NetworkConnections connectionsU = checkedConnections(nodeU); + if (!allowsSelfLoops && nodeU == nodeV) { // just an optimization, only check reference equality + return ImmutableSet.of(); + } + checkArgument(containsNode(nodeV), NODE_NOT_IN_GRAPH, nodeV); + return connectionsU.edgesConnecting(nodeV); + } + + @Override + public Set inEdges(N node) { + return checkedConnections(node).inEdges(); + } + + @Override + public Set outEdges(N node) { + return checkedConnections(node).outEdges(); + } + + @Override + public Set predecessors(N node) { + return checkedConnections(node).predecessors(); + } + + @Override + public Set successors(N node) { + return checkedConnections(node).successors(); + } + + final NetworkConnections checkedConnections(N node) { + NetworkConnections connections = nodeConnections.get(node); + if (connections == null) { + checkNotNull(node); + throw new IllegalArgumentException(String.format(NODE_NOT_IN_GRAPH, node)); + } + return connections; + } + + final N checkedReferenceNode(E edge) { + N referenceNode = edgeToReferenceNode.get(edge); + if (referenceNode == null) { + checkNotNull(edge); + throw new IllegalArgumentException(String.format(EDGE_NOT_IN_GRAPH, edge)); + } + return referenceNode; + } + + final boolean containsNode(N node) { + return nodeConnections.containsKey(node); + } + + final boolean containsEdge(E edge) { + return edgeToReferenceNode.containsKey(edge); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardValueGraph.java new file mode 100644 index 0000000000000..b60454bc08c4f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/StandardValueGraph.java @@ -0,0 +1,189 @@ +/* + * 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.iotdb.google.common.graph; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.GraphConstants.DEFAULT_NODE_COUNT; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; + +/** + * Standard implementation of {@link ValueGraph} that supports the options supplied by {@link + * AbstractGraphBuilder}. + * + *

This class maintains a map of nodes to {@link GraphConnections}. + * + *

Collection-returning accessors return unmodifiable views: the view returned will reflect + * changes to the graph (if the graph is mutable) but may not be modified by the user. + * + *

The time complexity of all collection-returning accessors is O(1), since views are returned. + * + * @author James Sexton + * @author Joshua O'Madadhain + * @author Omar Darwish + * @param Node parameter type + * @param Value parameter type + */ +@ElementTypesAreNonnullByDefault +class StandardValueGraph extends AbstractValueGraph { + private final boolean isDirected; + private final boolean allowsSelfLoops; + private final ElementOrder nodeOrder; + + final MapIteratorCache> nodeConnections; + + long edgeCount; // must be updated when edges are added or removed + + /** Constructs a graph with the properties specified in {@code builder}. */ + StandardValueGraph(AbstractGraphBuilder builder) { + this( + builder, + builder.nodeOrder.>createMap( + builder.expectedNodeCount.or(DEFAULT_NODE_COUNT)), + 0L); + } + + /** + * Constructs a graph with the properties specified in {@code builder}, initialized with the given + * node map. + */ + StandardValueGraph( + AbstractGraphBuilder builder, + Map> nodeConnections, + long edgeCount) { + this.isDirected = builder.directed; + this.allowsSelfLoops = builder.allowsSelfLoops; + this.nodeOrder = builder.nodeOrder.cast(); + // Prefer the heavier "MapRetrievalCache" for nodes if lookup is expensive. + this.nodeConnections = + (nodeConnections instanceof TreeMap) + ? new MapRetrievalCache>(nodeConnections) + : new MapIteratorCache>(nodeConnections); + this.edgeCount = checkNonNegative(edgeCount); + } + + @Override + public Set nodes() { + return nodeConnections.unmodifiableKeySet(); + } + + @Override + public boolean isDirected() { + return isDirected; + } + + @Override + public boolean allowsSelfLoops() { + return allowsSelfLoops; + } + + @Override + public ElementOrder nodeOrder() { + return nodeOrder; + } + + @Override + public Set adjacentNodes(N node) { + return checkedConnections(node).adjacentNodes(); + } + + @Override + public Set predecessors(N node) { + return checkedConnections(node).predecessors(); + } + + @Override + public Set successors(N node) { + return checkedConnections(node).successors(); + } + + @Override + public Set> incidentEdges(N node) { + GraphConnections connections = checkedConnections(node); + + return new IncidentEdgeSet(this, node) { + @Override + public Iterator> iterator() { + return connections.incidentEdgeIterator(node); + } + }; + } + + @Override + public boolean hasEdgeConnecting(N nodeU, N nodeV) { + return hasEdgeConnectingInternal(checkNotNull(nodeU), checkNotNull(nodeV)); + } + + @Override + public boolean hasEdgeConnecting(EndpointPair endpoints) { + checkNotNull(endpoints); + return isOrderingCompatible(endpoints) + && hasEdgeConnectingInternal(endpoints.nodeU(), endpoints.nodeV()); + } + + @Override + public V edgeValueOrDefault(N nodeU, N nodeV, V defaultValue) { + return edgeValueOrDefaultInternal(checkNotNull(nodeU), checkNotNull(nodeV), defaultValue); + } + + @Override + public V edgeValueOrDefault(EndpointPair endpoints, V defaultValue) { + validateEndpoints(endpoints); + return edgeValueOrDefaultInternal(endpoints.nodeU(), endpoints.nodeV(), defaultValue); + } + + @Override + protected long edgeCount() { + return edgeCount; + } + + private final GraphConnections checkedConnections(N node) { + GraphConnections connections = nodeConnections.get(node); + if (connections == null) { + checkNotNull(node); + throw new IllegalArgumentException("Node " + node + " is not an element of this graph."); + } + return connections; + } + + final boolean containsNode(N node) { + return nodeConnections.containsKey(node); + } + + private final boolean hasEdgeConnectingInternal(N nodeU, N nodeV) { + GraphConnections connectionsU = nodeConnections.get(nodeU); + return (connectionsU != null) && connectionsU.successors().contains(nodeV); + } + + private final V edgeValueOrDefaultInternal(N nodeU, N nodeV, V defaultValue) { + GraphConnections connectionsU = nodeConnections.get(nodeU); + V value = (connectionsU == null) ? null : connectionsU.value(nodeV); + // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. + if (value == null) { + return defaultValue; + } else { + return value; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/SuccessorsFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/SuccessorsFunction.java new file mode 100644 index 0000000000000..c041040b6144b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/SuccessorsFunction.java @@ -0,0 +1,110 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +/** + * A functional interface for graph-structured data. + * + *

This interface is meant to be used as the type of a parameter to graph algorithms (such as + * breadth first traversal) that only need a way of accessing the successors of a node in a graph. + * + *

Usage

+ * + * Given an algorithm, for example: + * + *
{@code
+ * public  someGraphAlgorithm(N startNode, SuccessorsFunction successorsFunction);
+ * }
+ * + * you will invoke it depending on the graph representation you're using. + * + *

If you have an instance of one of the primary {@code common.graph} types ({@link Graph}, + * {@link ValueGraph}, and {@link Network}): + * + *

{@code
+ * someGraphAlgorithm(startNode, graph);
+ * }
+ * + * This works because those types each implement {@code SuccessorsFunction}. It will also work with + * any other implementation of this interface. + * + *

If you have your own graph implementation based around a custom node type {@code MyNode}, + * which has a method {@code getChildren()} that retrieves its successors in a graph: + * + *

{@code
+ * someGraphAlgorithm(startNode, MyNode::getChildren);
+ * }
+ * + *

If you have some other mechanism for returning the successors of a node, or one that doesn't + * return an {@code Iterable}, then you can use a lambda to perform a more general + * transformation: + * + *

{@code
+ * someGraphAlgorithm(startNode, node -> ImmutableList.of(node.leftChild(), node.rightChild()));
+ * }
+ * + *

Graph algorithms that need additional capabilities (accessing both predecessors and + * successors, iterating over the edges, etc.) should declare their input to be of a type that + * provides those capabilities, such as {@link Graph}, {@link ValueGraph}, or {@link Network}. + * + *

Additional documentation

+ * + *

See the Guava User Guide for the {@code common.graph} package ("Graphs Explained") for + * additional documentation, including notes for + * implementors + * + * @author Joshua O'Madadhain + * @author Jens Nyman + * @param Node parameter type + * @since 23.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface SuccessorsFunction { + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s outgoing edges in the direction (if any) of the edge. + * + *

This is not the same as "all nodes reachable from {@code node} by following outgoing + * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. + * + *

Some algorithms that operate on a {@code SuccessorsFunction} may produce undesired results + * if the returned {@link Iterable} contains duplicate elements. Implementations of such + * algorithms should document their behavior in the presence of duplicates. + * + *

The elements of the returned {@code Iterable} must each be: + * + *

    + *
  • Non-null + *
  • Usable as {@code Map} keys (see the Guava User Guide's section on + * graph elements for details) + *
+ * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + Iterable successors(N node); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Traverser.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Traverser.java new file mode 100644 index 0000000000000..f8f15bb6a46e5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/Traverser.java @@ -0,0 +1,515 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.collect.AbstractIterator; +import org.apache.iotdb.google.common.collect.ImmutableSet; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An object that can traverse the nodes that are reachable from a specified (set of) start node(s) + * using a specified {@link SuccessorsFunction}. + * + *

There are two entry points for creating a {@code Traverser}: {@link + * #forTree(SuccessorsFunction)} and {@link #forGraph(SuccessorsFunction)}. You should choose one + * based on your answers to the following questions: + * + *

    + *
  1. Is there only one path to any node that's reachable from any start node? (If so, the graph + * to be traversed is a tree or forest even if it is a subgraph of a graph which is neither.) + *
  2. Are the node objects' implementations of {@code equals()}/{@code hashCode()} recursive? + *
+ * + *

If your answers are: + * + *

    + *
  • (1) "no" and (2) "no", use {@link #forGraph(SuccessorsFunction)}. + *
  • (1) "yes" and (2) "yes", use {@link #forTree(SuccessorsFunction)}. + *
  • (1) "yes" and (2) "no", you can use either, but {@code forTree()} will be more efficient. + *
  • (1) "no" and (2) "yes", neither will work, but if you transform your node + * objects into a non-recursive form, you can use {@code forGraph()}. + *
+ * + * @author Jens Nyman + * @param Node parameter type + * @since 23.1 + */ +@Beta +@ElementTypesAreNonnullByDefault +public abstract class Traverser { + private final SuccessorsFunction successorFunction; + + private Traverser(SuccessorsFunction successorFunction) { + this.successorFunction = checkNotNull(successorFunction); + } + + /** + * Creates a new traverser for the given general {@code graph}. + * + *

Traversers created using this method are guaranteed to visit each node reachable from the + * start node(s) at most once. + * + *

If you know that no node in {@code graph} is reachable by more than one path from the start + * node(s), consider using {@link #forTree(SuccessorsFunction)} instead. + * + *

Performance notes + * + *

    + *
  • Traversals require O(n) time (where n is the number of nodes reachable from + * the start node), assuming that the node objects have O(1) {@code equals()} and + * {@code hashCode()} implementations. (See the + * notes on element objects for more information.) + *
  • While traversing, the traverser will use O(n) space (where n is the number + * of nodes that have thus far been visited), plus O(H) space (where H is the + * number of nodes that have been seen but not yet visited, that is, the "horizon"). + *
+ * + * @param graph {@link SuccessorsFunction} representing a general graph that may have cycles. + */ + public static Traverser forGraph(SuccessorsFunction graph) { + return new Traverser(graph) { + @Override + Traversal newTraversal() { + return Traversal.inGraph(graph); + } + }; + } + + /** + * Creates a new traverser for a directed acyclic graph that has at most one path from the start + * node(s) to any node reachable from the start node(s), and has no paths from any start node to + * any other start node, such as a tree or forest. + * + *

{@code forTree()} is especially useful (versus {@code forGraph()}) in cases where the data + * structure being traversed is, in addition to being a tree/forest, also defined recursively. + * This is because the {@code forTree()}-based implementations don't keep track of visited nodes, + * and therefore don't need to call `equals()` or `hashCode()` on the node objects; this saves + * both time and space versus traversing the same graph using {@code forGraph()}. + * + *

Providing a graph to be traversed for which there is more than one path from the start + * node(s) to any node may lead to: + * + *

    + *
  • Traversal not terminating (if the graph has cycles) + *
  • Nodes being visited multiple times (if multiple paths exist from any start node to any + * node reachable from any start node) + *
+ * + *

Performance notes + * + *

    + *
  • Traversals require O(n) time (where n is the number of nodes reachable from + * the start node). + *
  • While traversing, the traverser will use O(H) space (where H is the number + * of nodes that have been seen but not yet visited, that is, the "horizon"). + *
+ * + *

Examples (all edges are directed facing downwards) + * + *

The graph below would be valid input with start nodes of {@code a, f, c}. However, if {@code + * b} were also a start node, then there would be multiple paths to reach {@code e} and + * {@code h}. + * + *

{@code
+   *    a     b      c
+   *   / \   / \     |
+   *  /   \ /   \    |
+   * d     e     f   g
+   *       |
+   *       |
+   *       h
+   * }
+ * + *

. + * + *

The graph below would be a valid input with start nodes of {@code a, f}. However, if {@code + * b} were a start node, there would be multiple paths to {@code f}. + * + *

{@code
+   *    a     b
+   *   / \   / \
+   *  /   \ /   \
+   * c     d     e
+   *        \   /
+   *         \ /
+   *          f
+   * }
+ * + *

Note on binary trees + * + *

This method can be used to traverse over a binary tree. Given methods {@code + * leftChild(node)} and {@code rightChild(node)}, this method can be called as + * + *

{@code
+   * Traverser.forTree(node -> ImmutableList.of(leftChild(node), rightChild(node)));
+   * }
+ * + * @param tree {@link SuccessorsFunction} representing a directed acyclic graph that has at most + * one path between any two nodes + */ + public static Traverser forTree(SuccessorsFunction tree) { + if (tree instanceof BaseGraph) { + checkArgument(((BaseGraph) tree).isDirected(), "Undirected graphs can never be trees."); + } + if (tree instanceof Network) { + checkArgument(((Network) tree).isDirected(), "Undirected networks can never be trees."); + } + return new Traverser(tree) { + @Override + Traversal newTraversal() { + return Traversal.inTree(tree); + } + }; + } + + /** + * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in + * the order of a breadth-first traversal. That is, all the nodes of depth 0 are returned, then + * depth 1, then 2, and so on. + * + *

Example: The following graph with {@code startNode} {@code a} would return nodes in + * the order {@code abcdef} (assuming successors are returned in alphabetical order). + * + *

{@code
+   * b ---- a ---- d
+   * |      |
+   * |      |
+   * e ---- c ---- f
+   * }
+ * + *

The behavior of this method is undefined if the nodes, or the topology of the graph, change + * while iteration is in progress. + * + *

The returned {@code Iterable} can be iterated over multiple times. Every iterator will + * compute its next element on the fly. It is thus possible to limit the traversal to a certain + * number of nodes as follows: + * + *

{@code
+   * Iterables.limit(Traverser.forGraph(graph).breadthFirst(node), maxNumberOfNodes);
+   * }
+ * + *

See Wikipedia for more + * info. + * + * @throws IllegalArgumentException if {@code startNode} is not an element of the graph + */ + public final Iterable breadthFirst(N startNode) { + return breadthFirst(ImmutableSet.of(startNode)); + } + + /** + * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code + * startNodes}, in the order of a breadth-first traversal. This is equivalent to a breadth-first + * traversal of a graph with an additional root node whose successors are the listed {@code + * startNodes}. + * + * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph + * @see #breadthFirst(Object) + * @since 24.1 + */ + public final Iterable breadthFirst(Iterable startNodes) { + ImmutableSet validated = validate(startNodes); + return new Iterable() { + @Override + public Iterator iterator() { + return newTraversal().breadthFirst(validated.iterator()); + } + }; + } + + /** + * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in + * the order of a depth-first pre-order traversal. "Pre-order" implies that nodes appear in the + * {@code Iterable} in the order in which they are first visited. + * + *

Example: The following graph with {@code startNode} {@code a} would return nodes in + * the order {@code abecfd} (assuming successors are returned in alphabetical order). + * + *

{@code
+   * b ---- a ---- d
+   * |      |
+   * |      |
+   * e ---- c ---- f
+   * }
+ * + *

The behavior of this method is undefined if the nodes, or the topology of the graph, change + * while iteration is in progress. + * + *

The returned {@code Iterable} can be iterated over multiple times. Every iterator will + * compute its next element on the fly. It is thus possible to limit the traversal to a certain + * number of nodes as follows: + * + *

{@code
+   * Iterables.limit(
+   *     Traverser.forGraph(graph).depthFirstPreOrder(node), maxNumberOfNodes);
+   * }
+ * + *

See Wikipedia for more info. + * + * @throws IllegalArgumentException if {@code startNode} is not an element of the graph + */ + public final Iterable depthFirstPreOrder(N startNode) { + return depthFirstPreOrder(ImmutableSet.of(startNode)); + } + + /** + * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code + * startNodes}, in the order of a depth-first pre-order traversal. This is equivalent to a + * depth-first pre-order traversal of a graph with an additional root node whose successors are + * the listed {@code startNodes}. + * + * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph + * @see #depthFirstPreOrder(Object) + * @since 24.1 + */ + public final Iterable depthFirstPreOrder(Iterable startNodes) { + ImmutableSet validated = validate(startNodes); + return new Iterable() { + @Override + public Iterator iterator() { + return newTraversal().preOrder(validated.iterator()); + } + }; + } + + /** + * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in + * the order of a depth-first post-order traversal. "Post-order" implies that nodes appear in the + * {@code Iterable} in the order in which they are visited for the last time. + * + *

Example: The following graph with {@code startNode} {@code a} would return nodes in + * the order {@code fcebda} (assuming successors are returned in alphabetical order). + * + *

{@code
+   * b ---- a ---- d
+   * |      |
+   * |      |
+   * e ---- c ---- f
+   * }
+ * + *

The behavior of this method is undefined if the nodes, or the topology of the graph, change + * while iteration is in progress. + * + *

The returned {@code Iterable} can be iterated over multiple times. Every iterator will + * compute its next element on the fly. It is thus possible to limit the traversal to a certain + * number of nodes as follows: + * + *

{@code
+   * Iterables.limit(
+   *     Traverser.forGraph(graph).depthFirstPostOrder(node), maxNumberOfNodes);
+   * }
+ * + *

See Wikipedia for more info. + * + * @throws IllegalArgumentException if {@code startNode} is not an element of the graph + */ + public final Iterable depthFirstPostOrder(N startNode) { + return depthFirstPostOrder(ImmutableSet.of(startNode)); + } + + /** + * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code + * startNodes}, in the order of a depth-first post-order traversal. This is equivalent to a + * depth-first post-order traversal of a graph with an additional root node whose successors are + * the listed {@code startNodes}. + * + * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph + * @see #depthFirstPostOrder(Object) + * @since 24.1 + */ + public final Iterable depthFirstPostOrder(Iterable startNodes) { + ImmutableSet validated = validate(startNodes); + return new Iterable() { + @Override + public Iterator iterator() { + return newTraversal().postOrder(validated.iterator()); + } + }; + } + + abstract Traversal newTraversal(); + + @SuppressWarnings("CheckReturnValue") + private ImmutableSet validate(Iterable startNodes) { + ImmutableSet copy = ImmutableSet.copyOf(startNodes); + for (N node : copy) { + successorFunction.successors(node); // Will throw if node doesn't exist + } + return copy; + } + + /** + * Abstracts away the difference between traversing a graph vs. a tree. For a tree, we just take + * the next element from the next non-empty iterator; for graph, we need to loop through the next + * non-empty iterator to find first unvisited node. + */ + private abstract static class Traversal { + final SuccessorsFunction successorFunction; + + Traversal(SuccessorsFunction successorFunction) { + this.successorFunction = successorFunction; + } + + static Traversal inGraph(SuccessorsFunction graph) { + Set visited = new HashSet<>(); + return new Traversal(graph) { + @Override + N visitNext(Deque> horizon) { + Iterator top = horizon.getFirst(); + while (top.hasNext()) { + N element = top.next(); + // requireNonNull is safe because horizon contains only graph nodes. + /* + * TODO(cpovirk): Replace these two statements with one (`N element = + * requireNonNull(top.next())`) once our checker supports it. + * + * (The problem is likely + * https://github.com/jspecify/jspecify-reference-checker/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecAnnotatedTypeFactory.java#L896) + */ + requireNonNull(element); + if (visited.add(element)) { + return element; + } + } + horizon.removeFirst(); + return null; + } + }; + } + + static Traversal inTree(SuccessorsFunction tree) { + return new Traversal(tree) { + @Override + N visitNext(Deque> horizon) { + Iterator top = horizon.getFirst(); + if (top.hasNext()) { + return checkNotNull(top.next()); + } + horizon.removeFirst(); + return null; + } + }; + } + + final Iterator breadthFirst(Iterator startNodes) { + return topDown(startNodes, InsertionOrder.BACK); + } + + final Iterator preOrder(Iterator startNodes) { + return topDown(startNodes, InsertionOrder.FRONT); + } + + /** + * In top-down traversal, an ancestor node is always traversed before any of its descendant + * nodes. The traversal order among descendant nodes (particularly aunts and nieces) are + * determined by the {@code InsertionOrder} parameter: nieces are placed at the FRONT before + * aunts for pre-order; while in BFS they are placed at the BACK after aunts. + */ + private Iterator topDown(Iterator startNodes, InsertionOrder order) { + Deque> horizon = new ArrayDeque<>(); + horizon.add(startNodes); + return new AbstractIterator() { + @Override + protected N computeNext() { + do { + N next = visitNext(horizon); + if (next != null) { + Iterator successors = successorFunction.successors(next).iterator(); + if (successors.hasNext()) { + // BFS: horizon.addLast(successors) + // Pre-order: horizon.addFirst(successors) + order.insertInto(horizon, successors); + } + return next; + } + } while (!horizon.isEmpty()); + return endOfData(); + } + }; + } + + final Iterator postOrder(Iterator startNodes) { + Deque ancestorStack = new ArrayDeque<>(); + Deque> horizon = new ArrayDeque<>(); + horizon.add(startNodes); + return new AbstractIterator() { + @Override + protected N computeNext() { + for (N next = visitNext(horizon); next != null; next = visitNext(horizon)) { + Iterator successors = successorFunction.successors(next).iterator(); + if (!successors.hasNext()) { + return next; + } + horizon.addFirst(successors); + ancestorStack.push(next); + } + // TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker. + if (!ancestorStack.isEmpty()) { + return ancestorStack.pop(); + } + return endOfData(); + } + }; + } + + /** + * Visits the next node from the top iterator of {@code horizon} and returns the visited node. + * Null is returned to indicate reaching the end of the top iterator. + * + *

For example, if horizon is {@code [[a, b], [c, d], [e]]}, {@code visitNext()} will return + * {@code [a, b, null, c, d, null, e, null]} sequentially, encoding the topological structure. + * (Note, however, that the callers of {@code visitNext()} often insert additional iterators + * into {@code horizon} between calls to {@code visitNext()}. This causes them to receive + * additional values interleaved with those shown above.) + */ + abstract N visitNext(Deque> horizon); + } + + /** Poor man's method reference for {@code Deque::addFirst} and {@code Deque::addLast}. */ + private enum InsertionOrder { + FRONT { + @Override + void insertInto(Deque deque, T value) { + deque.addFirst(value); + } + }, + BACK { + @Override + void insertInto(Deque deque, T value) { + deque.addLast(value); + } + }; + + abstract void insertInto(Deque deque, T value); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedGraphConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedGraphConnections.java new file mode 100644 index 0000000000000..a6ce4df10dd86 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedGraphConnections.java @@ -0,0 +1,116 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Iterators; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_CAPACITY; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_LOAD_FACTOR; + +/** + * An implementation of {@link GraphConnections} for undirected graphs. + * + * @author James Sexton + * @param Node parameter type + * @param Value parameter type + */ +@ElementTypesAreNonnullByDefault +final class UndirectedGraphConnections implements GraphConnections { + private final Map adjacentNodeValues; + + private UndirectedGraphConnections(Map adjacentNodeValues) { + this.adjacentNodeValues = checkNotNull(adjacentNodeValues); + } + + static UndirectedGraphConnections of(ElementOrder incidentEdgeOrder) { + switch (incidentEdgeOrder.type()) { + case UNORDERED: + return new UndirectedGraphConnections<>( + new HashMap(INNER_CAPACITY, INNER_LOAD_FACTOR)); + case STABLE: + return new UndirectedGraphConnections<>( + new LinkedHashMap(INNER_CAPACITY, INNER_LOAD_FACTOR)); + default: + throw new AssertionError(incidentEdgeOrder.type()); + } + } + + static UndirectedGraphConnections ofImmutable(Map adjacentNodeValues) { + return new UndirectedGraphConnections<>(ImmutableMap.copyOf(adjacentNodeValues)); + } + + @Override + public Set adjacentNodes() { + return Collections.unmodifiableSet(adjacentNodeValues.keySet()); + } + + @Override + public Set predecessors() { + return adjacentNodes(); + } + + @Override + public Set successors() { + return adjacentNodes(); + } + + @Override + public Iterator> incidentEdgeIterator(N thisNode) { + return Iterators.transform( + adjacentNodeValues.keySet().iterator(), + (N incidentNode) -> EndpointPair.unordered(thisNode, incidentNode)); + } + + @Override + public V value(N node) { + return adjacentNodeValues.get(node); + } + + @Override + public void removePredecessor(N node) { + @SuppressWarnings("unused") + V unused = removeSuccessor(node); + } + + @Override + public V removeSuccessor(N node) { + return adjacentNodeValues.remove(node); + } + + @Override + public void addPredecessor(N node, V value) { + @SuppressWarnings("unused") + V unused = addSuccessor(node, value); + } + + @Override + public V addSuccessor(N node, V value) { + return adjacentNodeValues.put(node, value); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedMultiNetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedMultiNetworkConnections.java new file mode 100644 index 0000000000000..5de0a6f3ced8f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedMultiNetworkConnections.java @@ -0,0 +1,124 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.HashMultiset; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Multiset; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_CAPACITY; +import static org.apache.iotdb.google.common.graph.GraphConstants.INNER_LOAD_FACTOR; + +/** + * An implementation of {@link NetworkConnections} for undirected networks with parallel edges. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +final class UndirectedMultiNetworkConnections + extends AbstractUndirectedNetworkConnections { + + private UndirectedMultiNetworkConnections(Map incidentEdges) { + super(incidentEdges); + } + + static UndirectedMultiNetworkConnections of() { + return new UndirectedMultiNetworkConnections<>( + new HashMap(INNER_CAPACITY, INNER_LOAD_FACTOR)); + } + + static UndirectedMultiNetworkConnections ofImmutable(Map incidentEdges) { + return new UndirectedMultiNetworkConnections<>(ImmutableMap.copyOf(incidentEdges)); + } + + private transient Reference> adjacentNodesReference; + + @Override + public Set adjacentNodes() { + return Collections.unmodifiableSet(adjacentNodesMultiset().elementSet()); + } + + private Multiset adjacentNodesMultiset() { + Multiset adjacentNodes = getReference(adjacentNodesReference); + if (adjacentNodes == null) { + adjacentNodes = HashMultiset.create(incidentEdgeMap.values()); + adjacentNodesReference = new SoftReference<>(adjacentNodes); + } + return adjacentNodes; + } + + @Override + public Set edgesConnecting(N node) { + return new MultiEdgesConnecting(incidentEdgeMap, node) { + @Override + public int size() { + return adjacentNodesMultiset().count(node); + } + }; + } + + @Override + public N removeInEdge(E edge, boolean isSelfLoop) { + if (!isSelfLoop) { + return removeOutEdge(edge); + } + return null; + } + + @Override + public N removeOutEdge(E edge) { + N node = super.removeOutEdge(edge); + Multiset adjacentNodes = getReference(adjacentNodesReference); + if (adjacentNodes != null) { + checkState(adjacentNodes.remove(node)); + } + return node; + } + + @Override + public void addInEdge(E edge, N node, boolean isSelfLoop) { + if (!isSelfLoop) { + addOutEdge(edge, node); + } + } + + @Override + public void addOutEdge(E edge, N node) { + super.addOutEdge(edge, node); + Multiset adjacentNodes = getReference(adjacentNodesReference); + if (adjacentNodes != null) { + checkState(adjacentNodes.add(node)); + } + } + + private static T getReference(Reference reference) { + return (reference == null) ? null : reference.get(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedNetworkConnections.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedNetworkConnections.java new file mode 100644 index 0000000000000..6b4c07143ffda --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/UndirectedNetworkConnections.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.collect.BiMap; +import org.apache.iotdb.google.common.collect.HashBiMap; +import org.apache.iotdb.google.common.collect.ImmutableBiMap; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.graph.GraphConstants.EXPECTED_DEGREE; + +/** + * An implementation of {@link NetworkConnections} for undirected networks. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +@ElementTypesAreNonnullByDefault +final class UndirectedNetworkConnections extends AbstractUndirectedNetworkConnections { + + UndirectedNetworkConnections(Map incidentEdgeMap) { + super(incidentEdgeMap); + } + + static UndirectedNetworkConnections of() { + return new UndirectedNetworkConnections<>(HashBiMap.create(EXPECTED_DEGREE)); + } + + static UndirectedNetworkConnections ofImmutable(Map incidentEdges) { + return new UndirectedNetworkConnections<>(ImmutableBiMap.copyOf(incidentEdges)); + } + + @Override + public Set adjacentNodes() { + return Collections.unmodifiableSet(((BiMap) incidentEdgeMap).values()); + } + + @Override + public Set edgesConnecting(N node) { + return new EdgesConnecting<>(((BiMap) incidentEdgeMap).inverse(), node); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ValueGraph.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ValueGraph.java new file mode 100644 index 0000000000000..7a76a1169a99d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ValueGraph.java @@ -0,0 +1,366 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +/** + * An interface for graph-structured data, + * whose edges have associated non-unique values. + * + *

A graph is composed of a set of nodes and a set of edges connecting pairs of nodes. + * + *

There are three primary interfaces provided to represent graphs. In order of increasing + * complexity they are: {@link Graph}, {@link ValueGraph}, and {@link Network}. You should generally + * prefer the simplest interface that satisfies your use case. See the + * "Choosing the right graph type" section of the Guava User Guide for more details. + * + *

Capabilities

+ * + *

{@code ValueGraph} supports the following use cases (definitions of + * terms): + * + *

    + *
  • directed graphs + *
  • undirected graphs + *
  • graphs that do/don't allow self-loops + *
  • graphs whose nodes/edges are insertion-ordered, sorted, or unordered + *
  • graphs whose edges have associated values + *
+ * + *

{@code ValueGraph}, as a subtype of {@code Graph}, explicitly does not support parallel edges, + * and forbids implementations or extensions with parallel edges. If you need parallel edges, use + * {@link Network}. (You can use a positive {@code Integer} edge value as a loose representation of + * edge multiplicity, but the {@code *degree()} and mutation methods will not reflect your + * interpretation of the edge value as its multiplicity.) + * + *

Building a {@code ValueGraph}

+ * + *

The implementation classes that {@code common.graph} provides are not public, by design. To + * create an instance of one of the built-in implementations of {@code ValueGraph}, use the {@link + * ValueGraphBuilder} class: + * + *

{@code
+ * MutableValueGraph graph = ValueGraphBuilder.directed().build();
+ * }
+ * + *

{@link ValueGraphBuilder#build()} returns an instance of {@link MutableValueGraph}, which is a + * subtype of {@code ValueGraph} that provides methods for adding and removing nodes and edges. If + * you do not need to mutate a graph (e.g. if you write a method than runs a read-only algorithm on + * the graph), you should use the non-mutating {@link ValueGraph} interface, or an {@link + * ImmutableValueGraph}. + * + *

You can create an immutable copy of an existing {@code ValueGraph} using {@link + * ImmutableValueGraph#copyOf(ValueGraph)}: + * + *

{@code
+ * ImmutableValueGraph immutableGraph = ImmutableValueGraph.copyOf(graph);
+ * }
+ * + *

Instances of {@link ImmutableValueGraph} do not implement {@link MutableValueGraph} + * (obviously!) and are contractually guaranteed to be unmodifiable and thread-safe. + * + *

The Guava User Guide has more + * information on (and examples of) building graphs. + * + *

Additional documentation

+ * + *

See the Guava User Guide for the {@code common.graph} package ("Graphs Explained") for + * additional documentation, including: + * + *

+ * + * @author James Sexton + * @author Joshua O'Madadhain + * @param Node parameter type + * @param Value parameter type + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface ValueGraph extends BaseGraph { + // + // ValueGraph-level accessors + // + + /** Returns all nodes in this graph, in the order specified by {@link #nodeOrder()}. */ + @Override + Set nodes(); + + /** Returns all edges in this graph. */ + @Override + Set> edges(); + + /** + * Returns a live view of this graph as a {@link Graph}. The resulting {@link Graph} will have an + * edge connecting node A to node B if this {@link ValueGraph} has an edge connecting A to B. + */ + Graph asGraph(); + + // + // ValueGraph properties + // + + /** + * Returns true if the edges in this graph are directed. Directed edges connect a {@link + * EndpointPair#source() source node} to a {@link EndpointPair#target() target node}, while + * undirected edges connect a pair of nodes to each other. + */ + @Override + boolean isDirected(); + + /** + * Returns true if this graph allows self-loops (edges that connect a node to itself). Attempting + * to add a self-loop to a graph that does not allow them will throw an {@link + * IllegalArgumentException}. + */ + @Override + boolean allowsSelfLoops(); + + /** Returns the order of iteration for the elements of {@link #nodes()}. */ + @Override + ElementOrder nodeOrder(); + + /** + * Returns an {@link ElementOrder} that specifies the order of iteration for the elements of + * {@link #edges()}, {@link #adjacentNodes(Object)}, {@link #predecessors(Object)}, {@link + * #successors(Object)} and {@link #incidentEdges(Object)}. + * + * @since 29.0 + */ + @Override + ElementOrder incidentEdgeOrder(); + + // + // Element-level accessors + // + + /** + * Returns the nodes which have an incident edge in common with {@code node} in this graph. + * + *

This is equal to the union of {@link #predecessors(Object)} and {@link #successors(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set adjacentNodes(N node); + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s incoming edges against the direction (if any) of the edge. + * + *

In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set predecessors(N node); + + /** + * Returns all nodes in this graph adjacent to {@code node} which can be reached by traversing + * {@code node}'s outgoing edges in the direction (if any) of the edge. + * + *

In an undirected graph, this is equivalent to {@link #adjacentNodes(Object)}. + * + *

This is not the same as "all nodes reachable from {@code node} by following outgoing + * edges". For that functionality, see {@link Graphs#reachableNodes(Graph, Object)}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + Set successors(N node); + + /** + * Returns the edges in this graph whose endpoints include {@code node}. + * + *

This is equal to the union of incoming and outgoing edges. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + * @since 24.0 + */ + @Override + Set> incidentEdges(N node); + + /** + * Returns the count of {@code node}'s incident edges, counting self-loops twice (equivalently, + * the number of times an edge touches {@code node}). + * + *

For directed graphs, this is equal to {@code inDegree(node) + outDegree(node)}. + * + *

For undirected graphs, this is equal to {@code incidentEdges(node).size()} + (number of + * self-loops incident to {@code node}). + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + int degree(N node); + + /** + * Returns the count of {@code node}'s incoming edges (equal to {@code predecessors(node).size()}) + * in a directed graph. In an undirected graph, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + int inDegree(N node); + + /** + * Returns the count of {@code node}'s outgoing edges (equal to {@code successors(node).size()}) + * in a directed graph. In an undirected graph, returns the {@link #degree(Object)}. + * + *

If the count is greater than {@code Integer.MAX_VALUE}, returns {@code Integer.MAX_VALUE}. + * + * @throws IllegalArgumentException if {@code node} is not an element of this graph + */ + @Override + int outDegree(N node); + + /** + * Returns true if there is an edge that directly connects {@code nodeU} to {@code nodeV}. This is + * equivalent to {@code nodes().contains(nodeU) && successors(nodeU).contains(nodeV)}. + * + *

In an undirected graph, this is equal to {@code hasEdgeConnecting(nodeV, nodeU)}. + * + * @since 23.0 + */ + @Override + boolean hasEdgeConnecting(N nodeU, N nodeV); + + /** + * Returns true if there is an edge that directly connects {@code endpoints} (in the order, if + * any, specified by {@code endpoints}). This is equivalent to {@code + * edges().contains(endpoints)}. + * + *

Unlike the other {@code EndpointPair}-accepting methods, this method does not throw if the + * endpoints are unordered and the graph is directed; it simply returns {@code false}. This is for + * consistency with the behavior of {@link Collection#contains(Object)} (which does not generally + * throw if the object cannot be present in the collection), and the desire to have this method's + * behavior be compatible with {@code edges().contains(endpoints)}. + * + * @since 27.1 + */ + @Override + boolean hasEdgeConnecting(EndpointPair endpoints); + + /** + * Returns the value of the edge that connects {@code nodeU} to {@code nodeV} (in the order, if + * any, specified by {@code endpoints}), if one is present; otherwise, returns {@code + * Optional.empty()}. + * + * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this + * graph + * @since 23.0 (since 20.0 with return type {@code V}) + */ + Optional edgeValue(N nodeU, N nodeV); + + /** + * Returns the value of the edge that connects {@code endpoints} (in the order, if any, specified + * by {@code endpoints}), if one is present; otherwise, returns {@code Optional.empty()}. + * + *

If this graph is directed, the endpoints must be ordered. + * + * @throws IllegalArgumentException if either endpoint is not an element of this graph + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + Optional edgeValue(EndpointPair endpoints); + + /** + * Returns the value of the edge that connects {@code nodeU} to {@code nodeV}, if one is present; + * otherwise, returns {@code defaultValue}. + * + *

In an undirected graph, this is equal to {@code edgeValueOrDefault(nodeV, nodeU, + * defaultValue)}. + * + * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not an element of this + * graph + */ + V edgeValueOrDefault(N nodeU, N nodeV, V defaultValue); + + /** + * Returns the value of the edge that connects {@code endpoints} (in the order, if any, specified + * by {@code endpoints}), if one is present; otherwise, returns {@code defaultValue}. + * + *

If this graph is directed, the endpoints must be ordered. + * + * @throws IllegalArgumentException if either endpoint is not an element of this graph + * @throws IllegalArgumentException if the endpoints are unordered and the graph is directed + * @since 27.1 + */ + V edgeValueOrDefault(EndpointPair endpoints, V defaultValue); + + // + // ValueGraph identity + // + + /** + * Returns {@code true} iff {@code object} is a {@link ValueGraph} that has the same elements and + * the same structural relationships as those in this graph. + * + *

Thus, two value graphs A and B are equal if all of the following are true: + * + *

    + *
  • A and B have equal {@link #isDirected() directedness}. + *
  • A and B have equal {@link #nodes() node sets}. + *
  • A and B have equal {@link #edges() edge sets}. + *
  • The {@link #edgeValue(Object, Object) value} of a given edge is the same in both A and B. + *
+ * + *

Graph properties besides {@link #isDirected() directedness} do not affect equality. + * For example, two graphs may be considered equal even if one allows self-loops and the other + * doesn't. Additionally, the order in which nodes or edges are added to the graph, and the order + * in which they are iterated over, are irrelevant. + * + *

A reference implementation of this is provided by {@link AbstractValueGraph#equals(Object)}. + */ + @Override + boolean equals(Object object); + + /** + * Returns the hash code for this graph. The hash code of a graph is defined as the hash code of a + * map from each of its {@link #edges() edges} to the associated {@link #edgeValue(Object, Object) + * edge value}. + * + *

A reference implementation of this is provided by {@link AbstractValueGraph#hashCode()}. + */ + @Override + int hashCode(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ValueGraphBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ValueGraphBuilder.java new file mode 100644 index 0000000000000..3ed01f527e35c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/ValueGraphBuilder.java @@ -0,0 +1,207 @@ +/* + * 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.iotdb.google.common.graph; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Optional; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.graph.Graphs.checkNonNegative; + +/** + * A builder for constructing instances of {@link MutableValueGraph} or {@link ImmutableValueGraph} + * with user-defined properties. + * + *

A {@code ValueGraph} built by this class has the following default properties: + * + *

    + *
  • does not allow self-loops + *
  • orders {@link ValueGraph#nodes()} in the order in which the elements were added (insertion + * order) + *
+ * + *

{@code ValueGraph}s built by this class also guarantee that each collection-returning accessor + * returns a (live) unmodifiable view; see the external + * documentation for details. + * + *

Examples of use: + * + *

{@code
+ * // Building a mutable value graph
+ * MutableValueGraph graph =
+ *     ValueGraphBuilder.undirected().allowsSelfLoops(true).build();
+ * graph.putEdgeValue("San Francisco", "San Francisco", 0.0);
+ * graph.putEdgeValue("San Jose", "San Jose", 0.0);
+ * graph.putEdgeValue("San Francisco", "San Jose", 48.4);
+ *
+ * // Building an immutable value graph
+ * ImmutableValueGraph immutableGraph =
+ *     ValueGraphBuilder.undirected()
+ *         .allowsSelfLoops(true)
+ *         .immutable()
+ *         .putEdgeValue("San Francisco", "San Francisco", 0.0)
+ *         .putEdgeValue("San Jose", "San Jose", 0.0)
+ *         .putEdgeValue("San Francisco", "San Jose", 48.4)
+ *         .build();
+ * }
+ * + * @author James Sexton + * @author Joshua O'Madadhain + * @param The most general node type this builder will support. This is normally {@code Object} + * unless it is constrained by using a method like {@link #nodeOrder}, or the builder is + * constructed based on an existing {@code ValueGraph} using {@link #from(ValueGraph)}. + * @param The most general value type this builder will support. This is normally {@code Object} + * unless the builder is constructed based on an existing {@code Graph} using {@link + * #from(ValueGraph)}. + * @since 20.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class ValueGraphBuilder extends AbstractGraphBuilder { + + /** Creates a new instance with the specified edge directionality. */ + private ValueGraphBuilder(boolean directed) { + super(directed); + } + + /** Returns a {@link ValueGraphBuilder} for building directed graphs. */ + public static ValueGraphBuilder directed() { + return new ValueGraphBuilder<>(true); + } + + /** Returns a {@link ValueGraphBuilder} for building undirected graphs. */ + public static ValueGraphBuilder undirected() { + return new ValueGraphBuilder<>(false); + } + + /** + * Returns a {@link ValueGraphBuilder} initialized with all properties queryable from {@code + * graph}. + * + *

The "queryable" properties are those that are exposed through the {@link ValueGraph} + * interface, such as {@link ValueGraph#isDirected()}. Other properties, such as {@link + * #expectedNodeCount(int)}, are not set in the new builder. + */ + public static ValueGraphBuilder from(ValueGraph graph) { + return new ValueGraphBuilder(graph.isDirected()) + .allowsSelfLoops(graph.allowsSelfLoops()) + .nodeOrder(graph.nodeOrder()) + .incidentEdgeOrder(graph.incidentEdgeOrder()); + } + + /** + * Returns an {@link ImmutableValueGraph.Builder} with the properties of this {@link + * ValueGraphBuilder}. + * + *

The returned builder can be used for populating an {@link ImmutableValueGraph}. + * + *

Note that the returned builder will always have {@link #incidentEdgeOrder} set to {@link + * ElementOrder#stable()}, regardless of the value that was set in this builder. + * + * @since 28.0 + */ + public ImmutableValueGraph.Builder immutable() { + ValueGraphBuilder castBuilder = cast(); + return new ImmutableValueGraph.Builder<>(castBuilder); + } + + /** + * Specifies whether the graph will allow self-loops (edges that connect a node to itself). + * Attempting to add a self-loop to a graph that does not allow them will throw an {@link + * UnsupportedOperationException}. + * + *

The default value is {@code false}. + */ + public ValueGraphBuilder allowsSelfLoops(boolean allowsSelfLoops) { + this.allowsSelfLoops = allowsSelfLoops; + return this; + } + + /** + * Specifies the expected number of nodes in the graph. + * + * @throws IllegalArgumentException if {@code expectedNodeCount} is negative + */ + public ValueGraphBuilder expectedNodeCount(int expectedNodeCount) { + this.expectedNodeCount = Optional.of(checkNonNegative(expectedNodeCount)); + return this; + } + + /** + * Specifies the order of iteration for the elements of {@link Graph#nodes()}. + * + *

The default value is {@link ElementOrder#insertion() insertion order}. + */ + public ValueGraphBuilder nodeOrder(ElementOrder nodeOrder) { + ValueGraphBuilder newBuilder = cast(); + newBuilder.nodeOrder = checkNotNull(nodeOrder); + return newBuilder; + } + + /** + * Specifies the order of iteration for the elements of {@link ValueGraph#edges()}, {@link + * ValueGraph#adjacentNodes(Object)}, {@link ValueGraph#predecessors(Object)}, {@link + * ValueGraph#successors(Object)} and {@link ValueGraph#incidentEdges(Object)}. + * + *

The default value is {@link ElementOrder#unordered() unordered} for mutable graphs. For + * immutable graphs, this value is ignored; they always have a {@link ElementOrder#stable() + * stable} order. + * + * @throws IllegalArgumentException if {@code incidentEdgeOrder} is not either {@code + * ElementOrder.unordered()} or {@code ElementOrder.stable()}. + * @since 29.0 + */ + public ValueGraphBuilder incidentEdgeOrder( + ElementOrder incidentEdgeOrder) { + checkArgument( + incidentEdgeOrder.type() == ElementOrder.Type.UNORDERED + || incidentEdgeOrder.type() == ElementOrder.Type.STABLE, + "The given elementOrder (%s) is unsupported. incidentEdgeOrder() only supports" + + " ElementOrder.unordered() and ElementOrder.stable().", + incidentEdgeOrder); + ValueGraphBuilder newBuilder = cast(); + newBuilder.incidentEdgeOrder = checkNotNull(incidentEdgeOrder); + return newBuilder; + } + + /** + * Returns an empty {@link MutableValueGraph} with the properties of this {@link + * ValueGraphBuilder}. + */ + public MutableValueGraph build() { + return new StandardMutableValueGraph<>(this); + } + + ValueGraphBuilder copy() { + ValueGraphBuilder newBuilder = new ValueGraphBuilder<>(directed); + newBuilder.allowsSelfLoops = allowsSelfLoops; + newBuilder.nodeOrder = nodeOrder; + newBuilder.expectedNodeCount = expectedNodeCount; + newBuilder.incidentEdgeOrder = incidentEdgeOrder; + return newBuilder; + } + + @SuppressWarnings("unchecked") + private ValueGraphBuilder cast() { + return (ValueGraphBuilder) this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/package-info.java new file mode 100644 index 0000000000000..6b2337799d84e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/graph/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.graph; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractByteHasher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractByteHasher.java new file mode 100644 index 0000000000000..50269b14c2bb3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractByteHasher.java @@ -0,0 +1,129 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.primitives.Chars; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.primitives.Longs; +import org.apache.iotdb.google.common.primitives.Shorts; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Abstract {@link Hasher} that handles converting primitives to bytes using a scratch {@code + * ByteBuffer} and streams all bytes to a sink to compute the hash. + * + * @author Colin Decker + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractByteHasher extends AbstractHasher { + private final ByteBuffer scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); + + /** Updates this hasher with the given byte. */ + protected abstract void update(byte b); + + /** Updates this hasher with the given bytes. */ + protected void update(byte[] b) { + update(b, 0, b.length); + } + + /** Updates this hasher with {@code len} bytes starting at {@code off} in the given buffer. */ + protected void update(byte[] b, int off, int len) { + for (int i = off; i < off + len; i++) { + update(b[i]); + } + } + + /** Updates this hasher with bytes from the given buffer. */ + protected void update(ByteBuffer b) { + if (b.hasArray()) { + update(b.array(), b.arrayOffset() + b.position(), b.remaining()); + Java8Compatibility.position(b, b.limit()); + } else { + for (int remaining = b.remaining(); remaining > 0; remaining--) { + update(b.get()); + } + } + } + + /** Updates the sink with the given number of bytes from the buffer. */ + private Hasher update(int bytes) { + try { + update(scratch.array(), 0, bytes); + } finally { + Java8Compatibility.clear(scratch); + } + return this; + } + + @Override + public Hasher putByte(byte b) { + update(b); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes) { + checkNotNull(bytes); + update(bytes); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + checkPositionIndexes(off, off + len, bytes.length); + update(bytes, off, len); + return this; + } + + @Override + public Hasher putBytes(ByteBuffer bytes) { + update(bytes); + return this; + } + + @Override + public Hasher putShort(short s) { + scratch.putShort(s); + return update(Shorts.BYTES); + } + + @Override + public Hasher putInt(int i) { + scratch.putInt(i); + return update(Ints.BYTES); + } + + @Override + public Hasher putLong(long l) { + scratch.putLong(l); + return update(Longs.BYTES); + } + + @Override + public Hasher putChar(char c) { + scratch.putChar(c); + return update(Chars.BYTES); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractCompositeHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractCompositeHashFunction.java new file mode 100644 index 0000000000000..ccb824d567434 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractCompositeHashFunction.java @@ -0,0 +1,200 @@ +/* + * 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.iotdb.google.common.hash; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An abstract composition of multiple hash functions. {@linkplain #newHasher()} delegates to the + * {@code Hasher} objects of the delegate hash functions, and in the end, they are used by + * {@linkplain #makeHash(Hasher[])} that constructs the final {@code HashCode}. + * + * @author Dimitris Andreou + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractCompositeHashFunction extends AbstractHashFunction { + + @SuppressWarnings("Immutable") // array not modified after creation + final HashFunction[] functions; + + AbstractCompositeHashFunction(HashFunction... functions) { + for (HashFunction function : functions) { + checkNotNull(function); + } + this.functions = functions; + } + + /** + * Constructs a {@code HashCode} from the {@code Hasher} objects of the functions. Each of them + * has consumed the entire input and they are ready to output a {@code HashCode}. The order of the + * hashers are the same order as the functions given to the constructor. + */ + // this could be cleaner if it passed HashCode[], but that would create yet another array... + /* protected */ abstract HashCode makeHash(Hasher[] hashers); + + @Override + public Hasher newHasher() { + Hasher[] hashers = new Hasher[functions.length]; + for (int i = 0; i < hashers.length; i++) { + hashers[i] = functions[i].newHasher(); + } + return fromHashers(hashers); + } + + @Override + public Hasher newHasher(int expectedInputSize) { + checkArgument(expectedInputSize >= 0); + Hasher[] hashers = new Hasher[functions.length]; + for (int i = 0; i < hashers.length; i++) { + hashers[i] = functions[i].newHasher(expectedInputSize); + } + return fromHashers(hashers); + } + + private Hasher fromHashers(Hasher[] hashers) { + return new Hasher() { + @Override + public Hasher putByte(byte b) { + for (Hasher hasher : hashers) { + hasher.putByte(b); + } + return this; + } + + @Override + public Hasher putBytes(byte[] bytes) { + for (Hasher hasher : hashers) { + hasher.putBytes(bytes); + } + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + for (Hasher hasher : hashers) { + hasher.putBytes(bytes, off, len); + } + return this; + } + + @Override + public Hasher putBytes(ByteBuffer bytes) { + int pos = bytes.position(); + for (Hasher hasher : hashers) { + Java8Compatibility.position(bytes, pos); + hasher.putBytes(bytes); + } + return this; + } + + @Override + public Hasher putShort(short s) { + for (Hasher hasher : hashers) { + hasher.putShort(s); + } + return this; + } + + @Override + public Hasher putInt(int i) { + for (Hasher hasher : hashers) { + hasher.putInt(i); + } + return this; + } + + @Override + public Hasher putLong(long l) { + for (Hasher hasher : hashers) { + hasher.putLong(l); + } + return this; + } + + @Override + public Hasher putFloat(float f) { + for (Hasher hasher : hashers) { + hasher.putFloat(f); + } + return this; + } + + @Override + public Hasher putDouble(double d) { + for (Hasher hasher : hashers) { + hasher.putDouble(d); + } + return this; + } + + @Override + public Hasher putBoolean(boolean b) { + for (Hasher hasher : hashers) { + hasher.putBoolean(b); + } + return this; + } + + @Override + public Hasher putChar(char c) { + for (Hasher hasher : hashers) { + hasher.putChar(c); + } + return this; + } + + @Override + public Hasher putUnencodedChars(CharSequence chars) { + for (Hasher hasher : hashers) { + hasher.putUnencodedChars(chars); + } + return this; + } + + @Override + public Hasher putString(CharSequence chars, Charset charset) { + for (Hasher hasher : hashers) { + hasher.putString(chars, charset); + } + return this; + } + + @Override + public Hasher putObject( + @ParametricNullness T instance, Funnel funnel) { + for (Hasher hasher : hashers) { + hasher.putObject(instance, funnel); + } + return this; + } + + @Override + public HashCode hash() { + return makeHash(hashers); + } + }; + } + + private static final long serialVersionUID = 0L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractHashFunction.java new file mode 100644 index 0000000000000..562fe03397c6b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractHashFunction.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.google.common.hash; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Skeleton implementation of {@link HashFunction} in terms of {@link #newHasher()}. + * + *

TODO(lowasser): make public + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractHashFunction implements HashFunction { + @Override + public HashCode hashObject( + @ParametricNullness T instance, Funnel funnel) { + return newHasher().putObject(instance, funnel).hash(); + } + + @Override + public HashCode hashUnencodedChars(CharSequence input) { + int len = input.length(); + return newHasher(len * 2).putUnencodedChars(input).hash(); + } + + @Override + public HashCode hashString(CharSequence input, Charset charset) { + return newHasher().putString(input, charset).hash(); + } + + @Override + public HashCode hashInt(int input) { + return newHasher(4).putInt(input).hash(); + } + + @Override + public HashCode hashLong(long input) { + return newHasher(8).putLong(input).hash(); + } + + @Override + public HashCode hashBytes(byte[] input) { + return hashBytes(input, 0, input.length); + } + + @Override + public HashCode hashBytes(byte[] input, int off, int len) { + checkPositionIndexes(off, off + len, input.length); + return newHasher(len).putBytes(input, off, len).hash(); + } + + @Override + public HashCode hashBytes(ByteBuffer input) { + return newHasher(input.remaining()).putBytes(input).hash(); + } + + @Override + public Hasher newHasher(int expectedInputSize) { + checkArgument( + expectedInputSize >= 0, "expectedInputSize must be >= 0 but was %s", expectedInputSize); + return newHasher(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractHasher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractHasher.java new file mode 100644 index 0000000000000..3c649abc02927 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractHasher.java @@ -0,0 +1,127 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.base.Preconditions; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * An abstract implementation of {@link Hasher}, which only requires subtypes to implement {@link + * #putByte}. Subtypes may provide more efficient implementations, however. + * + * @author Dimitris Andreou + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractHasher implements Hasher { + @Override + public final Hasher putBoolean(boolean b) { + return putByte(b ? (byte) 1 : (byte) 0); + } + + @Override + public final Hasher putDouble(double d) { + return putLong(Double.doubleToRawLongBits(d)); + } + + @Override + public final Hasher putFloat(float f) { + return putInt(Float.floatToRawIntBits(f)); + } + + @Override + public Hasher putUnencodedChars(CharSequence charSequence) { + for (int i = 0, len = charSequence.length(); i < len; i++) { + putChar(charSequence.charAt(i)); + } + return this; + } + + @Override + public Hasher putString(CharSequence charSequence, Charset charset) { + return putBytes(charSequence.toString().getBytes(charset)); + } + + @Override + public Hasher putBytes(byte[] bytes) { + return putBytes(bytes, 0, bytes.length); + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + Preconditions.checkPositionIndexes(off, off + len, bytes.length); + for (int i = 0; i < len; i++) { + putByte(bytes[off + i]); + } + return this; + } + + @Override + public Hasher putBytes(ByteBuffer b) { + if (b.hasArray()) { + putBytes(b.array(), b.arrayOffset() + b.position(), b.remaining()); + Java8Compatibility.position(b, b.limit()); + } else { + for (int remaining = b.remaining(); remaining > 0; remaining--) { + putByte(b.get()); + } + } + return this; + } + + @Override + public Hasher putShort(short s) { + putByte((byte) s); + putByte((byte) (s >>> 8)); + return this; + } + + @Override + public Hasher putInt(int i) { + putByte((byte) i); + putByte((byte) (i >>> 8)); + putByte((byte) (i >>> 16)); + putByte((byte) (i >>> 24)); + return this; + } + + @Override + public Hasher putLong(long l) { + for (int i = 0; i < 64; i += 8) { + putByte((byte) (l >>> i)); + } + return this; + } + + @Override + public Hasher putChar(char c) { + putByte((byte) c); + putByte((byte) (c >>> 8)); + return this; + } + + @Override + public Hasher putObject( + @ParametricNullness T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractNonStreamingHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractNonStreamingHashFunction.java new file mode 100644 index 0000000000000..fa27fde90e5ac --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractNonStreamingHashFunction.java @@ -0,0 +1,138 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * Skeleton implementation of {@link HashFunction}, appropriate for non-streaming algorithms. All + * the hash computation done using {@linkplain #newHasher()} are delegated to the {@linkplain + * #hashBytes(byte[], int, int)} method. + * + * @author Dimitris Andreou + */ +@ElementTypesAreNonnullByDefault +abstract class AbstractNonStreamingHashFunction extends AbstractHashFunction { + @Override + public Hasher newHasher() { + return newHasher(32); + } + + @Override + public Hasher newHasher(int expectedInputSize) { + Preconditions.checkArgument(expectedInputSize >= 0); + return new BufferingHasher(expectedInputSize); + } + + @Override + public HashCode hashInt(int input) { + return hashBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(input).array()); + } + + @Override + public HashCode hashLong(long input) { + return hashBytes(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(input).array()); + } + + @Override + public HashCode hashUnencodedChars(CharSequence input) { + int len = input.length(); + ByteBuffer buffer = ByteBuffer.allocate(len * 2).order(ByteOrder.LITTLE_ENDIAN); + for (int i = 0; i < len; i++) { + buffer.putChar(input.charAt(i)); + } + return hashBytes(buffer.array()); + } + + @Override + public HashCode hashString(CharSequence input, Charset charset) { + return hashBytes(input.toString().getBytes(charset)); + } + + @Override + public abstract HashCode hashBytes(byte[] input, int off, int len); + + @Override + public HashCode hashBytes(ByteBuffer input) { + return newHasher(input.remaining()).putBytes(input).hash(); + } + + /** In-memory stream-based implementation of Hasher. */ + private final class BufferingHasher extends AbstractHasher { + final ExposedByteArrayOutputStream stream; + + BufferingHasher(int expectedInputSize) { + this.stream = new ExposedByteArrayOutputStream(expectedInputSize); + } + + @Override + public Hasher putByte(byte b) { + stream.write(b); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + stream.write(bytes, off, len); + return this; + } + + @Override + public Hasher putBytes(ByteBuffer bytes) { + stream.write(bytes); + return this; + } + + @Override + public HashCode hash() { + return hashBytes(stream.byteArray(), 0, stream.length()); + } + } + + // Just to access the byte[] without introducing an unnecessary copy + private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream { + ExposedByteArrayOutputStream(int expectedInputSize) { + super(expectedInputSize); + } + + void write(ByteBuffer input) { + int remaining = input.remaining(); + if (count + remaining > buf.length) { + buf = Arrays.copyOf(buf, count + remaining); + } + input.get(buf, count, remaining); + count += remaining; + } + + byte[] byteArray() { + return buf; + } + + int length() { + return count; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractStreamingHasher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractStreamingHasher.java new file mode 100644 index 0000000000000..0a7b3d603ae4b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/AbstractStreamingHasher.java @@ -0,0 +1,218 @@ +/* + * 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.iotdb.google.common.hash; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * A convenience base class for implementors of {@code Hasher}; handles accumulating data until an + * entire "chunk" (of implementation-dependent length) is ready to be hashed. + * + * @author Kevin Bourrillion + * @author Dimitris Andreou + */ +// TODO(kevinb): this class still needs some design-and-document-for-inheritance love +@ElementTypesAreNonnullByDefault +abstract class AbstractStreamingHasher extends AbstractHasher { + /** Buffer via which we pass data to the hash algorithm (the implementor) */ + private final ByteBuffer buffer; + + /** Number of bytes to be filled before process() invocation(s). */ + private final int bufferSize; + + /** Number of bytes processed per process() invocation. */ + private final int chunkSize; + + /** + * Constructor for use by subclasses. This hasher instance will process chunks of the specified + * size. + * + * @param chunkSize the number of bytes available per {@link #process(ByteBuffer)} invocation; + * must be at least 4 + */ + protected AbstractStreamingHasher(int chunkSize) { + this(chunkSize, chunkSize); + } + + /** + * Constructor for use by subclasses. This hasher instance will process chunks of the specified + * size, using an internal buffer of {@code bufferSize} size, which must be a multiple of {@code + * chunkSize}. + * + * @param chunkSize the number of bytes available per {@link #process(ByteBuffer)} invocation; + * must be at least 4 + * @param bufferSize the size of the internal buffer. Must be a multiple of chunkSize + */ + protected AbstractStreamingHasher(int chunkSize, int bufferSize) { + // TODO(kevinb): check more preconditions (as bufferSize >= chunkSize) if this is ever public + checkArgument(bufferSize % chunkSize == 0); + + // TODO(user): benchmark performance difference with longer buffer + // always space for a single primitive + this.buffer = ByteBuffer.allocate(bufferSize + 7).order(ByteOrder.LITTLE_ENDIAN); + this.bufferSize = bufferSize; + this.chunkSize = chunkSize; + } + + /** Processes the available bytes of the buffer (at most {@code chunk} bytes). */ + protected abstract void process(ByteBuffer bb); + + /** + * This is invoked for the last bytes of the input, which are not enough to fill a whole chunk. + * The passed {@code ByteBuffer} is guaranteed to be non-empty. + * + *

This implementation simply pads with zeros and delegates to {@link #process(ByteBuffer)}. + */ + protected void processRemaining(ByteBuffer bb) { + Java8Compatibility.position(bb, bb.limit()); // move at the end + Java8Compatibility.limit(bb, chunkSize + 7); // get ready to pad with longs + while (bb.position() < chunkSize) { + bb.putLong(0); + } + Java8Compatibility.limit(bb, chunkSize); + Java8Compatibility.flip(bb); + process(bb); + } + + @Override + public final Hasher putBytes(byte[] bytes, int off, int len) { + return putBytesInternal(ByteBuffer.wrap(bytes, off, len).order(ByteOrder.LITTLE_ENDIAN)); + } + + @Override + public final Hasher putBytes(ByteBuffer readBuffer) { + ByteOrder order = readBuffer.order(); + try { + readBuffer.order(ByteOrder.LITTLE_ENDIAN); + return putBytesInternal(readBuffer); + } finally { + readBuffer.order(order); + } + } + + private Hasher putBytesInternal(ByteBuffer readBuffer) { + // If we have room for all of it, this is easy + if (readBuffer.remaining() <= buffer.remaining()) { + buffer.put(readBuffer); + munchIfFull(); + return this; + } + + // First add just enough to fill buffer size, and munch that + int bytesToCopy = bufferSize - buffer.position(); + for (int i = 0; i < bytesToCopy; i++) { + buffer.put(readBuffer.get()); + } + munch(); // buffer becomes empty here, since chunkSize divides bufferSize + + // Now process directly from the rest of the input buffer + while (readBuffer.remaining() >= chunkSize) { + process(readBuffer); + } + + // Finally stick the remainder back in our usual buffer + buffer.put(readBuffer); + return this; + } + + /* + * Note: hashString(CharSequence, Charset) is intentionally not overridden. + * + * While intuitively, using CharsetEncoder to encode the CharSequence directly to the buffer (or + * even to an intermediate buffer) should be considerably more efficient than potentially + * copying the CharSequence to a String and then calling getBytes(Charset) on that String, in + * reality there are optimizations that make the getBytes(Charset) approach considerably faster, + * at least for commonly used charsets like UTF-8. + */ + + @Override + public final Hasher putByte(byte b) { + buffer.put(b); + munchIfFull(); + return this; + } + + @Override + public final Hasher putShort(short s) { + buffer.putShort(s); + munchIfFull(); + return this; + } + + @Override + public final Hasher putChar(char c) { + buffer.putChar(c); + munchIfFull(); + return this; + } + + @Override + public final Hasher putInt(int i) { + buffer.putInt(i); + munchIfFull(); + return this; + } + + @Override + public final Hasher putLong(long l) { + buffer.putLong(l); + munchIfFull(); + return this; + } + + @Override + public final HashCode hash() { + munch(); + Java8Compatibility.flip(buffer); + if (buffer.remaining() > 0) { + processRemaining(buffer); + Java8Compatibility.position(buffer, buffer.limit()); + } + return makeHash(); + } + + /** + * Computes a hash code based on the data that have been provided to this hasher. This is called + * after all chunks are handled with {@link #process} and any leftover bytes that did not make a + * complete chunk are handled with {@link #processRemaining}. + */ + protected abstract HashCode makeHash(); + + // Process pent-up data in chunks + private void munchIfFull() { + if (buffer.remaining() < 8) { + // buffer is full; not enough room for a primitive. We have at least one full chunk. + munch(); + } + } + + private void munch() { + Java8Compatibility.flip(buffer); + while (buffer.remaining() >= chunkSize) { + // we could limit the buffer to ensure process() does not read more than + // chunkSize number of bytes, but we trust the implementations + process(buffer); + } + buffer.compact(); // preserve any remaining data that do not make a full chunk + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/BloomFilter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/BloomFilter.java new file mode 100644 index 0000000000000..043a06d07fa79 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/BloomFilter.java @@ -0,0 +1,638 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.hash.BloomFilterStrategies.LockFreeBitArray; +import org.apache.iotdb.google.common.math.DoubleMath; +import org.apache.iotdb.google.common.math.LongMath; +import org.apache.iotdb.google.common.primitives.SignedBytes; +import org.apache.iotdb.google.common.primitives.UnsignedBytes; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.math.RoundingMode; +import java.util.stream.Collector; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A Bloom filter for instances of {@code T}. A Bloom filter offers an approximate containment test + * with one-sided error: if it claims that an element is contained in it, this might be in error, + * but if it claims that an element is not contained in it, then this is definitely true. + * + *

If you are unfamiliar with Bloom filters, this nice tutorial may help you understand how + * they work. + * + *

The false positive probability ({@code FPP}) of a Bloom filter is defined as the probability + * that {@linkplain #mightContain(Object)} will erroneously return {@code true} for an object that + * has not actually been put in the {@code BloomFilter}. + * + *

Bloom filters are serializable. They also support a more compact serial representation via the + * {@link #writeTo} and {@link #readFrom} methods. Both serialized forms will continue to be + * supported by future versions of this library. However, serial forms generated by newer versions + * of the code may not be readable by older versions of the code (e.g., a serialized Bloom filter + * generated today may not be readable by a binary that was compiled 6 months ago). + * + *

As of Guava 23.0, this class is thread-safe and lock-free. It internally uses atomics and + * compare-and-swap to ensure correctness when multiple threads are used to access it. + * + * @param the type of instances that the {@code BloomFilter} accepts + * @author Dimitris Andreou + * @author Kevin Bourrillion + * @since 11.0 (thread-safe since 23.0) + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class BloomFilter implements Predicate, Serializable { + /** + * A strategy to translate T instances, to {@code numHashFunctions} bit indexes. + * + *

Implementations should be collections of pure functions (i.e. stateless). + */ + interface Strategy extends java.io.Serializable { + + /** + * Sets {@code numHashFunctions} bits of the given bit array, by hashing a user element. + * + *

Returns whether any bits changed as a result of this operation. + */ + boolean put( + @ParametricNullness T object, + Funnel funnel, + int numHashFunctions, + LockFreeBitArray bits); + + /** + * Queries {@code numHashFunctions} bits of the given bit array, by hashing a user element; + * returns {@code true} if and only if all selected bits are set. + */ + boolean mightContain( + @ParametricNullness T object, + Funnel funnel, + int numHashFunctions, + LockFreeBitArray bits); + + /** + * Identifier used to encode this strategy, when marshalled as part of a BloomFilter. Only + * values in the [-128, 127] range are valid for the compact serial form. Non-negative values + * are reserved for enums defined in BloomFilterStrategies; negative values are reserved for any + * custom, stateful strategy we may define (e.g. any kind of strategy that would depend on user + * input). + */ + int ordinal(); + } + + /** The bit set of the BloomFilter (not necessarily power of 2!) */ + private final LockFreeBitArray bits; + + /** Number of hashes per element */ + private final int numHashFunctions; + + /** The funnel to translate Ts to bytes */ + private final Funnel funnel; + + /** The strategy we employ to map an element T to {@code numHashFunctions} bit indexes. */ + private final Strategy strategy; + + /** Creates a BloomFilter. */ + private BloomFilter( + LockFreeBitArray bits, int numHashFunctions, Funnel funnel, Strategy strategy) { + checkArgument(numHashFunctions > 0, "numHashFunctions (%s) must be > 0", numHashFunctions); + checkArgument( + numHashFunctions <= 255, "numHashFunctions (%s) must be <= 255", numHashFunctions); + this.bits = checkNotNull(bits); + this.numHashFunctions = numHashFunctions; + this.funnel = checkNotNull(funnel); + this.strategy = checkNotNull(strategy); + } + + /** + * Creates a new {@code BloomFilter} that's a copy of this instance. The new instance is equal to + * this instance but shares no mutable state. + * + * @since 12.0 + */ + public BloomFilter copy() { + return new BloomFilter(bits.copy(), numHashFunctions, funnel, strategy); + } + + /** + * Returns {@code true} if the element might have been put in this Bloom filter, {@code + * false} if this is definitely not the case. + */ + public boolean mightContain(@ParametricNullness T object) { + return strategy.mightContain(object, funnel, numHashFunctions, bits); + } + + /** + * @deprecated Provided only to satisfy the {@link Predicate} interface; use {@link #mightContain} + * instead. + */ + @Deprecated + @Override + public boolean apply(@ParametricNullness T input) { + return mightContain(input); + } + + /** + * Puts an element into this {@code BloomFilter}. Ensures that subsequent invocations of {@link + * #mightContain(Object)} with the same element will always return {@code true}. + * + * @return true if the Bloom filter's bits changed as a result of this operation. If the bits + * changed, this is definitely the first time {@code object} has been added to the + * filter. If the bits haven't changed, this might be the first time {@code object} has + * been added to the filter. Note that {@code put(t)} always returns the opposite + * result to what {@code mightContain(t)} would have returned at the time it is called. + * @since 12.0 (present in 11.0 with {@code void} return type}) + */ + public boolean put(@ParametricNullness T object) { + return strategy.put(object, funnel, numHashFunctions, bits); + } + + /** + * Returns the probability that {@linkplain #mightContain(Object)} will erroneously return {@code + * true} for an object that has not actually been put in the {@code BloomFilter}. + * + *

Ideally, this number should be close to the {@code fpp} parameter passed in {@linkplain + * #create(Funnel, int, double)}, or smaller. If it is significantly higher, it is usually the + * case that too many elements (more than expected) have been put in the {@code BloomFilter}, + * degenerating it. + * + * @since 14.0 (since 11.0 as expectedFalsePositiveProbability()) + */ + public double expectedFpp() { + return Math.pow((double) bits.bitCount() / bitSize(), numHashFunctions); + } + + /** + * Returns an estimate for the total number of distinct elements that have been added to this + * Bloom filter. This approximation is reasonably accurate if it does not exceed the value of + * {@code expectedInsertions} that was used when constructing the filter. + * + * @since 22.0 + */ + public long approximateElementCount() { + long bitSize = bits.bitSize(); + long bitCount = bits.bitCount(); + + /** + * Each insertion is expected to reduce the # of clear bits by a factor of + * `numHashFunctions/bitSize`. So, after n insertions, expected bitCount is `bitSize * (1 - (1 - + * numHashFunctions/bitSize)^n)`. Solving that for n, and approximating `ln x` as `x - 1` when x + * is close to 1 (why?), gives the following formula. + */ + double fractionOfBitsSet = (double) bitCount / bitSize; + return DoubleMath.roundToLong( + -Math.log1p(-fractionOfBitsSet) * bitSize / numHashFunctions, RoundingMode.HALF_UP); + } + + /** Returns the number of bits in the underlying bit array. */ + @VisibleForTesting + long bitSize() { + return bits.bitSize(); + } + + /** + * Determines whether a given Bloom filter is compatible with this Bloom filter. For two Bloom + * filters to be compatible, they must: + * + *

    + *
  • not be the same instance + *
  • have the same number of hash functions + *
  • have the same bit size + *
  • have the same strategy + *
  • have equal funnels + *
+ * + * @param that The Bloom filter to check for compatibility. + * @since 15.0 + */ + public boolean isCompatible(BloomFilter that) { + checkNotNull(that); + return this != that + && this.numHashFunctions == that.numHashFunctions + && this.bitSize() == that.bitSize() + && this.strategy.equals(that.strategy) + && this.funnel.equals(that.funnel); + } + + /** + * Combines this Bloom filter with another Bloom filter by performing a bitwise OR of the + * underlying data. The mutations happen to this instance. Callers must ensure the Bloom + * filters are appropriately sized to avoid saturating them. + * + * @param that The Bloom filter to combine this Bloom filter with. It is not mutated. + * @throws IllegalArgumentException if {@code isCompatible(that) == false} + * @since 15.0 + */ + public void putAll(BloomFilter that) { + checkNotNull(that); + checkArgument(this != that, "Cannot combine a BloomFilter with itself."); + checkArgument( + this.numHashFunctions == that.numHashFunctions, + "BloomFilters must have the same number of hash functions (%s != %s)", + this.numHashFunctions, + that.numHashFunctions); + checkArgument( + this.bitSize() == that.bitSize(), + "BloomFilters must have the same size underlying bit arrays (%s != %s)", + this.bitSize(), + that.bitSize()); + checkArgument( + this.strategy.equals(that.strategy), + "BloomFilters must have equal strategies (%s != %s)", + this.strategy, + that.strategy); + checkArgument( + this.funnel.equals(that.funnel), + "BloomFilters must have equal funnels (%s != %s)", + this.funnel, + that.funnel); + this.bits.putAll(that.bits); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof BloomFilter) { + BloomFilter that = (BloomFilter) object; + return this.numHashFunctions == that.numHashFunctions + && this.funnel.equals(that.funnel) + && this.bits.equals(that.bits) + && this.strategy.equals(that.strategy); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(numHashFunctions, funnel, strategy, bits); + } + + /** + * Returns a {@code Collector} expecting the specified number of insertions, and yielding a {@link + * BloomFilter} with false positive probability 3%. + * + *

Note that if the {@code Collector} receives significantly more elements than specified, the + * resulting {@code BloomFilter} will suffer a sharp deterioration of its false positive + * probability. + * + *

The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @return a {@code Collector} generating a {@code BloomFilter} of the received elements + * @since 23.0 + */ + public static Collector> toBloomFilter( + Funnel funnel, long expectedInsertions) { + return toBloomFilter(funnel, expectedInsertions, 0.03); + } + + /** + * Returns a {@code Collector} expecting the specified number of insertions, and yielding a {@link + * BloomFilter} with the specified expected false positive probability. + * + *

Note that if the {@code Collector} receives significantly more elements than specified, the + * resulting {@code BloomFilter} will suffer a sharp deterioration of its false positive + * probability. + * + *

The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @param fpp the desired false positive probability (must be positive and less than 1.0) + * @return a {@code Collector} generating a {@code BloomFilter} of the received elements + * @since 23.0 + */ + public static Collector> toBloomFilter( + Funnel funnel, long expectedInsertions, double fpp) { + checkNotNull(funnel); + checkArgument( + expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", expectedInsertions); + checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp); + checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp); + return Collector.of( + () -> BloomFilter.create(funnel, expectedInsertions, fpp), + BloomFilter::put, + (bf1, bf2) -> { + bf1.putAll(bf2); + return bf1; + }, + Collector.Characteristics.UNORDERED, + Collector.Characteristics.CONCURRENT); + } + + /** + * Creates a {@link BloomFilter} with the expected number of insertions and expected false + * positive probability. + * + *

Note that overflowing a {@code BloomFilter} with significantly more elements than specified, + * will result in its saturation, and a sharp deterioration of its false positive probability. + * + *

The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @param fpp the desired false positive probability (must be positive and less than 1.0) + * @return a {@code BloomFilter} + */ + public static BloomFilter create( + Funnel funnel, int expectedInsertions, double fpp) { + return create(funnel, (long) expectedInsertions, fpp); + } + + /** + * Creates a {@link BloomFilter} with the expected number of insertions and expected false + * positive probability. + * + *

Note that overflowing a {@code BloomFilter} with significantly more elements than specified, + * will result in its saturation, and a sharp deterioration of its false positive probability. + * + *

The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @param fpp the desired false positive probability (must be positive and less than 1.0) + * @return a {@code BloomFilter} + * @since 19.0 + */ + public static BloomFilter create( + Funnel funnel, long expectedInsertions, double fpp) { + return create(funnel, expectedInsertions, fpp, BloomFilterStrategies.MURMUR128_MITZ_64); + } + + @VisibleForTesting + static BloomFilter create( + Funnel funnel, long expectedInsertions, double fpp, Strategy strategy) { + checkNotNull(funnel); + checkArgument( + expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", expectedInsertions); + checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp); + checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp); + checkNotNull(strategy); + + if (expectedInsertions == 0) { + expectedInsertions = 1; + } + /* + * TODO(user): Put a warning in the javadoc about tiny fpp values, since the resulting size + * is proportional to -log(p), but there is not much of a point after all, e.g. + * optimalM(1000, 0.0000000000000001) = 76680 which is less than 10kb. Who cares! + */ + long numBits = optimalNumOfBits(expectedInsertions, fpp); + int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits); + try { + return new BloomFilter(new LockFreeBitArray(numBits), numHashFunctions, funnel, strategy); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Could not create BloomFilter of " + numBits + " bits", e); + } + } + + /** + * Creates a {@link BloomFilter} with the expected number of insertions and a default expected + * false positive probability of 3%. + * + *

Note that overflowing a {@code BloomFilter} with significantly more elements than specified, + * will result in its saturation, and a sharp deterioration of its false positive probability. + * + *

The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @return a {@code BloomFilter} + */ + public static BloomFilter create( + Funnel funnel, int expectedInsertions) { + return create(funnel, (long) expectedInsertions); + } + + /** + * Creates a {@link BloomFilter} with the expected number of insertions and a default expected + * false positive probability of 3%. + * + *

Note that overflowing a {@code BloomFilter} with significantly more elements than specified, + * will result in its saturation, and a sharp deterioration of its false positive probability. + * + *

The constructed {@code BloomFilter} will be serializable if the provided {@code Funnel} + * is. + * + *

It is recommended that the funnel be implemented as a Java enum. This has the benefit of + * ensuring proper serialization and deserialization, which is important since {@link #equals} + * also relies on object identity of funnels. + * + * @param funnel the funnel of T's that the constructed {@code BloomFilter} will use + * @param expectedInsertions the number of expected insertions to the constructed {@code + * BloomFilter}; must be positive + * @return a {@code BloomFilter} + * @since 19.0 + */ + public static BloomFilter create( + Funnel funnel, long expectedInsertions) { + return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions + } + + // Cheat sheet: + // + // m: total bits + // n: expected insertions + // b: m/n, bits per insertion + // p: expected false positive probability + // + // 1) Optimal k = b * ln2 + // 2) p = (1 - e ^ (-kn/m))^k + // 3) For optimal k: p = 2 ^ (-k) ~= 0.6185^b + // 4) For optimal k: m = -nlnp / ((ln2) ^ 2) + + /** + * Computes the optimal k (number of hashes per element inserted in Bloom filter), given the + * expected insertions and total number of bits in the Bloom filter. + * + *

See http://en.wikipedia.org/wiki/File:Bloom_filter_fp_probability.svg for the formula. + * + * @param n expected insertions (must be positive) + * @param m total number of bits in Bloom filter (must be positive) + */ + @VisibleForTesting + static int optimalNumOfHashFunctions(long n, long m) { + // (m / n) * log(2), but avoid truncation due to division! + return Math.max(1, (int) Math.round((double) m / n * Math.log(2))); + } + + /** + * Computes m (total bits of Bloom filter) which is expected to achieve, for the specified + * expected insertions, the required false positive probability. + * + *

See http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives for the + * formula. + * + * @param n expected insertions (must be positive) + * @param p false positive rate (must be 0 < p < 1) + */ + @VisibleForTesting + static long optimalNumOfBits(long n, double p) { + if (p == 0) { + p = Double.MIN_VALUE; + } + return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2))); + } + + private Object writeReplace() { + return new SerialForm(this); + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + private static class SerialForm implements Serializable { + final long[] data; + final int numHashFunctions; + final Funnel funnel; + final Strategy strategy; + + SerialForm(BloomFilter bf) { + this.data = LockFreeBitArray.toPlainArray(bf.bits.data); + this.numHashFunctions = bf.numHashFunctions; + this.funnel = bf.funnel; + this.strategy = bf.strategy; + } + + Object readResolve() { + return new BloomFilter(new LockFreeBitArray(data), numHashFunctions, funnel, strategy); + } + + private static final long serialVersionUID = 1; + } + + /** + * Writes this {@code BloomFilter} to an output stream, with a custom format (not Java + * serialization). This has been measured to save at least 400 bytes compared to regular + * serialization. + * + *

Use {@linkplain #readFrom(InputStream, Funnel)} to reconstruct the written BloomFilter. + */ + public void writeTo(OutputStream out) throws IOException { + // Serial form: + // 1 signed byte for the strategy + // 1 unsigned byte for the number of hash functions + // 1 big endian int, the number of longs in our bitset + // N big endian longs of our bitset + DataOutputStream dout = new DataOutputStream(out); + dout.writeByte(SignedBytes.checkedCast(strategy.ordinal())); + dout.writeByte(UnsignedBytes.checkedCast(numHashFunctions)); // note: checked at the c'tor + dout.writeInt(bits.data.length()); + for (int i = 0; i < bits.data.length(); i++) { + dout.writeLong(bits.data.get(i)); + } + } + + /** + * Reads a byte stream, which was written by {@linkplain #writeTo(OutputStream)}, into a {@code + * BloomFilter}. + * + *

The {@code Funnel} to be used is not encoded in the stream, so it must be provided here. + * Warning: the funnel provided must behave identically to the one used to populate + * the original Bloom filter! + * + * @throws IOException if the InputStream throws an {@code IOException}, or if its data does not + * appear to be a BloomFilter serialized using the {@linkplain #writeTo(OutputStream)} method. + */ + public static BloomFilter readFrom(InputStream in, Funnel funnel) + throws IOException { + checkNotNull(in, "InputStream"); + checkNotNull(funnel, "Funnel"); + int strategyOrdinal = -1; + int numHashFunctions = -1; + int dataLength = -1; + try { + DataInputStream din = new DataInputStream(in); + // currently this assumes there is no negative ordinal; will have to be updated if we + // add non-stateless strategies (for which we've reserved negative ordinals; see + // Strategy.ordinal()). + strategyOrdinal = din.readByte(); + numHashFunctions = UnsignedBytes.toInt(din.readByte()); + dataLength = din.readInt(); + + Strategy strategy = BloomFilterStrategies.values()[strategyOrdinal]; + + LockFreeBitArray dataArray = new LockFreeBitArray(LongMath.checkedMultiply(dataLength, 64L)); + for (int i = 0; i < dataLength; i++) { + dataArray.putData(i, din.readLong()); + } + + return new BloomFilter(dataArray, numHashFunctions, funnel, strategy); + } catch (RuntimeException e) { + String message = + "Unable to deserialize BloomFilter from InputStream." + + " strategyOrdinal: " + + strategyOrdinal + + " numHashFunctions: " + + numHashFunctions + + " dataLength: " + + dataLength; + throw new IOException(message, e); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/BloomFilterStrategies.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/BloomFilterStrategies.java new file mode 100644 index 0000000000000..0ca69fc5d8ebe --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/BloomFilterStrategies.java @@ -0,0 +1,318 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.math.LongMath; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.primitives.Longs; + +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLongArray; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * Collections of strategies of generating the k * log(M) bits required for an element to be mapped + * to a BloomFilter of M bits and k hash functions. These strategies are part of the serialized form + * of the Bloom filters that use them, thus they must be preserved as is (no updates allowed, only + * introduction of new versions). + * + *

Important: the order of the constants cannot change, and they cannot be deleted - we depend on + * their ordinal for BloomFilter serialization. + * + * @author Dimitris Andreou + * @author Kurt Alfred Kluever + */ +@ElementTypesAreNonnullByDefault +enum BloomFilterStrategies implements BloomFilter.Strategy { + /** + * See "Less Hashing, Same Performance: Building a Better Bloom Filter" by Adam Kirsch and Michael + * Mitzenmacher. The paper argues that this trick doesn't significantly deteriorate the + * performance of a Bloom filter (yet only needs two 32bit hash functions). + */ + MURMUR128_MITZ_32() { + @Override + public boolean put( + @ParametricNullness T object, + Funnel funnel, + int numHashFunctions, + LockFreeBitArray bits) { + long bitSize = bits.bitSize(); + long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); + int hash1 = (int) hash64; + int hash2 = (int) (hash64 >>> 32); + + boolean bitsChanged = false; + for (int i = 1; i <= numHashFunctions; i++) { + int combinedHash = hash1 + (i * hash2); + // Flip all the bits if it's negative (guaranteed positive number) + if (combinedHash < 0) { + combinedHash = ~combinedHash; + } + bitsChanged |= bits.set(combinedHash % bitSize); + } + return bitsChanged; + } + + @Override + public boolean mightContain( + @ParametricNullness T object, + Funnel funnel, + int numHashFunctions, + LockFreeBitArray bits) { + long bitSize = bits.bitSize(); + long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); + int hash1 = (int) hash64; + int hash2 = (int) (hash64 >>> 32); + + for (int i = 1; i <= numHashFunctions; i++) { + int combinedHash = hash1 + (i * hash2); + // Flip all the bits if it's negative (guaranteed positive number) + if (combinedHash < 0) { + combinedHash = ~combinedHash; + } + if (!bits.get(combinedHash % bitSize)) { + return false; + } + } + return true; + } + }, + /** + * This strategy uses all 128 bits of {@link Hashing#murmur3_128} when hashing. It looks different + * from the implementation in MURMUR128_MITZ_32 because we're avoiding the multiplication in the + * loop and doing a (much simpler) += hash2. We're also changing the index to a positive number by + * AND'ing with Long.MAX_VALUE instead of flipping the bits. + */ + MURMUR128_MITZ_64() { + @Override + public boolean put( + @ParametricNullness T object, + Funnel funnel, + int numHashFunctions, + LockFreeBitArray bits) { + long bitSize = bits.bitSize(); + byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal(); + long hash1 = lowerEight(bytes); + long hash2 = upperEight(bytes); + + boolean bitsChanged = false; + long combinedHash = hash1; + for (int i = 0; i < numHashFunctions; i++) { + // Make the combined hash positive and indexable + bitsChanged |= bits.set((combinedHash & Long.MAX_VALUE) % bitSize); + combinedHash += hash2; + } + return bitsChanged; + } + + @Override + public boolean mightContain( + @ParametricNullness T object, + Funnel funnel, + int numHashFunctions, + LockFreeBitArray bits) { + long bitSize = bits.bitSize(); + byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal(); + long hash1 = lowerEight(bytes); + long hash2 = upperEight(bytes); + + long combinedHash = hash1; + for (int i = 0; i < numHashFunctions; i++) { + // Make the combined hash positive and indexable + if (!bits.get((combinedHash & Long.MAX_VALUE) % bitSize)) { + return false; + } + combinedHash += hash2; + } + return true; + } + + private /* static */ long lowerEight(byte[] bytes) { + return Longs.fromBytes( + bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); + } + + private /* static */ long upperEight(byte[] bytes) { + return Longs.fromBytes( + bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]); + } + }; + + /** + * Models a lock-free array of bits. + * + *

We use this instead of java.util.BitSet because we need access to the array of longs and we + * need compare-and-swap. + */ + static final class LockFreeBitArray { + private static final int LONG_ADDRESSABLE_BITS = 6; + final AtomicLongArray data; + private final LongAddable bitCount; + + LockFreeBitArray(long bits) { + checkArgument(bits > 0, "data length is zero!"); + // Avoid delegating to this(long[]), since AtomicLongArray(long[]) will clone its input and + // thus double memory usage. + this.data = + new AtomicLongArray(Ints.checkedCast(LongMath.divide(bits, 64, RoundingMode.CEILING))); + this.bitCount = LongAddables.create(); + } + + // Used by serialization + LockFreeBitArray(long[] data) { + checkArgument(data.length > 0, "data length is zero!"); + this.data = new AtomicLongArray(data); + this.bitCount = LongAddables.create(); + long bitCount = 0; + for (long value : data) { + bitCount += Long.bitCount(value); + } + this.bitCount.add(bitCount); + } + + /** Returns true if the bit changed value. */ + boolean set(long bitIndex) { + if (get(bitIndex)) { + return false; + } + + int longIndex = (int) (bitIndex >>> LONG_ADDRESSABLE_BITS); + long mask = 1L << bitIndex; // only cares about low 6 bits of bitIndex + + long oldValue; + long newValue; + do { + oldValue = data.get(longIndex); + newValue = oldValue | mask; + if (oldValue == newValue) { + return false; + } + } while (!data.compareAndSet(longIndex, oldValue, newValue)); + + // We turned the bit on, so increment bitCount. + bitCount.increment(); + return true; + } + + boolean get(long bitIndex) { + return (data.get((int) (bitIndex >>> LONG_ADDRESSABLE_BITS)) & (1L << bitIndex)) != 0; + } + + /** + * Careful here: if threads are mutating the atomicLongArray while this method is executing, the + * final long[] will be a "rolling snapshot" of the state of the bit array. This is usually good + * enough, but should be kept in mind. + */ + public static long[] toPlainArray(AtomicLongArray atomicLongArray) { + long[] array = new long[atomicLongArray.length()]; + for (int i = 0; i < array.length; ++i) { + array[i] = atomicLongArray.get(i); + } + return array; + } + + /** Number of bits */ + long bitSize() { + return (long) data.length() * Long.SIZE; + } + + /** + * Number of set bits (1s). + * + *

Note that because of concurrent set calls and uses of atomics, this bitCount is a (very) + * close *estimate* of the actual number of bits set. It's not possible to do better than an + * estimate without locking. Note that the number, if not exactly accurate, is *always* + * underestimating, never overestimating. + */ + long bitCount() { + return bitCount.sum(); + } + + LockFreeBitArray copy() { + return new LockFreeBitArray(toPlainArray(data)); + } + + /** + * Combines the two BitArrays using bitwise OR. + * + *

NOTE: Because of the use of atomics, if the other LockFreeBitArray is being mutated while + * this operation is executing, not all of those new 1's may be set in the final state of this + * LockFreeBitArray. The ONLY guarantee provided is that all the bits that were set in the other + * LockFreeBitArray at the start of this method will be set in this LockFreeBitArray at the end + * of this method. + */ + void putAll(LockFreeBitArray other) { + checkArgument( + data.length() == other.data.length(), + "BitArrays must be of equal length (%s != %s)", + data.length(), + other.data.length()); + for (int i = 0; i < data.length(); i++) { + putData(i, other.data.get(i)); + } + } + + /** + * ORs the bits encoded in the {@code i}th {@code long} in the underlying {@link + * AtomicLongArray} with the given value. + */ + void putData(int i, long longValue) { + long ourLongOld; + long ourLongNew; + boolean changedAnyBits = true; + do { + ourLongOld = data.get(i); + ourLongNew = ourLongOld | longValue; + if (ourLongOld == ourLongNew) { + changedAnyBits = false; + break; + } + } while (!data.compareAndSet(i, ourLongOld, ourLongNew)); + + if (changedAnyBits) { + int bitsAdded = Long.bitCount(ourLongNew) - Long.bitCount(ourLongOld); + bitCount.add(bitsAdded); + } + } + + /** Returns the number of {@code long}s in the underlying {@link AtomicLongArray}. */ + int dataLength() { + return data.length(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof LockFreeBitArray) { + LockFreeBitArray lockFreeBitArray = (LockFreeBitArray) o; + // TODO(lowasser): avoid allocation here + return Arrays.equals(toPlainArray(data), toPlainArray(lockFreeBitArray.data)); + } + return false; + } + + @Override + public int hashCode() { + // TODO(lowasser): avoid allocation here + return Arrays.hashCode(toPlainArray(data)); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ChecksumHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ChecksumHashFunction.java new file mode 100644 index 0000000000000..7665c70c8dab2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ChecksumHashFunction.java @@ -0,0 +1,97 @@ +/* + * 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.iotdb.google.common.hash; + +import java.io.Serializable; +import java.util.zip.Checksum; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * {@link HashFunction} adapter for {@link Checksum} instances. + * + * @author Colin Decker + */ +@ElementTypesAreNonnullByDefault +final class ChecksumHashFunction extends AbstractHashFunction implements Serializable { + private final ImmutableSupplier checksumSupplier; + private final int bits; + private final String toString; + + ChecksumHashFunction( + ImmutableSupplier checksumSupplier, int bits, String toString) { + this.checksumSupplier = checkNotNull(checksumSupplier); + checkArgument(bits == 32 || bits == 64, "bits (%s) must be either 32 or 64", bits); + this.bits = bits; + this.toString = checkNotNull(toString); + } + + @Override + public int bits() { + return bits; + } + + @Override + public Hasher newHasher() { + return new ChecksumHasher(checksumSupplier.get()); + } + + @Override + public String toString() { + return toString; + } + + /** Hasher that updates a checksum. */ + private final class ChecksumHasher extends AbstractByteHasher { + private final Checksum checksum; + + private ChecksumHasher(Checksum checksum) { + this.checksum = checkNotNull(checksum); + } + + @Override + protected void update(byte b) { + checksum.update(b); + } + + @Override + protected void update(byte[] bytes, int off, int len) { + checksum.update(bytes, off, len); + } + + @Override + public HashCode hash() { + long value = checksum.getValue(); + if (bits == 32) { + /* + * The long returned from a 32-bit Checksum will have all 0s for its second word, so the + * cast won't lose any information and is necessary to return a HashCode of the correct + * size. + */ + return HashCode.fromInt((int) value); + } else { + return HashCode.fromLong(value); + } + } + } + + private static final long serialVersionUID = 0L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Crc32cHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Crc32cHashFunction.java new file mode 100644 index 0000000000000..9ef30553418d7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Crc32cHashFunction.java @@ -0,0 +1,377 @@ +/* + * 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.iotdb.google.common.hash; + +import java.nio.ByteBuffer; + +/** + * This class generates a CRC32C checksum, defined by RFC 3720, Section 12.1. The generator + * polynomial for this checksum is {@code 0x11EDC6F41}. + * + * @author Kurt Alfred Kluever + */ +@ElementTypesAreNonnullByDefault +final class Crc32cHashFunction extends AbstractHashFunction { + static final HashFunction CRC_32_C = new Crc32cHashFunction(); + + @Override + public int bits() { + return 32; + } + + @Override + public Hasher newHasher() { + return new Crc32cHasher(); + } + + @Override + public String toString() { + return "Hashing.crc32c()"; + } + + static final class Crc32cHasher extends AbstractStreamingHasher { + + /* + * The striding algorithm works roughly as follows: it is universally the case that + * CRC(x ^ y) == CRC(x) ^ CRC(y). The approach we take is to break the message as follows, + * with each letter representing a 4-byte word: ABCDABCDABCDABCD... and to calculate + * CRC(A000A000A000...), CRC(0B000B000B...), CRC(00C000C000C...), CRC(000D000D000D...) + * and then to XOR them together. The STRIDE_TABLE enables us to hash an int followed by 12 + * zero bytes (3 ints), while the BYTE_TABLE is for advancing one byte at a time. + * This algorithm is due to the paper "Everything we know about CRC but [are] afraid to forget" + * by Kadatch and Jenkins, 2010. + */ + + Crc32cHasher() { + super(16); + } + + private boolean finished = false; + + /* + * This trick allows us to avoid having separate states for "first four ints" and "all other + * four int chunks." The state we want after the first four bytes is + * + * crc0 = ~int0 + * crc1 = int1 + * crc2 = int2 + * crc3 = int3 + * + * ...so we set crc0 so that computeForWord(crc0) = -1 and xoring it with the first int + * gives us the desired result. computeForWord(0) == 0, so all the others do the right thing. + */ + private int crc0 = INVERSE_COMPUTE_FOR_WORD_OF_ALL_1S; + private int crc1 = 0; + private int crc2 = 0; + private int crc3 = 0; + + @Override + protected void process(ByteBuffer bb) { + if (finished) { + throw new IllegalStateException( + "The behavior of calling any method after calling hash() is undefined."); + } + while (bb.remaining() >= 16) { + crc0 = computeForWord(crc0); + crc1 = computeForWord(crc1); + crc2 = computeForWord(crc2); + crc3 = computeForWord(crc3); + crc0 ^= bb.getInt(); + crc1 ^= bb.getInt(); + crc2 ^= bb.getInt(); + crc3 ^= bb.getInt(); + } + } + + @Override + protected void processRemaining(ByteBuffer bb) { + if (finished) { + return; + } + crc0 = combine(0, crc0); + crc0 = combine(crc0, crc1); + crc0 = combine(crc0, crc2); + crc0 = combine(crc0, crc3); + while (bb.hasRemaining()) { + crc0 = (crc0 >>> 8) ^ BYTE_TABLE[(bb.get() ^ crc0) & 0xFF]; + } + finished = true; + } + + @Override + protected HashCode makeHash() { + if (!finished) { + // processRemaining does teardown we always want to do -- the folding together of the four + // rolling CRCs. So we call it on an empty ByteBuffer if we didn't already. + processRemaining(EMPTY); + } + return HashCode.fromInt(~crc0); + } + + static final int[] BYTE_TABLE = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, + 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, + 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, + 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, + 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, + 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, + 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, + 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, + 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, + 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, + 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, + 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4, + 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, + 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, + 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, + 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, + 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, + 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, + 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, + 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, + 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, + 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 + }; + + static final int[][] STRIDE_TABLE = { + { + 0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1, + 0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76, + 0x4035544d, 0x70e76c28, 0x21912487, 0x11431ce2, 0x03171d43, 0x33c52526, + 0x62b36d89, 0x526155ec, 0xc05ffcd7, 0xf08dc4b2, 0xa1fb8c1d, 0x9129b478, + 0x806aa89a, 0xb0b890ff, 0xe1ced850, 0xd11ce035, 0x4322490e, 0x73f0716b, + 0x228639c4, 0x125401a1, 0x062e3a86, 0x36fc02e3, 0x678a4a4c, 0x57587229, + 0xc566db12, 0xf5b4e377, 0xa4c2abd8, 0x941093bd, 0x85538f5f, 0xb581b73a, + 0xe4f7ff95, 0xd425c7f0, 0x461b6ecb, 0x76c956ae, 0x27bf1e01, 0x176d2664, + 0x053927c5, 0x35eb1fa0, 0x649d570f, 0x544f6f6a, 0xc671c651, 0xf6a3fe34, + 0xa7d5b69b, 0x97078efe, 0x8644921c, 0xb696aa79, 0xe7e0e2d6, 0xd732dab3, + 0x450c7388, 0x75de4bed, 0x24a80342, 0x147a3b27, 0x0c5c750c, 0x3c8e4d69, + 0x6df805c6, 0x5d2a3da3, 0xcf149498, 0xffc6acfd, 0xaeb0e452, 0x9e62dc37, + 0x8f21c0d5, 0xbff3f8b0, 0xee85b01f, 0xde57887a, 0x4c692141, 0x7cbb1924, + 0x2dcd518b, 0x1d1f69ee, 0x0f4b684f, 0x3f99502a, 0x6eef1885, 0x5e3d20e0, + 0xcc0389db, 0xfcd1b1be, 0xada7f911, 0x9d75c174, 0x8c36dd96, 0xbce4e5f3, + 0xed92ad5c, 0xdd409539, 0x4f7e3c02, 0x7fac0467, 0x2eda4cc8, 0x1e0874ad, + 0x0a724f8a, 0x3aa077ef, 0x6bd63f40, 0x5b040725, 0xc93aae1e, 0xf9e8967b, + 0xa89eded4, 0x984ce6b1, 0x890ffa53, 0xb9ddc236, 0xe8ab8a99, 0xd879b2fc, + 0x4a471bc7, 0x7a9523a2, 0x2be36b0d, 0x1b315368, 0x096552c9, 0x39b76aac, + 0x68c12203, 0x58131a66, 0xca2db35d, 0xfaff8b38, 0xab89c397, 0x9b5bfbf2, + 0x8a18e710, 0xbacadf75, 0xebbc97da, 0xdb6eafbf, 0x49500684, 0x79823ee1, + 0x28f4764e, 0x18264e2b, 0x18b8ea18, 0x286ad27d, 0x791c9ad2, 0x49cea2b7, + 0xdbf00b8c, 0xeb2233e9, 0xba547b46, 0x8a864323, 0x9bc55fc1, 0xab1767a4, + 0xfa612f0b, 0xcab3176e, 0x588dbe55, 0x685f8630, 0x3929ce9f, 0x09fbf6fa, + 0x1baff75b, 0x2b7dcf3e, 0x7a0b8791, 0x4ad9bff4, 0xd8e716cf, 0xe8352eaa, + 0xb9436605, 0x89915e60, 0x98d24282, 0xa8007ae7, 0xf9763248, 0xc9a40a2d, + 0x5b9aa316, 0x6b489b73, 0x3a3ed3dc, 0x0aecebb9, 0x1e96d09e, 0x2e44e8fb, + 0x7f32a054, 0x4fe09831, 0xddde310a, 0xed0c096f, 0xbc7a41c0, 0x8ca879a5, + 0x9deb6547, 0xad395d22, 0xfc4f158d, 0xcc9d2de8, 0x5ea384d3, 0x6e71bcb6, + 0x3f07f419, 0x0fd5cc7c, 0x1d81cddd, 0x2d53f5b8, 0x7c25bd17, 0x4cf78572, + 0xdec92c49, 0xee1b142c, 0xbf6d5c83, 0x8fbf64e6, 0x9efc7804, 0xae2e4061, + 0xff5808ce, 0xcf8a30ab, 0x5db49990, 0x6d66a1f5, 0x3c10e95a, 0x0cc2d13f, + 0x14e49f14, 0x2436a771, 0x7540efde, 0x4592d7bb, 0xd7ac7e80, 0xe77e46e5, + 0xb6080e4a, 0x86da362f, 0x97992acd, 0xa74b12a8, 0xf63d5a07, 0xc6ef6262, + 0x54d1cb59, 0x6403f33c, 0x3575bb93, 0x05a783f6, 0x17f38257, 0x2721ba32, + 0x7657f29d, 0x4685caf8, 0xd4bb63c3, 0xe4695ba6, 0xb51f1309, 0x85cd2b6c, + 0x948e378e, 0xa45c0feb, 0xf52a4744, 0xc5f87f21, 0x57c6d61a, 0x6714ee7f, + 0x3662a6d0, 0x06b09eb5, 0x12caa592, 0x22189df7, 0x736ed558, 0x43bced3d, + 0xd1824406, 0xe1507c63, 0xb02634cc, 0x80f40ca9, 0x91b7104b, 0xa165282e, + 0xf0136081, 0xc0c158e4, 0x52fff1df, 0x622dc9ba, 0x335b8115, 0x0389b970, + 0x11ddb8d1, 0x210f80b4, 0x7079c81b, 0x40abf07e, 0xd2955945, 0xe2476120, + 0xb331298f, 0x83e311ea, 0x92a00d08, 0xa272356d, 0xf3047dc2, 0xc3d645a7, + 0x51e8ec9c, 0x613ad4f9, 0x304c9c56, 0x009ea433, + }, + { + 0x00000000, 0x54075546, 0xa80eaa8c, 0xfc09ffca, 0x55f123e9, 0x01f676af, + 0xfdff8965, 0xa9f8dc23, 0xabe247d2, 0xffe51294, 0x03eced5e, 0x57ebb818, + 0xfe13643b, 0xaa14317d, 0x561dceb7, 0x021a9bf1, 0x5228f955, 0x062fac13, + 0xfa2653d9, 0xae21069f, 0x07d9dabc, 0x53de8ffa, 0xafd77030, 0xfbd02576, + 0xf9cabe87, 0xadcdebc1, 0x51c4140b, 0x05c3414d, 0xac3b9d6e, 0xf83cc828, + 0x043537e2, 0x503262a4, 0xa451f2aa, 0xf056a7ec, 0x0c5f5826, 0x58580d60, + 0xf1a0d143, 0xa5a78405, 0x59ae7bcf, 0x0da92e89, 0x0fb3b578, 0x5bb4e03e, + 0xa7bd1ff4, 0xf3ba4ab2, 0x5a429691, 0x0e45c3d7, 0xf24c3c1d, 0xa64b695b, + 0xf6790bff, 0xa27e5eb9, 0x5e77a173, 0x0a70f435, 0xa3882816, 0xf78f7d50, + 0x0b86829a, 0x5f81d7dc, 0x5d9b4c2d, 0x099c196b, 0xf595e6a1, 0xa192b3e7, + 0x086a6fc4, 0x5c6d3a82, 0xa064c548, 0xf463900e, 0x4d4f93a5, 0x1948c6e3, + 0xe5413929, 0xb1466c6f, 0x18beb04c, 0x4cb9e50a, 0xb0b01ac0, 0xe4b74f86, + 0xe6add477, 0xb2aa8131, 0x4ea37efb, 0x1aa42bbd, 0xb35cf79e, 0xe75ba2d8, + 0x1b525d12, 0x4f550854, 0x1f676af0, 0x4b603fb6, 0xb769c07c, 0xe36e953a, + 0x4a964919, 0x1e911c5f, 0xe298e395, 0xb69fb6d3, 0xb4852d22, 0xe0827864, + 0x1c8b87ae, 0x488cd2e8, 0xe1740ecb, 0xb5735b8d, 0x497aa447, 0x1d7df101, + 0xe91e610f, 0xbd193449, 0x4110cb83, 0x15179ec5, 0xbcef42e6, 0xe8e817a0, + 0x14e1e86a, 0x40e6bd2c, 0x42fc26dd, 0x16fb739b, 0xeaf28c51, 0xbef5d917, + 0x170d0534, 0x430a5072, 0xbf03afb8, 0xeb04fafe, 0xbb36985a, 0xef31cd1c, + 0x133832d6, 0x473f6790, 0xeec7bbb3, 0xbac0eef5, 0x46c9113f, 0x12ce4479, + 0x10d4df88, 0x44d38ace, 0xb8da7504, 0xecdd2042, 0x4525fc61, 0x1122a927, + 0xed2b56ed, 0xb92c03ab, 0x9a9f274a, 0xce98720c, 0x32918dc6, 0x6696d880, + 0xcf6e04a3, 0x9b6951e5, 0x6760ae2f, 0x3367fb69, 0x317d6098, 0x657a35de, + 0x9973ca14, 0xcd749f52, 0x648c4371, 0x308b1637, 0xcc82e9fd, 0x9885bcbb, + 0xc8b7de1f, 0x9cb08b59, 0x60b97493, 0x34be21d5, 0x9d46fdf6, 0xc941a8b0, + 0x3548577a, 0x614f023c, 0x635599cd, 0x3752cc8b, 0xcb5b3341, 0x9f5c6607, + 0x36a4ba24, 0x62a3ef62, 0x9eaa10a8, 0xcaad45ee, 0x3eced5e0, 0x6ac980a6, + 0x96c07f6c, 0xc2c72a2a, 0x6b3ff609, 0x3f38a34f, 0xc3315c85, 0x973609c3, + 0x952c9232, 0xc12bc774, 0x3d2238be, 0x69256df8, 0xc0ddb1db, 0x94dae49d, + 0x68d31b57, 0x3cd44e11, 0x6ce62cb5, 0x38e179f3, 0xc4e88639, 0x90efd37f, + 0x39170f5c, 0x6d105a1a, 0x9119a5d0, 0xc51ef096, 0xc7046b67, 0x93033e21, + 0x6f0ac1eb, 0x3b0d94ad, 0x92f5488e, 0xc6f21dc8, 0x3afbe202, 0x6efcb744, + 0xd7d0b4ef, 0x83d7e1a9, 0x7fde1e63, 0x2bd94b25, 0x82219706, 0xd626c240, + 0x2a2f3d8a, 0x7e2868cc, 0x7c32f33d, 0x2835a67b, 0xd43c59b1, 0x803b0cf7, + 0x29c3d0d4, 0x7dc48592, 0x81cd7a58, 0xd5ca2f1e, 0x85f84dba, 0xd1ff18fc, + 0x2df6e736, 0x79f1b270, 0xd0096e53, 0x840e3b15, 0x7807c4df, 0x2c009199, + 0x2e1a0a68, 0x7a1d5f2e, 0x8614a0e4, 0xd213f5a2, 0x7beb2981, 0x2fec7cc7, + 0xd3e5830d, 0x87e2d64b, 0x73814645, 0x27861303, 0xdb8fecc9, 0x8f88b98f, + 0x267065ac, 0x727730ea, 0x8e7ecf20, 0xda799a66, 0xd8630197, 0x8c6454d1, + 0x706dab1b, 0x246afe5d, 0x8d92227e, 0xd9957738, 0x259c88f2, 0x719bddb4, + 0x21a9bf10, 0x75aeea56, 0x89a7159c, 0xdda040da, 0x74589cf9, 0x205fc9bf, + 0xdc563675, 0x88516333, 0x8a4bf8c2, 0xde4cad84, 0x2245524e, 0x76420708, + 0xdfbadb2b, 0x8bbd8e6d, 0x77b471a7, 0x23b324e1, + }, + { + 0x00000000, 0x678efd01, 0xcf1dfa02, 0xa8930703, 0x9bd782f5, 0xfc597ff4, + 0x54ca78f7, 0x334485f6, 0x3243731b, 0x55cd8e1a, 0xfd5e8919, 0x9ad07418, + 0xa994f1ee, 0xce1a0cef, 0x66890bec, 0x0107f6ed, 0x6486e636, 0x03081b37, + 0xab9b1c34, 0xcc15e135, 0xff5164c3, 0x98df99c2, 0x304c9ec1, 0x57c263c0, + 0x56c5952d, 0x314b682c, 0x99d86f2f, 0xfe56922e, 0xcd1217d8, 0xaa9cead9, + 0x020fedda, 0x658110db, 0xc90dcc6c, 0xae83316d, 0x0610366e, 0x619ecb6f, + 0x52da4e99, 0x3554b398, 0x9dc7b49b, 0xfa49499a, 0xfb4ebf77, 0x9cc04276, + 0x34534575, 0x53ddb874, 0x60993d82, 0x0717c083, 0xaf84c780, 0xc80a3a81, + 0xad8b2a5a, 0xca05d75b, 0x6296d058, 0x05182d59, 0x365ca8af, 0x51d255ae, + 0xf94152ad, 0x9ecfafac, 0x9fc85941, 0xf846a440, 0x50d5a343, 0x375b5e42, + 0x041fdbb4, 0x639126b5, 0xcb0221b6, 0xac8cdcb7, 0x97f7ee29, 0xf0791328, + 0x58ea142b, 0x3f64e92a, 0x0c206cdc, 0x6bae91dd, 0xc33d96de, 0xa4b36bdf, + 0xa5b49d32, 0xc23a6033, 0x6aa96730, 0x0d279a31, 0x3e631fc7, 0x59ede2c6, + 0xf17ee5c5, 0x96f018c4, 0xf371081f, 0x94fff51e, 0x3c6cf21d, 0x5be20f1c, + 0x68a68aea, 0x0f2877eb, 0xa7bb70e8, 0xc0358de9, 0xc1327b04, 0xa6bc8605, + 0x0e2f8106, 0x69a17c07, 0x5ae5f9f1, 0x3d6b04f0, 0x95f803f3, 0xf276fef2, + 0x5efa2245, 0x3974df44, 0x91e7d847, 0xf6692546, 0xc52da0b0, 0xa2a35db1, + 0x0a305ab2, 0x6dbea7b3, 0x6cb9515e, 0x0b37ac5f, 0xa3a4ab5c, 0xc42a565d, + 0xf76ed3ab, 0x90e02eaa, 0x387329a9, 0x5ffdd4a8, 0x3a7cc473, 0x5df23972, + 0xf5613e71, 0x92efc370, 0xa1ab4686, 0xc625bb87, 0x6eb6bc84, 0x09384185, + 0x083fb768, 0x6fb14a69, 0xc7224d6a, 0xa0acb06b, 0x93e8359d, 0xf466c89c, + 0x5cf5cf9f, 0x3b7b329e, 0x2a03aaa3, 0x4d8d57a2, 0xe51e50a1, 0x8290ada0, + 0xb1d42856, 0xd65ad557, 0x7ec9d254, 0x19472f55, 0x1840d9b8, 0x7fce24b9, + 0xd75d23ba, 0xb0d3debb, 0x83975b4d, 0xe419a64c, 0x4c8aa14f, 0x2b045c4e, + 0x4e854c95, 0x290bb194, 0x8198b697, 0xe6164b96, 0xd552ce60, 0xb2dc3361, + 0x1a4f3462, 0x7dc1c963, 0x7cc63f8e, 0x1b48c28f, 0xb3dbc58c, 0xd455388d, + 0xe711bd7b, 0x809f407a, 0x280c4779, 0x4f82ba78, 0xe30e66cf, 0x84809bce, + 0x2c139ccd, 0x4b9d61cc, 0x78d9e43a, 0x1f57193b, 0xb7c41e38, 0xd04ae339, + 0xd14d15d4, 0xb6c3e8d5, 0x1e50efd6, 0x79de12d7, 0x4a9a9721, 0x2d146a20, + 0x85876d23, 0xe2099022, 0x878880f9, 0xe0067df8, 0x48957afb, 0x2f1b87fa, + 0x1c5f020c, 0x7bd1ff0d, 0xd342f80e, 0xb4cc050f, 0xb5cbf3e2, 0xd2450ee3, + 0x7ad609e0, 0x1d58f4e1, 0x2e1c7117, 0x49928c16, 0xe1018b15, 0x868f7614, + 0xbdf4448a, 0xda7ab98b, 0x72e9be88, 0x15674389, 0x2623c67f, 0x41ad3b7e, + 0xe93e3c7d, 0x8eb0c17c, 0x8fb73791, 0xe839ca90, 0x40aacd93, 0x27243092, + 0x1460b564, 0x73ee4865, 0xdb7d4f66, 0xbcf3b267, 0xd972a2bc, 0xbefc5fbd, + 0x166f58be, 0x71e1a5bf, 0x42a52049, 0x252bdd48, 0x8db8da4b, 0xea36274a, + 0xeb31d1a7, 0x8cbf2ca6, 0x242c2ba5, 0x43a2d6a4, 0x70e65352, 0x1768ae53, + 0xbffba950, 0xd8755451, 0x74f988e6, 0x137775e7, 0xbbe472e4, 0xdc6a8fe5, + 0xef2e0a13, 0x88a0f712, 0x2033f011, 0x47bd0d10, 0x46bafbfd, 0x213406fc, + 0x89a701ff, 0xee29fcfe, 0xdd6d7908, 0xbae38409, 0x1270830a, 0x75fe7e0b, + 0x107f6ed0, 0x77f193d1, 0xdf6294d2, 0xb8ec69d3, 0x8ba8ec25, 0xec261124, + 0x44b51627, 0x233beb26, 0x223c1dcb, 0x45b2e0ca, 0xed21e7c9, 0x8aaf1ac8, + 0xb9eb9f3e, 0xde65623f, 0x76f6653c, 0x1178983d, + }, + { + 0x00000000, 0xf20c0dfe, 0xe1f46d0d, 0x13f860f3, 0xc604aceb, 0x3408a115, + 0x27f0c1e6, 0xd5fccc18, 0x89e52f27, 0x7be922d9, 0x6811422a, 0x9a1d4fd4, + 0x4fe183cc, 0xbded8e32, 0xae15eec1, 0x5c19e33f, 0x162628bf, 0xe42a2541, + 0xf7d245b2, 0x05de484c, 0xd0228454, 0x222e89aa, 0x31d6e959, 0xc3dae4a7, + 0x9fc30798, 0x6dcf0a66, 0x7e376a95, 0x8c3b676b, 0x59c7ab73, 0xabcba68d, + 0xb833c67e, 0x4a3fcb80, 0x2c4c517e, 0xde405c80, 0xcdb83c73, 0x3fb4318d, + 0xea48fd95, 0x1844f06b, 0x0bbc9098, 0xf9b09d66, 0xa5a97e59, 0x57a573a7, + 0x445d1354, 0xb6511eaa, 0x63add2b2, 0x91a1df4c, 0x8259bfbf, 0x7055b241, + 0x3a6a79c1, 0xc866743f, 0xdb9e14cc, 0x29921932, 0xfc6ed52a, 0x0e62d8d4, + 0x1d9ab827, 0xef96b5d9, 0xb38f56e6, 0x41835b18, 0x527b3beb, 0xa0773615, + 0x758bfa0d, 0x8787f7f3, 0x947f9700, 0x66739afe, 0x5898a2fc, 0xaa94af02, + 0xb96ccff1, 0x4b60c20f, 0x9e9c0e17, 0x6c9003e9, 0x7f68631a, 0x8d646ee4, + 0xd17d8ddb, 0x23718025, 0x3089e0d6, 0xc285ed28, 0x17792130, 0xe5752cce, + 0xf68d4c3d, 0x048141c3, 0x4ebe8a43, 0xbcb287bd, 0xaf4ae74e, 0x5d46eab0, + 0x88ba26a8, 0x7ab62b56, 0x694e4ba5, 0x9b42465b, 0xc75ba564, 0x3557a89a, + 0x26afc869, 0xd4a3c597, 0x015f098f, 0xf3530471, 0xe0ab6482, 0x12a7697c, + 0x74d4f382, 0x86d8fe7c, 0x95209e8f, 0x672c9371, 0xb2d05f69, 0x40dc5297, + 0x53243264, 0xa1283f9a, 0xfd31dca5, 0x0f3dd15b, 0x1cc5b1a8, 0xeec9bc56, + 0x3b35704e, 0xc9397db0, 0xdac11d43, 0x28cd10bd, 0x62f2db3d, 0x90fed6c3, + 0x8306b630, 0x710abbce, 0xa4f677d6, 0x56fa7a28, 0x45021adb, 0xb70e1725, + 0xeb17f41a, 0x191bf9e4, 0x0ae39917, 0xf8ef94e9, 0x2d1358f1, 0xdf1f550f, + 0xcce735fc, 0x3eeb3802, 0xb13145f8, 0x433d4806, 0x50c528f5, 0xa2c9250b, + 0x7735e913, 0x8539e4ed, 0x96c1841e, 0x64cd89e0, 0x38d46adf, 0xcad86721, + 0xd92007d2, 0x2b2c0a2c, 0xfed0c634, 0x0cdccbca, 0x1f24ab39, 0xed28a6c7, + 0xa7176d47, 0x551b60b9, 0x46e3004a, 0xb4ef0db4, 0x6113c1ac, 0x931fcc52, + 0x80e7aca1, 0x72eba15f, 0x2ef24260, 0xdcfe4f9e, 0xcf062f6d, 0x3d0a2293, + 0xe8f6ee8b, 0x1afae375, 0x09028386, 0xfb0e8e78, 0x9d7d1486, 0x6f711978, + 0x7c89798b, 0x8e857475, 0x5b79b86d, 0xa975b593, 0xba8dd560, 0x4881d89e, + 0x14983ba1, 0xe694365f, 0xf56c56ac, 0x07605b52, 0xd29c974a, 0x20909ab4, + 0x3368fa47, 0xc164f7b9, 0x8b5b3c39, 0x795731c7, 0x6aaf5134, 0x98a35cca, + 0x4d5f90d2, 0xbf539d2c, 0xacabfddf, 0x5ea7f021, 0x02be131e, 0xf0b21ee0, + 0xe34a7e13, 0x114673ed, 0xc4babff5, 0x36b6b20b, 0x254ed2f8, 0xd742df06, + 0xe9a9e704, 0x1ba5eafa, 0x085d8a09, 0xfa5187f7, 0x2fad4bef, 0xdda14611, + 0xce5926e2, 0x3c552b1c, 0x604cc823, 0x9240c5dd, 0x81b8a52e, 0x73b4a8d0, + 0xa64864c8, 0x54446936, 0x47bc09c5, 0xb5b0043b, 0xff8fcfbb, 0x0d83c245, + 0x1e7ba2b6, 0xec77af48, 0x398b6350, 0xcb876eae, 0xd87f0e5d, 0x2a7303a3, + 0x766ae09c, 0x8466ed62, 0x979e8d91, 0x6592806f, 0xb06e4c77, 0x42624189, + 0x519a217a, 0xa3962c84, 0xc5e5b67a, 0x37e9bb84, 0x2411db77, 0xd61dd689, + 0x03e11a91, 0xf1ed176f, 0xe215779c, 0x10197a62, 0x4c00995d, 0xbe0c94a3, + 0xadf4f450, 0x5ff8f9ae, 0x8a0435b6, 0x78083848, 0x6bf058bb, 0x99fc5545, + 0xd3c39ec5, 0x21cf933b, 0x3237f3c8, 0xc03bfe36, 0x15c7322e, 0xe7cb3fd0, + 0xf4335f23, 0x063f52dd, 0x5a26b1e2, 0xa82abc1c, 0xbbd2dcef, 0x49ded111, + 0x9c221d09, 0x6e2e10f7, 0x7dd67004, 0x8fda7dfa, + }, + }; + + // Value x picked so computeForWord(x) == ~0, found by exhaustive search. + static final int INVERSE_COMPUTE_FOR_WORD_OF_ALL_1S = 0xeee3ddcd; + + static int computeForWord(int word) { + return STRIDE_TABLE[3][word & 0xFF] + ^ STRIDE_TABLE[2][(word >>> 8) & 0xFF] + ^ STRIDE_TABLE[1][(word >>> 16) & 0xFF] + ^ STRIDE_TABLE[0][word >>> 24]; + } + + static int combine(int csum, int crc) { + csum ^= crc; + for (int i = 0; i < 4; i++) { + csum = (csum >>> 8) ^ BYTE_TABLE[csum & 0xFF]; + } + return csum; + } + + private static final ByteBuffer EMPTY = ByteBuffer.allocate(0); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..3d11179dc026f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/FarmHashFingerprint64.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/FarmHashFingerprint64.java new file mode 100644 index 0000000000000..b85d355e865f4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/FarmHashFingerprint64.java @@ -0,0 +1,222 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import static java.lang.Long.rotateRight; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.hash.LittleEndianByteArray.load32; +import static org.apache.iotdb.google.common.hash.LittleEndianByteArray.load64; + +/** + * Implementation of FarmHash Fingerprint64, an open-source fingerprinting algorithm for strings. + * + *

Its speed is comparable to CityHash64, and its quality of hashing is at least as good. + * + *

Note to maintainers: This implementation relies on signed arithmetic being bit-wise equivalent + * to unsigned arithmetic in all cases except: + * + *

    + *
  • comparisons (signed values can be negative) + *
  • division (avoided here) + *
  • shifting (right shift must be unsigned) + *
+ * + * @author Kyle Maddison + * @author Geoff Pike + */ +@ElementTypesAreNonnullByDefault +final class FarmHashFingerprint64 extends AbstractNonStreamingHashFunction { + static final HashFunction FARMHASH_FINGERPRINT_64 = new FarmHashFingerprint64(); + + // Some primes between 2^63 and 2^64 for various uses. + private static final long K0 = 0xc3a5c85c97cb3127L; + private static final long K1 = 0xb492b66fbe98f273L; + private static final long K2 = 0x9ae16a3b2f90404fL; + + @Override + public HashCode hashBytes(byte[] input, int off, int len) { + checkPositionIndexes(off, off + len, input.length); + return HashCode.fromLong(fingerprint(input, off, len)); + } + + @Override + public int bits() { + return 64; + } + + @Override + public String toString() { + return "Hashing.farmHashFingerprint64()"; + } + + // End of public functions. + + @VisibleForTesting + static long fingerprint(byte[] bytes, int offset, int length) { + if (length <= 32) { + if (length <= 16) { + return hashLength0to16(bytes, offset, length); + } else { + return hashLength17to32(bytes, offset, length); + } + } else if (length <= 64) { + return hashLength33To64(bytes, offset, length); + } else { + return hashLength65Plus(bytes, offset, length); + } + } + + private static long shiftMix(long val) { + return val ^ (val >>> 47); + } + + private static long hashLength16(long u, long v, long mul) { + long a = (u ^ v) * mul; + a ^= (a >>> 47); + long b = (v ^ a) * mul; + b ^= (b >>> 47); + b *= mul; + return b; + } + + /** + * Computes intermediate hash of 32 bytes of byte array from the given offset. Results are + * returned in the output array because when we last measured, this was 12% faster than allocating + * new arrays every time. + */ + private static void weakHashLength32WithSeeds( + byte[] bytes, int offset, long seedA, long seedB, long[] output) { + long part1 = load64(bytes, offset); + long part2 = load64(bytes, offset + 8); + long part3 = load64(bytes, offset + 16); + long part4 = load64(bytes, offset + 24); + + seedA += part1; + seedB = rotateRight(seedB + seedA + part4, 21); + long c = seedA; + seedA += part2; + seedA += part3; + seedB += rotateRight(seedA, 44); + output[0] = seedA + part4; + output[1] = seedB + c; + } + + private static long hashLength0to16(byte[] bytes, int offset, int length) { + if (length >= 8) { + long mul = K2 + length * 2L; + long a = load64(bytes, offset) + K2; + long b = load64(bytes, offset + length - 8); + long c = rotateRight(b, 37) * mul + a; + long d = (rotateRight(a, 25) + b) * mul; + return hashLength16(c, d, mul); + } + if (length >= 4) { + long mul = K2 + length * 2; + long a = load32(bytes, offset) & 0xFFFFFFFFL; + return hashLength16(length + (a << 3), load32(bytes, offset + length - 4) & 0xFFFFFFFFL, mul); + } + if (length > 0) { + byte a = bytes[offset]; + byte b = bytes[offset + (length >> 1)]; + byte c = bytes[offset + (length - 1)]; + int y = (a & 0xFF) + ((b & 0xFF) << 8); + int z = length + ((c & 0xFF) << 2); + return shiftMix(y * K2 ^ z * K0) * K2; + } + return K2; + } + + private static long hashLength17to32(byte[] bytes, int offset, int length) { + long mul = K2 + length * 2L; + long a = load64(bytes, offset) * K1; + long b = load64(bytes, offset + 8); + long c = load64(bytes, offset + length - 8) * mul; + long d = load64(bytes, offset + length - 16) * K2; + return hashLength16( + rotateRight(a + b, 43) + rotateRight(c, 30) + d, a + rotateRight(b + K2, 18) + c, mul); + } + + private static long hashLength33To64(byte[] bytes, int offset, int length) { + long mul = K2 + length * 2L; + long a = load64(bytes, offset) * K2; + long b = load64(bytes, offset + 8); + long c = load64(bytes, offset + length - 8) * mul; + long d = load64(bytes, offset + length - 16) * K2; + long y = rotateRight(a + b, 43) + rotateRight(c, 30) + d; + long z = hashLength16(y, a + rotateRight(b + K2, 18) + c, mul); + long e = load64(bytes, offset + 16) * mul; + long f = load64(bytes, offset + 24); + long g = (y + load64(bytes, offset + length - 32)) * mul; + long h = (z + load64(bytes, offset + length - 24)) * mul; + return hashLength16( + rotateRight(e + f, 43) + rotateRight(g, 30) + h, e + rotateRight(f + a, 18) + g, mul); + } + + /* + * Compute an 8-byte hash of a byte array of length greater than 64 bytes. + */ + private static long hashLength65Plus(byte[] bytes, int offset, int length) { + int seed = 81; + // For strings over 64 bytes we loop. Internal state consists of 56 bytes: v, w, x, y, and z. + long x = seed; + @SuppressWarnings("ConstantOverflow") + long y = seed * K1 + 113; + long z = shiftMix(y * K2 + 113) * K2; + long[] v = new long[2]; + long[] w = new long[2]; + x = x * K2 + load64(bytes, offset); + + // Set end so that after the loop we have 1 to 64 bytes left to process. + int end = offset + ((length - 1) / 64) * 64; + int last64offset = end + ((length - 1) & 63) - 63; + do { + x = rotateRight(x + y + v[0] + load64(bytes, offset + 8), 37) * K1; + y = rotateRight(y + v[1] + load64(bytes, offset + 48), 42) * K1; + x ^= w[1]; + y += v[0] + load64(bytes, offset + 40); + z = rotateRight(z + w[0], 33) * K1; + weakHashLength32WithSeeds(bytes, offset, v[1] * K1, x + w[0], v); + weakHashLength32WithSeeds(bytes, offset + 32, z + w[1], y + load64(bytes, offset + 16), w); + long tmp = x; + x = z; + z = tmp; + offset += 64; + } while (offset != end); + long mul = K1 + ((z & 0xFF) << 1); + // Operate on the last 64 bytes of input. + offset = last64offset; + w[0] += ((length - 1) & 63); + v[0] += w[0]; + w[0] += v[0]; + x = rotateRight(x + y + v[0] + load64(bytes, offset + 8), 37) * mul; + y = rotateRight(y + v[1] + load64(bytes, offset + 48), 42) * mul; + x ^= w[1] * 9; + y += v[0] * 9 + load64(bytes, offset + 40); + z = rotateRight(z + w[0], 33) * mul; + weakHashLength32WithSeeds(bytes, offset, v[1] * mul, x + w[0], v); + weakHashLength32WithSeeds(bytes, offset + 32, z + w[1], y + load64(bytes, offset + 16), w); + return hashLength16( + hashLength16(v[0], w[0], mul) + shiftMix(y) * K0 + x, + hashLength16(v[1], w[1], mul) + z, + mul); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Fingerprint2011.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Fingerprint2011.java new file mode 100644 index 0000000000000..ff509c3ae46ed --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Fingerprint2011.java @@ -0,0 +1,215 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import static java.lang.Long.rotateRight; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.hash.LittleEndianByteArray.load64; +import static org.apache.iotdb.google.common.hash.LittleEndianByteArray.load64Safely; + +/** + * Implementation of Geoff Pike's fingerprint2011 hash function. See {@link Hashing#fingerprint2011} + * for information on the behaviour of the algorithm. + * + *

On Intel Core2 2.66, on 1000 bytes, fingerprint2011 takes 0.9 microseconds compared to + * fingerprint at 4.0 microseconds and md5 at 4.5 microseconds. + * + *

Note to maintainers: This implementation relies on signed arithmetic being bit-wise equivalent + * to unsigned arithmetic in all cases except: + * + *

    + *
  • comparisons (signed values can be negative) + *
  • division (avoided here) + *
  • shifting (right shift must be unsigned) + *
+ * + * @author kylemaddison@google.com (Kyle Maddison) + * @author gpike@google.com (Geoff Pike) + */ +@ElementTypesAreNonnullByDefault +final class Fingerprint2011 extends AbstractNonStreamingHashFunction { + static final HashFunction FINGERPRINT_2011 = new Fingerprint2011(); + + // Some primes between 2^63 and 2^64 for various uses. + private static final long K0 = 0xa5b85c5e198ed849L; + private static final long K1 = 0x8d58ac26afe12e47L; + private static final long K2 = 0xc47b6e9e3a970ed3L; + private static final long K3 = 0xc6a4a7935bd1e995L; + + @Override + public HashCode hashBytes(byte[] input, int off, int len) { + checkPositionIndexes(off, off + len, input.length); + return HashCode.fromLong(fingerprint(input, off, len)); + } + + @Override + public int bits() { + return 64; + } + + @Override + public String toString() { + return "Hashing.fingerprint2011()"; + } + + // End of public functions. + + @VisibleForTesting + static long fingerprint(byte[] bytes, int offset, int length) { + long result; + + if (length <= 32) { + result = murmurHash64WithSeed(bytes, offset, length, K0 ^ K1 ^ K2); + } else if (length <= 64) { + result = hashLength33To64(bytes, offset, length); + } else { + result = fullFingerprint(bytes, offset, length); + } + + long u = length >= 8 ? load64(bytes, offset) : K0; + long v = length >= 9 ? load64(bytes, offset + length - 8) : K0; + result = hash128to64(result + v, u); + return result == 0 || result == 1 ? result + ~1 : result; + } + + private static long shiftMix(long val) { + return val ^ (val >>> 47); + } + + /** Implementation of Hash128to64 from util/hash/hash128to64.h */ + @VisibleForTesting + static long hash128to64(long high, long low) { + long a = (low ^ high) * K3; + a ^= (a >>> 47); + long b = (high ^ a) * K3; + b ^= (b >>> 47); + b *= K3; + return b; + } + + /** + * Computes intermediate hash of 32 bytes of byte array from the given offset. Results are + * returned in the output array - this is 12% faster than allocating new arrays every time. + */ + private static void weakHashLength32WithSeeds( + byte[] bytes, int offset, long seedA, long seedB, long[] output) { + long part1 = load64(bytes, offset); + long part2 = load64(bytes, offset + 8); + long part3 = load64(bytes, offset + 16); + long part4 = load64(bytes, offset + 24); + + seedA += part1; + seedB = rotateRight(seedB + seedA + part4, 51); + long c = seedA; + seedA += part2; + seedA += part3; + seedB += rotateRight(seedA, 23); + output[0] = seedA + part4; + output[1] = seedB + c; + } + + /* + * Compute an 8-byte hash of a byte array of length greater than 64 bytes. + */ + private static long fullFingerprint(byte[] bytes, int offset, int length) { + // For lengths over 64 bytes we hash the end first, and then as we + // loop we keep 56 bytes of state: v, w, x, y, and z. + long x = load64(bytes, offset); + long y = load64(bytes, offset + length - 16) ^ K1; + long z = load64(bytes, offset + length - 56) ^ K0; + long[] v = new long[2]; + long[] w = new long[2]; + weakHashLength32WithSeeds(bytes, offset + length - 64, length, y, v); + weakHashLength32WithSeeds(bytes, offset + length - 32, length * K1, K0, w); + z += shiftMix(v[1]) * K1; + x = rotateRight(z + x, 39) * K1; + y = rotateRight(y, 33) * K1; + + // Decrease length to the nearest multiple of 64, and operate on 64-byte chunks. + length = (length - 1) & ~63; + do { + x = rotateRight(x + y + v[0] + load64(bytes, offset + 16), 37) * K1; + y = rotateRight(y + v[1] + load64(bytes, offset + 48), 42) * K1; + x ^= w[1]; + y ^= v[0]; + z = rotateRight(z ^ w[0], 33); + weakHashLength32WithSeeds(bytes, offset, v[1] * K1, x + w[0], v); + weakHashLength32WithSeeds(bytes, offset + 32, z + w[1], y, w); + long tmp = z; + z = x; + x = tmp; + offset += 64; + length -= 64; + } while (length != 0); + return hash128to64(hash128to64(v[0], w[0]) + shiftMix(y) * K1 + z, hash128to64(v[1], w[1]) + x); + } + + private static long hashLength33To64(byte[] bytes, int offset, int length) { + long z = load64(bytes, offset + 24); + long a = load64(bytes, offset) + (length + load64(bytes, offset + length - 16)) * K0; + long b = rotateRight(a + z, 52); + long c = rotateRight(a, 37); + a += load64(bytes, offset + 8); + c += rotateRight(a, 7); + a += load64(bytes, offset + 16); + long vf = a + z; + long vs = b + rotateRight(a, 31) + c; + a = load64(bytes, offset + 16) + load64(bytes, offset + length - 32); + z = load64(bytes, offset + length - 8); + b = rotateRight(a + z, 52); + c = rotateRight(a, 37); + a += load64(bytes, offset + length - 24); + c += rotateRight(a, 7); + a += load64(bytes, offset + length - 16); + long wf = a + z; + long ws = b + rotateRight(a, 31) + c; + long r = shiftMix((vf + ws) * K2 + (wf + vs) * K0); + return shiftMix(r * K0 + vs) * K2; + } + + @VisibleForTesting + static long murmurHash64WithSeed(byte[] bytes, int offset, int length, long seed) { + long mul = K3; + int topBit = 0x7; + + int lengthAligned = length & ~topBit; + int lengthRemainder = length & topBit; + long hash = seed ^ (length * mul); + + for (int i = 0; i < lengthAligned; i += 8) { + long loaded = load64(bytes, offset + i); + long data = shiftMix(loaded * mul) * mul; + hash ^= data; + hash *= mul; + } + + if (lengthRemainder != 0) { + long data = load64Safely(bytes, offset + lengthAligned, lengthRemainder); + hash ^= data; + hash *= mul; + } + + hash = shiftMix(hash) * mul; + hash = shiftMix(hash); + return hash; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Funnel.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Funnel.java new file mode 100644 index 0000000000000..d394674b1bc1a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Funnel.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.io.Serializable; + +/** + * An object which can send data from an object of type {@code T} into a {@code PrimitiveSink}. + * Implementations for common types can be found in {@link Funnels}. + * + *

Note that serialization of {@linkplain BloomFilter bloom filters} requires the proper + * serialization of funnels. When possible, it is recommended that funnels be implemented as a + * single-element enum to maintain serialization guarantees. See Effective Java (2nd Edition), Item + * 3: "Enforce the singleton property with a private constructor or an enum type". For example: + * + *

{@code
+ * public enum PersonFunnel implements Funnel {
+ *   INSTANCE;
+ *   public void funnel(Person person, PrimitiveSink into) {
+ *     into.putUnencodedChars(person.getFirstName())
+ *         .putUnencodedChars(person.getLastName())
+ *         .putInt(person.getAge());
+ *   }
+ * }
+ * }
+ * + * @author Dimitris Andreou + * @since 11.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface Funnel extends Serializable { + + /** + * Sends a stream of data from the {@code from} object into the sink {@code into}. There is no + * requirement that this data be complete enough to fully reconstitute the object later. + * + * @since 12.0 (in Guava 11.0, {@code PrimitiveSink} was named {@code Sink}) + */ + void funnel(@ParametricNullness T from, PrimitiveSink into); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Funnels.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Funnels.java new file mode 100644 index 0000000000000..bb5dcda617c5a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Funnels.java @@ -0,0 +1,283 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.nio.charset.Charset; + +/** + * Funnels for common types. All implementations are serializable. + * + * @author Dimitris Andreou + * @since 11.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class Funnels { + private Funnels() {} + + /** Returns a funnel that extracts the bytes from a {@code byte} array. */ + public static Funnel byteArrayFunnel() { + return ByteArrayFunnel.INSTANCE; + } + + private enum ByteArrayFunnel implements Funnel { + INSTANCE; + + @Override + public void funnel(byte[] from, PrimitiveSink into) { + into.putBytes(from); + } + + @Override + public String toString() { + return "Funnels.byteArrayFunnel()"; + } + } + + /** + * Returns a funnel that extracts the characters from a {@code CharSequence}, a character at a + * time, without performing any encoding. If you need to use a specific encoding, use {@link + * Funnels#stringFunnel(Charset)} instead. + * + * @since 15.0 (since 11.0 as {@code Funnels.stringFunnel()}. + */ + public static Funnel unencodedCharsFunnel() { + return UnencodedCharsFunnel.INSTANCE; + } + + private enum UnencodedCharsFunnel implements Funnel { + INSTANCE; + + @Override + public void funnel(CharSequence from, PrimitiveSink into) { + into.putUnencodedChars(from); + } + + @Override + public String toString() { + return "Funnels.unencodedCharsFunnel()"; + } + } + + /** + * Returns a funnel that encodes the characters of a {@code CharSequence} with the specified + * {@code Charset}. + * + * @since 15.0 + */ + public static Funnel stringFunnel(Charset charset) { + return new StringCharsetFunnel(charset); + } + + private static class StringCharsetFunnel implements Funnel, Serializable { + private final Charset charset; + + StringCharsetFunnel(Charset charset) { + this.charset = Preconditions.checkNotNull(charset); + } + + @Override + public void funnel(CharSequence from, PrimitiveSink into) { + into.putString(from, charset); + } + + @Override + public String toString() { + return "Funnels.stringFunnel(" + charset.name() + ")"; + } + + @Override + public boolean equals(Object o) { + if (o instanceof StringCharsetFunnel) { + StringCharsetFunnel funnel = (StringCharsetFunnel) o; + return this.charset.equals(funnel.charset); + } + return false; + } + + @Override + public int hashCode() { + return StringCharsetFunnel.class.hashCode() ^ charset.hashCode(); + } + + Object writeReplace() { + return new SerializedForm(charset); + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + private static class SerializedForm implements Serializable { + private final String charsetCanonicalName; + + SerializedForm(Charset charset) { + this.charsetCanonicalName = charset.name(); + } + + private Object readResolve() { + return stringFunnel(Charset.forName(charsetCanonicalName)); + } + + private static final long serialVersionUID = 0; + } + } + + /** + * Returns a funnel for integers. + * + * @since 13.0 + */ + public static Funnel integerFunnel() { + return IntegerFunnel.INSTANCE; + } + + private enum IntegerFunnel implements Funnel { + INSTANCE; + + @Override + public void funnel(Integer from, PrimitiveSink into) { + into.putInt(from); + } + + @Override + public String toString() { + return "Funnels.integerFunnel()"; + } + } + + /** + * Returns a funnel that processes an {@code Iterable} by funneling its elements in iteration + * order with the specified funnel. No separators are added between the elements. + * + * @since 15.0 + */ + public static Funnel> sequentialFunnel( + Funnel elementFunnel) { + return new SequentialFunnel<>(elementFunnel); + } + + private static class SequentialFunnel + implements Funnel>, Serializable { + private final Funnel elementFunnel; + + SequentialFunnel(Funnel elementFunnel) { + this.elementFunnel = Preconditions.checkNotNull(elementFunnel); + } + + @Override + public void funnel(Iterable from, PrimitiveSink into) { + for (E e : from) { + elementFunnel.funnel(e, into); + } + } + + @Override + public String toString() { + return "Funnels.sequentialFunnel(" + elementFunnel + ")"; + } + + @Override + public boolean equals(Object o) { + if (o instanceof SequentialFunnel) { + SequentialFunnel funnel = (SequentialFunnel) o; + return elementFunnel.equals(funnel.elementFunnel); + } + return false; + } + + @Override + public int hashCode() { + return SequentialFunnel.class.hashCode() ^ elementFunnel.hashCode(); + } + } + + /** + * Returns a funnel for longs. + * + * @since 13.0 + */ + public static Funnel longFunnel() { + return LongFunnel.INSTANCE; + } + + private enum LongFunnel implements Funnel { + INSTANCE; + + @Override + public void funnel(Long from, PrimitiveSink into) { + into.putLong(from); + } + + @Override + public String toString() { + return "Funnels.longFunnel()"; + } + } + + /** + * Wraps a {@code PrimitiveSink} as an {@link OutputStream}, so it is easy to {@link Funnel#funnel + * funnel} an object to a {@code PrimitiveSink} if there is already a way to write the contents of + * the object to an {@code OutputStream}. + * + *

The {@code close} and {@code flush} methods of the returned {@code OutputStream} do nothing, + * and no method throws {@code IOException}. + * + * @since 13.0 + */ + public static OutputStream asOutputStream(PrimitiveSink sink) { + return new SinkAsStream(sink); + } + + private static class SinkAsStream extends OutputStream { + final PrimitiveSink sink; + + SinkAsStream(PrimitiveSink sink) { + this.sink = Preconditions.checkNotNull(sink); + } + + @Override + public void write(int b) { + sink.putByte((byte) b); + } + + @Override + public void write(byte[] bytes) { + sink.putBytes(bytes); + } + + @Override + public void write(byte[] bytes, int off, int len) { + sink.putBytes(bytes, off, len); + } + + @Override + public String toString() { + return "Funnels.asOutputStream(" + sink + ")"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashCode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashCode.java new file mode 100644 index 0000000000000..e78f72a23955c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashCode.java @@ -0,0 +1,426 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.primitives.UnsignedInts; + +import java.io.Serializable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An immutable hash code of arbitrary bit length. + * + * @author Dimitris Andreou + * @author Kurt Alfred Kluever + * @since 11.0 + */ +@ElementTypesAreNonnullByDefault +public abstract class HashCode { + HashCode() {} + + /** Returns the number of bits in this hash code; a positive multiple of 8. */ + public abstract int bits(); + + /** + * Returns the first four bytes of {@linkplain #asBytes() this hashcode's bytes}, converted to an + * {@code int} value in little-endian order. + * + * @throws IllegalStateException if {@code bits() < 32} + */ + public abstract int asInt(); + + /** + * Returns the first eight bytes of {@linkplain #asBytes() this hashcode's bytes}, converted to a + * {@code long} value in little-endian order. + * + * @throws IllegalStateException if {@code bits() < 64} + */ + public abstract long asLong(); + + /** + * If this hashcode has enough bits, returns {@code asLong()}, otherwise returns a {@code long} + * value with {@code asBytes()} as the least-significant bytes and {@code 0x00} as the remaining + * most-significant bytes. + * + * @since 14.0 (since 11.0 as {@code Hashing.padToLong(HashCode)}) + */ + public abstract long padToLong(); + + /** + * Returns the value of this hash code as a byte array. The caller may modify the byte array; + * changes to it will not be reflected in this {@code HashCode} object or any other arrays + * returned by this method. + */ + // TODO(user): consider ByteString here, when that is available + public abstract byte[] asBytes(); + + /** + * Copies bytes from this hash code into {@code dest}. + * + * @param dest the byte array into which the hash code will be written + * @param offset the start offset in the data + * @param maxLength the maximum number of bytes to write + * @return the number of bytes written to {@code dest} + * @throws IndexOutOfBoundsException if there is not enough room in {@code dest} + */ + public int writeBytesTo(byte[] dest, int offset, int maxLength) { + maxLength = Ints.min(maxLength, bits() / 8); + Preconditions.checkPositionIndexes(offset, offset + maxLength, dest.length); + writeBytesToImpl(dest, offset, maxLength); + return maxLength; + } + + abstract void writeBytesToImpl(byte[] dest, int offset, int maxLength); + + /** + * Returns a mutable view of the underlying bytes for the given {@code HashCode} if it is a + * byte-based hashcode. Otherwise it returns {@link HashCode#asBytes}. Do not mutate this + * array or else you will break the immutability contract of {@code HashCode}. + */ + byte[] getBytesInternal() { + return asBytes(); + } + + /** + * Returns whether this {@code HashCode} and that {@code HashCode} have the same value, given that + * they have the same number of bits. + */ + abstract boolean equalsSameBits(HashCode that); + + /** + * Creates a 32-bit {@code HashCode} representation of the given int value. The underlying bytes + * are interpreted in little endian order. + * + * @since 15.0 (since 12.0 in HashCodes) + */ + public static HashCode fromInt(int hash) { + return new IntHashCode(hash); + } + + private static final class IntHashCode extends HashCode implements Serializable { + final int hash; + + IntHashCode(int hash) { + this.hash = hash; + } + + @Override + public int bits() { + return 32; + } + + @Override + public byte[] asBytes() { + return new byte[] {(byte) hash, (byte) (hash >> 8), (byte) (hash >> 16), (byte) (hash >> 24)}; + } + + @Override + public int asInt() { + return hash; + } + + @Override + public long asLong() { + throw new IllegalStateException("this HashCode only has 32 bits; cannot create a long"); + } + + @Override + public long padToLong() { + return UnsignedInts.toLong(hash); + } + + @Override + void writeBytesToImpl(byte[] dest, int offset, int maxLength) { + for (int i = 0; i < maxLength; i++) { + dest[offset + i] = (byte) (hash >> (i * 8)); + } + } + + @Override + boolean equalsSameBits(HashCode that) { + return hash == that.asInt(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates a 64-bit {@code HashCode} representation of the given long value. The underlying bytes + * are interpreted in little endian order. + * + * @since 15.0 (since 12.0 in HashCodes) + */ + public static HashCode fromLong(long hash) { + return new LongHashCode(hash); + } + + private static final class LongHashCode extends HashCode implements Serializable { + final long hash; + + LongHashCode(long hash) { + this.hash = hash; + } + + @Override + public int bits() { + return 64; + } + + @Override + public byte[] asBytes() { + return new byte[] { + (byte) hash, + (byte) (hash >> 8), + (byte) (hash >> 16), + (byte) (hash >> 24), + (byte) (hash >> 32), + (byte) (hash >> 40), + (byte) (hash >> 48), + (byte) (hash >> 56) + }; + } + + @Override + public int asInt() { + return (int) hash; + } + + @Override + public long asLong() { + return hash; + } + + @Override + public long padToLong() { + return hash; + } + + @Override + void writeBytesToImpl(byte[] dest, int offset, int maxLength) { + for (int i = 0; i < maxLength; i++) { + dest[offset + i] = (byte) (hash >> (i * 8)); + } + } + + @Override + boolean equalsSameBits(HashCode that) { + return hash == that.asLong(); + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates a {@code HashCode} from a byte array. The array is defensively copied to preserve the + * immutability contract of {@code HashCode}. The array cannot be empty. + * + * @since 15.0 (since 12.0 in HashCodes) + */ + public static HashCode fromBytes(byte[] bytes) { + checkArgument(bytes.length >= 1, "A HashCode must contain at least 1 byte."); + return fromBytesNoCopy(bytes.clone()); + } + + /** + * Creates a {@code HashCode} from a byte array. The array is not copied defensively, so it + * must be handed-off so as to preserve the immutability contract of {@code HashCode}. + */ + static HashCode fromBytesNoCopy(byte[] bytes) { + return new BytesHashCode(bytes); + } + + private static final class BytesHashCode extends HashCode implements Serializable { + final byte[] bytes; + + BytesHashCode(byte[] bytes) { + this.bytes = checkNotNull(bytes); + } + + @Override + public int bits() { + return bytes.length * 8; + } + + @Override + public byte[] asBytes() { + return bytes.clone(); + } + + @Override + public int asInt() { + checkState( + bytes.length >= 4, + "HashCode#asInt() requires >= 4 bytes (it only has %s bytes).", + bytes.length); + return (bytes[0] & 0xFF) + | ((bytes[1] & 0xFF) << 8) + | ((bytes[2] & 0xFF) << 16) + | ((bytes[3] & 0xFF) << 24); + } + + @Override + public long asLong() { + checkState( + bytes.length >= 8, + "HashCode#asLong() requires >= 8 bytes (it only has %s bytes).", + bytes.length); + return padToLong(); + } + + @Override + public long padToLong() { + long retVal = (bytes[0] & 0xFF); + for (int i = 1; i < Math.min(bytes.length, 8); i++) { + retVal |= (bytes[i] & 0xFFL) << (i * 8); + } + return retVal; + } + + @Override + void writeBytesToImpl(byte[] dest, int offset, int maxLength) { + System.arraycopy(bytes, 0, dest, offset, maxLength); + } + + @Override + byte[] getBytesInternal() { + return bytes; + } + + @Override + boolean equalsSameBits(HashCode that) { + // We don't use MessageDigest.isEqual() here because its contract does not guarantee + // constant-time evaluation (no short-circuiting). + if (this.bytes.length != that.getBytesInternal().length) { + return false; + } + + boolean areEqual = true; + for (int i = 0; i < this.bytes.length; i++) { + areEqual &= (this.bytes[i] == that.getBytesInternal()[i]); + } + return areEqual; + } + + private static final long serialVersionUID = 0; + } + + /** + * Creates a {@code HashCode} from a hexadecimal ({@code base 16}) encoded string. The string must + * be at least 2 characters long, and contain only valid, lower-cased hexadecimal characters. + * + *

This method accepts the exact format generated by {@link #toString}. If you require more + * lenient {@code base 16} decoding, please use {@link + * org.apache.iotdb.google.common.io.BaseEncoding#decode} (and pass the result to {@link + * #fromBytes}). + * + * @since 15.0 + */ + public static HashCode fromString(String string) { + checkArgument( + string.length() >= 2, "input string (%s) must have at least 2 characters", string); + checkArgument( + string.length() % 2 == 0, + "input string (%s) must have an even number of characters", + string); + + byte[] bytes = new byte[string.length() / 2]; + for (int i = 0; i < string.length(); i += 2) { + int ch1 = decode(string.charAt(i)) << 4; + int ch2 = decode(string.charAt(i + 1)); + bytes[i / 2] = (byte) (ch1 + ch2); + } + return fromBytesNoCopy(bytes); + } + + private static int decode(char ch) { + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } + if (ch >= 'a' && ch <= 'f') { + return ch - 'a' + 10; + } + throw new IllegalArgumentException("Illegal hexadecimal character: " + ch); + } + + /** + * Returns {@code true} if {@code object} is a {@link HashCode} instance with the identical byte + * representation to this hash code. + * + *

Security note: this method uses a constant-time (not short-circuiting) implementation + * to protect against timing attacks. + */ + @Override + public final boolean equals(Object object) { + if (object instanceof HashCode) { + HashCode that = (HashCode) object; + return bits() == that.bits() && equalsSameBits(that); + } + return false; + } + + /** + * Returns a "Java hash code" for this {@code HashCode} instance; this is well-defined (so, for + * example, you can safely put {@code HashCode} instances into a {@code HashSet}) but is otherwise + * probably not what you want to use. + */ + @Override + public final int hashCode() { + // If we have at least 4 bytes (32 bits), just take the first 4 bytes. Since this is + // already a (presumably) high-quality hash code, any four bytes of it will do. + if (bits() >= 32) { + return asInt(); + } + // If we have less than 4 bytes, use them all. + byte[] bytes = getBytesInternal(); + int val = (bytes[0] & 0xFF); + for (int i = 1; i < bytes.length; i++) { + val |= ((bytes[i] & 0xFF) << (i * 8)); + } + return val; + } + + /** + * Returns a string containing each byte of {@link #asBytes}, in order, as a two-digit unsigned + * hexadecimal number in lower case. + * + *

Note that if the output is considered to be a single hexadecimal number, whether this string + * is big-endian or little-endian depends on the byte order of {@link #asBytes}. This may be + * surprising for implementations of {@code HashCode} that represent the number in big-endian + * since everything else in the hashing API uniformly treats multibyte values as little-endian. + * + *

To create a {@code HashCode} from its string representation, see {@link #fromString}. + */ + @Override + public final String toString() { + byte[] bytes = getBytesInternal(); + StringBuilder sb = new StringBuilder(2 * bytes.length); + for (byte b : bytes) { + sb.append(hexDigits[(b >> 4) & 0xf]).append(hexDigits[b & 0xf]); + } + return sb.toString(); + } + + private static final char[] hexDigits = "0123456789abcdef".toCharArray(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashFunction.java new file mode 100644 index 0000000000000..cb8ca079be1da --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashFunction.java @@ -0,0 +1,224 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.primitives.Ints; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * A hash function is a collision-averse pure function that maps an arbitrary block of data to a + * number called a hash code. + * + *

Definition

+ * + *

Unpacking this definition: + * + *

    + *
  • block of data: the input for a hash function is always, in concept, an ordered byte + * array. This hashing API accepts an arbitrary sequence of byte and multibyte values (via + * {@link Hasher}), but this is merely a convenience; these are always translated into raw + * byte sequences under the covers. + *
  • hash code: each hash function always yields hash codes of the same fixed bit length + * (given by {@link #bits}). For example, {@link Hashing#sha1} produces a 160-bit number, + * while {@link Hashing#murmur3_32()} yields only 32 bits. Because a {@code long} value is + * clearly insufficient to hold all hash code values, this API represents a hash code as an + * instance of {@link HashCode}. + *
  • pure function: the value produced must depend only on the input bytes, in the order + * they appear. Input data is never modified. {@link HashFunction} instances should always be + * stateless, and therefore thread-safe. + *
  • collision-averse: while it can't be helped that a hash function will sometimes + * produce the same hash code for distinct inputs (a "collision"), every hash function strives + * to some degree to make this unlikely. (Without this condition, a function that + * always returns zero could be called a hash function. It is not.) + *
+ * + *

Summarizing the last two points: "equal yield equal always; unequal yield unequal + * often." This is the most important characteristic of all hash functions. + * + *

Desirable properties

+ * + *

A high-quality hash function strives for some subset of the following virtues: + * + *

    + *
  • collision-resistant: while the definition above requires making at least some + * token attempt, one measure of the quality of a hash function is how well it succeeds + * at this goal. Important note: it may be easy to achieve the theoretical minimum collision + * rate when using completely random sample input. The true test of a hash function is + * how it performs on representative real-world data, which tends to contain many hidden + * patterns and clumps. The goal of a good hash function is to stamp these patterns out as + * thoroughly as possible. + *
  • bit-dispersing: masking out any single bit from a hash code should yield only + * the expected twofold increase to all collision rates. Informally, the "information" + * in the hash code should be as evenly "spread out" through the hash code's bits as possible. + * The result is that, for example, when choosing a bucket in a hash table of size 2^8, + * any eight bits could be consistently used. + *
  • cryptographic: certain hash functions such as {@link Hashing#sha512} are designed to + * make it as infeasible as possible to reverse-engineer the input that produced a given hash + * code, or even to discover any two distinct inputs that yield the same result. These + * are called cryptographic hash functions. But, whenever it is learned that either of + * these feats has become computationally feasible, the function is deemed "broken" and should + * no longer be used for secure purposes. (This is the likely eventual fate of all + * cryptographic hashes.) + *
  • fast: perhaps self-explanatory, but often the most important consideration. + *
+ * + *

Providing input to a hash function

+ * + *

The primary way to provide the data that your hash function should act on is via a {@link + * Hasher}. Obtain a new hasher from the hash function using {@link #newHasher}, "push" the relevant + * data into it using methods like {@link Hasher#putBytes(byte[])}, and finally ask for the {@code + * HashCode} when finished using {@link Hasher#hash}. (See an {@linkplain #newHasher example} of + * this.) + * + *

If all you want to hash is a single byte array, string or {@code long} value, there are + * convenient shortcut methods defined directly on {@link HashFunction} to make this easier. + * + *

Hasher accepts primitive data types, but can also accept any Object of type {@code T} provided + * that you implement a {@link Funnel}{@code } to specify how to "feed" data from that object + * into the function. (See {@linkplain Hasher#putObject an example} of this.) + * + *

Compatibility note: Throughout this API, multibyte values are always interpreted in + * little-endian order. That is, hashing the byte array {@code {0x01, 0x02, 0x03, 0x04}} is + * equivalent to hashing the {@code int} value {@code 0x04030201}. If this isn't what you need, + * methods such as {@link Integer#reverseBytes} and {@link Ints#toByteArray} will help. + * + *

Relationship to {@link Object#hashCode}

+ * + *

Java's baked-in concept of hash codes is constrained to 32 bits, and provides no separation + * between hash algorithms and the data they act on, so alternate hash algorithms can't be easily + * substituted. Also, implementations of {@code hashCode} tend to be poor-quality, in part because + * they end up depending on other existing poor-quality {@code hashCode} implementations, + * including those in many JDK classes. + * + *

{@code Object.hashCode} implementations tend to be very fast, but have weak collision + * prevention and no expectation of bit dispersion. This leaves them perfectly suitable for + * use in hash tables, because extra collisions cause only a slight performance hit, while poor bit + * dispersion is easily corrected using a secondary hash function (which all reasonable hash table + * implementations in Java use). For the many uses of hash functions beyond data structures, + * however, {@code Object.hashCode} almost always falls short -- hence this library. + * + * @author Kevin Bourrillion + * @since 11.0 + */ +@ElementTypesAreNonnullByDefault +public interface HashFunction { + /** + * Begins a new hash code computation by returning an initialized, stateful {@code Hasher} + * instance that is ready to receive data. Example: + * + *

{@code
+   * HashFunction hf = Hashing.md5();
+   * HashCode hc = hf.newHasher()
+   *     .putLong(id)
+   *     .putBoolean(isActive)
+   *     .hash();
+   * }
+ */ + Hasher newHasher(); + + /** + * Begins a new hash code computation as {@link #newHasher()}, but provides a hint of the expected + * size of the input (in bytes). This is only important for non-streaming hash functions (hash + * functions that need to buffer their whole input before processing any of it). + */ + Hasher newHasher(int expectedInputSize); + + /** + * Shortcut for {@code newHasher().putInt(input).hash()}; returns the hash code for the given + * {@code int} value, interpreted in little-endian byte order. The implementation might + * perform better than its longhand equivalent, but should not perform worse. + * + * @since 12.0 + */ + HashCode hashInt(int input); + + /** + * Shortcut for {@code newHasher().putLong(input).hash()}; returns the hash code for the given + * {@code long} value, interpreted in little-endian byte order. The implementation might + * perform better than its longhand equivalent, but should not perform worse. + */ + HashCode hashLong(long input); + + /** + * Shortcut for {@code newHasher().putBytes(input).hash()}. The implementation might + * perform better than its longhand equivalent, but should not perform worse. + */ + HashCode hashBytes(byte[] input); + + /** + * Shortcut for {@code newHasher().putBytes(input, off, len).hash()}. The implementation + * might perform better than its longhand equivalent, but should not perform worse. + * + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > bytes.length} or + * {@code len < 0} + */ + HashCode hashBytes(byte[] input, int off, int len); + + /** + * Shortcut for {@code newHasher().putBytes(input).hash()}. The implementation might + * perform better than its longhand equivalent, but should not perform worse. + * + * @since 23.0 + */ + HashCode hashBytes(ByteBuffer input); + + /** + * Shortcut for {@code newHasher().putUnencodedChars(input).hash()}. The implementation + * might perform better than its longhand equivalent, but should not perform worse. Note + * that no character encoding is performed; the low byte and high byte of each {@code char} are + * hashed directly (in that order). + * + *

Warning: This method will produce different output than most other languages do when + * running the same hash function on the equivalent input. For cross-language compatibility, use + * {@link #hashString}, usually with a charset of UTF-8. For other use cases, use {@code + * hashUnencodedChars}. + * + * @since 15.0 (since 11.0 as hashString(CharSequence)). + */ + HashCode hashUnencodedChars(CharSequence input); + + /** + * Shortcut for {@code newHasher().putString(input, charset).hash()}. Characters are encoded using + * the given {@link Charset}. The implementation might perform better than its longhand + * equivalent, but should not perform worse. + * + *

Warning: This method, which reencodes the input before hashing it, is useful only for + * cross-language compatibility. For other use cases, prefer {@link #hashUnencodedChars}, which is + * faster, produces the same output across Java releases, and hashes every {@code char} in the + * input, even if some are invalid. + */ + HashCode hashString(CharSequence input, Charset charset); + + /** + * Shortcut for {@code newHasher().putObject(instance, funnel).hash()}. The implementation + * might perform better than its longhand equivalent, but should not perform worse. + * + * @since 14.0 + */ + HashCode hashObject(@ParametricNullness T instance, Funnel funnel); + + /** + * Returns the number of bits (a multiple of 32) that each hash code produced by this hash + * function has. + */ + int bits(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Hasher.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Hasher.java new file mode 100644 index 0000000000000..7b845768899ba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Hasher.java @@ -0,0 +1,144 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * A {@link PrimitiveSink} that can compute a hash code after reading the input. Each hasher should + * translate all multibyte values ({@link #putInt(int)}, {@link #putLong(long)}, etc) to bytes in + * little-endian order. + * + *

Warning: The result of calling any methods after calling {@link #hash} is undefined. + * + *

Warning: Using a specific character encoding when hashing a {@link CharSequence} with + * {@link #putString(CharSequence, Charset)} is generally only useful for cross-language + * compatibility (otherwise prefer {@link #putUnencodedChars}). However, the character encodings + * must be identical across languages. Also beware that {@link Charset} definitions may occasionally + * change between Java releases. + * + *

Warning: Chunks of data that are put into the {@link Hasher} are not delimited. The + * resulting {@link HashCode} is dependent only on the bytes inserted, and the order in which they + * were inserted, not how those bytes were chunked into discrete put() operations. For example, the + * following three expressions all generate colliding hash codes: + * + *

{@code
+ * newHasher().putByte(b1).putByte(b2).putByte(b3).hash()
+ * newHasher().putByte(b1).putBytes(new byte[] { b2, b3 }).hash()
+ * newHasher().putBytes(new byte[] { b1, b2, b3 }).hash()
+ * }
+ * + *

If you wish to avoid this, you should either prepend or append the size of each chunk. Keep in + * mind that when dealing with char sequences, the encoded form of two concatenated char sequences + * is not equivalent to the concatenation of their encoded form. Therefore, {@link + * #putString(CharSequence, Charset)} should only be used consistently with complete + * sequences and not broken into chunks. + * + * @author Kevin Bourrillion + * @since 11.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface Hasher extends PrimitiveSink { + @Override + Hasher putByte(byte b); + + @Override + Hasher putBytes(byte[] bytes); + + @Override + Hasher putBytes(byte[] bytes, int off, int len); + + @Override + Hasher putBytes(ByteBuffer bytes); + + @Override + Hasher putShort(short s); + + @Override + Hasher putInt(int i); + + @Override + Hasher putLong(long l); + + /** Equivalent to {@code putInt(Float.floatToRawIntBits(f))}. */ + @Override + Hasher putFloat(float f); + + /** Equivalent to {@code putLong(Double.doubleToRawLongBits(d))}. */ + @Override + Hasher putDouble(double d); + + /** Equivalent to {@code putByte(b ? (byte) 1 : (byte) 0)}. */ + @Override + Hasher putBoolean(boolean b); + + @Override + Hasher putChar(char c); + + /** + * Equivalent to processing each {@code char} value in the {@code CharSequence}, in order. In + * other words, no character encoding is performed; the low byte and high byte of each {@code + * char} are hashed directly (in that order). The input must not be updated while this method is + * in progress. + * + *

Warning: This method will produce different output than most other languages do when + * running the same hash function on the equivalent input. For cross-language compatibility, use + * {@link #putString}, usually with a charset of UTF-8. For other use cases, use {@code + * putUnencodedChars}. + * + * @since 15.0 (since 11.0 as putString(CharSequence)). + */ + @Override + Hasher putUnencodedChars(CharSequence charSequence); + + /** + * Equivalent to {@code putBytes(charSequence.toString().getBytes(charset))}. + * + *

Warning: This method, which reencodes the input before hashing it, is useful only for + * cross-language compatibility. For other use cases, prefer {@link #putUnencodedChars}, which is + * faster, produces the same output across Java releases, and hashes every {@code char} in the + * input, even if some are invalid. + */ + @Override + Hasher putString(CharSequence charSequence, Charset charset); + + /** A simple convenience for {@code funnel.funnel(object, this)}. */ + Hasher putObject(@ParametricNullness T instance, Funnel funnel); + + /** + * Computes a hash code based on the data that have been provided to this hasher. The result is + * unspecified if this method is called more than once on the same instance. + */ + HashCode hash(); + + /** + * {@inheritDoc} + * + * @deprecated This returns {@link Object#hashCode()}; you almost certainly mean to call {@code + * hash().asInt()}. + */ + @Override + @Deprecated + int hashCode(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Hashing.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Hashing.java new file mode 100644 index 0000000000000..cf098de8503aa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Hashing.java @@ -0,0 +1,837 @@ +/* + * 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.iotdb.google.common.hash; + +import javax.crypto.spec.SecretKeySpec; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.security.Key; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.zip.Adler32; +import java.util.zip.CRC32; +import java.util.zip.Checksum; + +import static java.lang.invoke.MethodType.methodType; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; + +/** + * Static methods to obtain {@link HashFunction} instances, and other static hashing-related + * utilities. + * + *

A comparison of the various hash functions can be found here. + * + * @author Kevin Bourrillion + * @author Dimitris Andreou + * @author Kurt Alfred Kluever + * @since 11.0 + */ +@ElementTypesAreNonnullByDefault +public final class Hashing { + /** + * Returns a general-purpose, temporary-use, non-cryptographic hash function. The algorithm + * the returned function implements is unspecified and subject to change without notice. + * + *

Warning: a new random seed for these functions is chosen each time the {@code + * Hashing} class is loaded. Do not use this method if hash codes may escape the current + * process in any way, for example being sent over RPC, or saved to disk. For a general-purpose, + * non-cryptographic hash function that will never change behavior, we suggest {@link + * #murmur3_128}. + * + *

Repeated calls to this method on the same loaded {@code Hashing} class, using the same value + * for {@code minimumBits}, will return identically-behaving {@link HashFunction} instances. + * + * @param minimumBits a positive integer. This can be arbitrarily large. The returned {@link + * HashFunction} instance may use memory proportional to this integer. + * @return a hash function, described above, that produces hash codes of length {@code + * minimumBits} or greater + */ + public static HashFunction goodFastHash(int minimumBits) { + int bits = checkPositiveAndMakeMultipleOf32(minimumBits); + + if (bits == 32) { + return Murmur3_32HashFunction.GOOD_FAST_HASH_32; + } + if (bits <= 128) { + return Murmur3_128HashFunction.GOOD_FAST_HASH_128; + } + + // Otherwise, join together some 128-bit murmur3s + int hashFunctionsNeeded = (bits + 127) / 128; + HashFunction[] hashFunctions = new HashFunction[hashFunctionsNeeded]; + hashFunctions[0] = Murmur3_128HashFunction.GOOD_FAST_HASH_128; + int seed = GOOD_FAST_HASH_SEED; + for (int i = 1; i < hashFunctionsNeeded; i++) { + seed += 1500450271; // a prime; shouldn't matter + hashFunctions[i] = murmur3_128(seed); + } + return new ConcatenatedHashFunction(hashFunctions); + } + + /** + * Used to randomize {@link #goodFastHash} instances, so that programs which persist anything + * dependent on the hash codes they produce will fail sooner. + */ + @SuppressWarnings("GoodTime") // reading system time without TimeSource + static final int GOOD_FAST_HASH_SEED = (int) System.currentTimeMillis(); + + /** + * Returns a hash function implementing the 32-bit murmur3 + * algorithm, x86 variant (little-endian variant), using the given seed value, with a known + * bug as described in the deprecation text. + * + *

The C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A), which however does not + * have the bug. + * + * @deprecated This implementation produces incorrect hash values from the {@link + * HashFunction#hashString} method if the string contains non-BMP characters. Use {@link + * #murmur3_32_fixed(int)} instead. + */ + @Deprecated + public static HashFunction murmur3_32(int seed) { + return new Murmur3_32HashFunction(seed, /* supplementaryPlaneFix= */ false); + } + + /** + * Returns a hash function implementing the 32-bit murmur3 + * algorithm, x86 variant (little-endian variant), using the given seed value, with a known + * bug as described in the deprecation text. + * + *

The C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A), which however does not + * have the bug. + * + * @deprecated This implementation produces incorrect hash values from the {@link + * HashFunction#hashString} method if the string contains non-BMP characters. Use {@link + * #murmur3_32_fixed()} instead. + */ + @Deprecated + public static HashFunction murmur3_32() { + return Murmur3_32HashFunction.MURMUR3_32; + } + + /** + * Returns a hash function implementing the 32-bit murmur3 + * algorithm, x86 variant (little-endian variant), using the given seed value. + * + *

The exact C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A). + * + *

This method is called {@code murmur3_32_fixed} because it fixes a bug in the {@code + * HashFunction} returned by the original {@code murmur3_32} method. + * + * @since 31.0 + */ + public static HashFunction murmur3_32_fixed(int seed) { + return new Murmur3_32HashFunction(seed, /* supplementaryPlaneFix= */ true); + } + + /** + * Returns a hash function implementing the 32-bit murmur3 + * algorithm, x86 variant (little-endian variant), using a seed value of zero. + * + *

The exact C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A). + * + *

This method is called {@code murmur3_32_fixed} because it fixes a bug in the {@code + * HashFunction} returned by the original {@code murmur3_32} method. + * + * @since 31.0 + */ + public static HashFunction murmur3_32_fixed() { + return Murmur3_32HashFunction.MURMUR3_32_FIXED; + } + + /** + * Returns a hash function implementing the 128-bit murmur3 + * algorithm, x64 variant (little-endian variant), using the given seed value. + * + *

The exact C++ equivalent is the MurmurHash3_x64_128 function (Murmur3F). + */ + public static HashFunction murmur3_128(int seed) { + return new Murmur3_128HashFunction(seed); + } + + /** + * Returns a hash function implementing the 128-bit murmur3 + * algorithm, x64 variant (little-endian variant), using a seed value of zero. + * + *

The exact C++ equivalent is the MurmurHash3_x64_128 function (Murmur3F). + */ + public static HashFunction murmur3_128() { + return Murmur3_128HashFunction.MURMUR3_128; + } + + /** + * Returns a hash function implementing the 64-bit + * SipHash-2-4 algorithm using a seed value of {@code k = 00 01 02 ...}. + * + * @since 15.0 + */ + public static HashFunction sipHash24() { + return SipHashFunction.SIP_HASH_24; + } + + /** + * Returns a hash function implementing the 64-bit + * SipHash-2-4 algorithm using the given seed. + * + * @since 15.0 + */ + public static HashFunction sipHash24(long k0, long k1) { + return new SipHashFunction(2, 4, k0, k1); + } + + /** + * Returns a hash function implementing the MD5 hash algorithm (128 hash bits). + * + * @deprecated If you must interoperate with a system that requires MD5, then use this method, + * despite its deprecation. But if you can choose your hash function, avoid MD5, which is + * neither fast nor secure. As of January 2017, we suggest: + *

    + *
  • For security: {@link Hashing#sha256} or a higher-level API. + *
  • For speed: {@link Hashing#goodFastHash}, though see its docs for caveats. + *
+ */ + @Deprecated + public static HashFunction md5() { + return Md5Holder.MD5; + } + + private static class Md5Holder { + static final HashFunction MD5 = new MessageDigestHashFunction("MD5", "Hashing.md5()"); + } + + /** + * Returns a hash function implementing the SHA-1 algorithm (160 hash bits). + * + * @deprecated If you must interoperate with a system that requires SHA-1, then use this method, + * despite its deprecation. But if you can choose your hash function, avoid SHA-1, which is + * neither fast nor secure. As of January 2017, we suggest: + *
    + *
  • For security: {@link Hashing#sha256} or a higher-level API. + *
  • For speed: {@link Hashing#goodFastHash}, though see its docs for caveats. + *
+ */ + @Deprecated + public static HashFunction sha1() { + return Sha1Holder.SHA_1; + } + + private static class Sha1Holder { + static final HashFunction SHA_1 = new MessageDigestHashFunction("SHA-1", "Hashing.sha1()"); + } + + /** Returns a hash function implementing the SHA-256 algorithm (256 hash bits). */ + public static HashFunction sha256() { + return Sha256Holder.SHA_256; + } + + private static class Sha256Holder { + static final HashFunction SHA_256 = + new MessageDigestHashFunction("SHA-256", "Hashing.sha256()"); + } + + /** + * Returns a hash function implementing the SHA-384 algorithm (384 hash bits). + * + * @since 19.0 + */ + public static HashFunction sha384() { + return Sha384Holder.SHA_384; + } + + private static class Sha384Holder { + static final HashFunction SHA_384 = + new MessageDigestHashFunction("SHA-384", "Hashing.sha384()"); + } + + /** Returns a hash function implementing the SHA-512 algorithm (512 hash bits). */ + public static HashFunction sha512() { + return Sha512Holder.SHA_512; + } + + private static class Sha512Holder { + static final HashFunction SHA_512 = + new MessageDigestHashFunction("SHA-512", "Hashing.sha512()"); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * MD5 (128 hash bits) hash function and the given secret key. + * + * @param key the secret key + * @throws IllegalArgumentException if the given key is inappropriate for initializing this MAC + * @since 20.0 + */ + public static HashFunction hmacMd5(Key key) { + return new MacHashFunction("HmacMD5", key, hmacToString("hmacMd5", key)); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * MD5 (128 hash bits) hash function and a {@link SecretKeySpec} created from the given byte array + * and the MD5 algorithm. + * + * @param key the key material of the secret key + * @since 20.0 + */ + public static HashFunction hmacMd5(byte[] key) { + return hmacMd5(new SecretKeySpec(checkNotNull(key), "HmacMD5")); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * SHA-1 (160 hash bits) hash function and the given secret key. + * + * @param key the secret key + * @throws IllegalArgumentException if the given key is inappropriate for initializing this MAC + * @since 20.0 + */ + public static HashFunction hmacSha1(Key key) { + return new MacHashFunction("HmacSHA1", key, hmacToString("hmacSha1", key)); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * SHA-1 (160 hash bits) hash function and a {@link SecretKeySpec} created from the given byte + * array and the SHA-1 algorithm. + * + * @param key the key material of the secret key + * @since 20.0 + */ + public static HashFunction hmacSha1(byte[] key) { + return hmacSha1(new SecretKeySpec(checkNotNull(key), "HmacSHA1")); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * SHA-256 (256 hash bits) hash function and the given secret key. + * + * @param key the secret key + * @throws IllegalArgumentException if the given key is inappropriate for initializing this MAC + * @since 20.0 + */ + public static HashFunction hmacSha256(Key key) { + return new MacHashFunction("HmacSHA256", key, hmacToString("hmacSha256", key)); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * SHA-256 (256 hash bits) hash function and a {@link SecretKeySpec} created from the given byte + * array and the SHA-256 algorithm. + * + * @param key the key material of the secret key + * @since 20.0 + */ + public static HashFunction hmacSha256(byte[] key) { + return hmacSha256(new SecretKeySpec(checkNotNull(key), "HmacSHA256")); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * SHA-512 (512 hash bits) hash function and the given secret key. + * + * @param key the secret key + * @throws IllegalArgumentException if the given key is inappropriate for initializing this MAC + * @since 20.0 + */ + public static HashFunction hmacSha512(Key key) { + return new MacHashFunction("HmacSHA512", key, hmacToString("hmacSha512", key)); + } + + /** + * Returns a hash function implementing the Message Authentication Code (MAC) algorithm, using the + * SHA-512 (512 hash bits) hash function and a {@link SecretKeySpec} created from the given byte + * array and the SHA-512 algorithm. + * + * @param key the key material of the secret key + * @since 20.0 + */ + public static HashFunction hmacSha512(byte[] key) { + return hmacSha512(new SecretKeySpec(checkNotNull(key), "HmacSHA512")); + } + + private static String hmacToString(String methodName, Key key) { + return "Hashing." + + methodName + + "(Key[algorithm=" + + key.getAlgorithm() + + ", format=" + + key.getFormat() + + "])"; + } + + /** + * Returns a hash function implementing the CRC32C checksum algorithm (32 hash bits) as described + * by RFC 3720, Section 12.1. + * + *

This function is best understood as a checksum rather than a true hash function. + * + * @since 18.0 + */ + public static HashFunction crc32c() { + return Crc32CSupplier.HASH_FUNCTION; + } + + private enum Crc32CSupplier implements ImmutableSupplier { + JAVA_UTIL_ZIP { + @Override + public HashFunction get() { + return ChecksumType.CRC_32C.hashFunction; + } + }, + ABSTRACT_HASH_FUNCTION { + @Override + public HashFunction get() { + return Crc32cHashFunction.CRC_32_C; + } + }; + + static final HashFunction HASH_FUNCTION = pickFunction().get(); + + private static Crc32CSupplier pickFunction() { + Crc32CSupplier[] functions = values(); + + if (functions.length == 1) { + // We're running under J2ObjC. + return functions[0]; + } + + // We can't refer to JAVA_UTIL_ZIP directly at compile time because of J2ObjC. + Crc32CSupplier javaUtilZip = functions[0]; + + try { + Class.forName("java.util.zip.CRC32C"); + return javaUtilZip; + } catch (ClassNotFoundException runningUnderJava8) { + return ABSTRACT_HASH_FUNCTION; + } + } + } + + /** + * Returns a hash function implementing the CRC-32 checksum algorithm (32 hash bits). + * + *

To get the {@code long} value equivalent to {@link Checksum#getValue()} for a {@code + * HashCode} produced by this function, use {@link HashCode#padToLong()}. + * + *

This function is best understood as a checksum rather than a true hash function. + * + * @since 14.0 + */ + public static HashFunction crc32() { + return ChecksumType.CRC_32.hashFunction; + } + + /** + * Returns a hash function implementing the Adler-32 checksum algorithm (32 hash bits). + * + *

To get the {@code long} value equivalent to {@link Checksum#getValue()} for a {@code + * HashCode} produced by this function, use {@link HashCode#padToLong()}. + * + *

This function is best understood as a checksum rather than a true hash function. + * + * @since 14.0 + */ + public static HashFunction adler32() { + return ChecksumType.ADLER_32.hashFunction; + } + + enum ChecksumType implements ImmutableSupplier { + CRC_32("Hashing.crc32()") { + @Override + public Checksum get() { + return new CRC32(); + } + }, + CRC_32C("Hashing.crc32c()") { + @Override + public Checksum get() { + return Crc32cMethodHandles.newCrc32c(); + } + }, + ADLER_32("Hashing.adler32()") { + @Override + public Checksum get() { + return new Adler32(); + } + }; + + public final HashFunction hashFunction; + + ChecksumType(String toString) { + this.hashFunction = new ChecksumHashFunction(this, 32, toString); + } + } + + @SuppressWarnings("unused") + private static final class Crc32cMethodHandles { + private static final MethodHandle CONSTRUCTOR = crc32cConstructor(); + + @IgnoreJRERequirement // https://github.com/mojohaus/animal-sniffer/issues/67 + static Checksum newCrc32c() { + try { + return (Checksum) CONSTRUCTOR.invokeExact(); + } catch (Throwable e) { + throwIfUnchecked(e); + // That constructor has no `throws` clause. + throw newLinkageError(e); + } + } + + private static MethodHandle crc32cConstructor() { + try { + Class clazz = Class.forName("java.util.zip.CRC32C"); + /* + * We can't cast to CRC32C at the call site because we support building with Java 8 + * (https://github.com/google/guava/issues/6549). So we have to use asType() to change from + * CRC32C to Checksum. This may carry some performance cost + * (https://stackoverflow.com/a/22321671/28465), but I'd have to benchmark more carefully to + * even detect it. + */ + return MethodHandles.lookup() + .findConstructor(clazz, methodType(void.class)) + .asType(methodType(Checksum.class)); + } catch (ClassNotFoundException e) { + // We check that the class is available before calling this method. + throw new AssertionError(e); + } catch (IllegalAccessException e) { + // That API is public. + throw newLinkageError(e); + } catch (NoSuchMethodException e) { + // That constructor exists. + throw newLinkageError(e); + } + } + + private static LinkageError newLinkageError(Throwable cause) { + return new LinkageError(cause.toString(), cause); + } + } + + /** + * Returns a hash function implementing FarmHash's Fingerprint64, an open-source algorithm. + * + *

This is designed for generating persistent fingerprints of strings. It isn't + * cryptographically secure, but it produces a high-quality hash with fewer collisions than some + * alternatives we've used in the past. + * + *

FarmHash fingerprints are encoded by {@link HashCode#asBytes} in little-endian order. This + * means {@link HashCode#asLong} is guaranteed to return the same value that + * farmhash::Fingerprint64() would for the same input (when compared using {@link + * org.apache.iotdb.google.common.primitives.UnsignedLongs}'s encoding of 64-bit unsigned + * numbers). + * + *

This function is best understood as a fingerprint rather than a true + * hash function. + * + * @since 20.0 + */ + public static HashFunction farmHashFingerprint64() { + return FarmHashFingerprint64.FARMHASH_FINGERPRINT_64; + } + + /** + * Returns a hash function implementing the Fingerprint2011 hashing function (64 hash bits). + * + *

This is designed for generating persistent fingerprints of strings. It isn't + * cryptographically secure, but it produces a high-quality hash with few collisions. Fingerprints + * generated using this are byte-wise identical to those created using the C++ version, but note + * that this uses unsigned integers (see {@link + * org.apache.iotdb.google.common.primitives.UnsignedInts}). Comparisons between the two should + * take this into account. + * + *

Fingerprint2011() is a form of Murmur2 on strings up to 32 bytes and a form of CityHash for + * longer strings. It could have been one or the other throughout. The main advantage of the + * combination is that CityHash has a bunch of special cases for short strings that don't need to + * be replicated here. The result will never be 0 or 1. + * + *

This function is best understood as a fingerprint rather than a true + * hash function. + * + * @since 31.1 + */ + public static HashFunction fingerprint2011() { + return Fingerprint2011.FINGERPRINT_2011; + } + + /** + * Assigns to {@code hashCode} a "bucket" in the range {@code [0, buckets)}, in a uniform manner + * that minimizes the need for remapping as {@code buckets} grows. That is, {@code + * consistentHash(h, n)} equals: + * + *

    + *
  • {@code n - 1}, with approximate probability {@code 1/n} + *
  • {@code consistentHash(h, n - 1)}, otherwise (probability {@code 1 - 1/n}) + *
+ * + *

This method is suitable for the common use case of dividing work among buckets that meet the + * following conditions: + * + *

    + *
  • You want to assign the same fraction of inputs to each bucket. + *
  • When you reduce the number of buckets, you can accept that the most recently added + * buckets will be removed first. More concretely, if you are dividing traffic among tasks, + * you can decrease the number of tasks from 15 and 10, killing off the final 5 tasks, and + * {@code consistentHash} will handle it. If, however, you are dividing traffic among + * servers {@code alpha}, {@code bravo}, and {@code charlie} and you occasionally need to + * take each of the servers offline, {@code consistentHash} will be a poor fit: It provides + * no way for you to specify which of the three buckets is disappearing. Thus, if your + * buckets change from {@code [alpha, bravo, charlie]} to {@code [bravo, charlie]}, it will + * assign all the old {@code alpha} traffic to {@code bravo} and all the old {@code bravo} + * traffic to {@code charlie}, rather than letting {@code bravo} keep its traffic. + *
+ * + *

See the Wikipedia article on + * consistent hashing for more information. + */ + public static int consistentHash(HashCode hashCode, int buckets) { + return consistentHash(hashCode.padToLong(), buckets); + } + + /** + * Assigns to {@code input} a "bucket" in the range {@code [0, buckets)}, in a uniform manner that + * minimizes the need for remapping as {@code buckets} grows. That is, {@code consistentHash(h, + * n)} equals: + * + *

    + *
  • {@code n - 1}, with approximate probability {@code 1/n} + *
  • {@code consistentHash(h, n - 1)}, otherwise (probability {@code 1 - 1/n}) + *
+ * + *

This method is suitable for the common use case of dividing work among buckets that meet the + * following conditions: + * + *

    + *
  • You want to assign the same fraction of inputs to each bucket. + *
  • When you reduce the number of buckets, you can accept that the most recently added + * buckets will be removed first. More concretely, if you are dividing traffic among tasks, + * you can decrease the number of tasks from 15 and 10, killing off the final 5 tasks, and + * {@code consistentHash} will handle it. If, however, you are dividing traffic among + * servers {@code alpha}, {@code bravo}, and {@code charlie} and you occasionally need to + * take each of the servers offline, {@code consistentHash} will be a poor fit: It provides + * no way for you to specify which of the three buckets is disappearing. Thus, if your + * buckets change from {@code [alpha, bravo, charlie]} to {@code [bravo, charlie]}, it will + * assign all the old {@code alpha} traffic to {@code bravo} and all the old {@code bravo} + * traffic to {@code charlie}, rather than letting {@code bravo} keep its traffic. + *
+ * + *

See the Wikipedia article on + * consistent hashing for more information. + */ + public static int consistentHash(long input, int buckets) { + checkArgument(buckets > 0, "buckets must be positive: %s", buckets); + LinearCongruentialGenerator generator = new LinearCongruentialGenerator(input); + int candidate = 0; + int next; + + // Jump from bucket to bucket until we go out of range + while (true) { + next = (int) ((candidate + 1) / generator.nextDouble()); + if (next >= 0 && next < buckets) { + candidate = next; + } else { + return candidate; + } + } + } + + /** + * Returns a hash code, having the same bit length as each of the input hash codes, that combines + * the information of these hash codes in an ordered fashion. That is, whenever two equal hash + * codes are produced by two calls to this method, it is as likely as possible that each + * was computed from the same input hash codes in the same order. + * + * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all + * have the same bit length + */ + public static HashCode combineOrdered(Iterable hashCodes) { + Iterator iterator = hashCodes.iterator(); + checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); + int bits = iterator.next().bits(); + byte[] resultBytes = new byte[bits / 8]; + for (HashCode hashCode : hashCodes) { + byte[] nextBytes = hashCode.asBytes(); + checkArgument( + nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); + for (int i = 0; i < nextBytes.length; i++) { + resultBytes[i] = (byte) (resultBytes[i] * 37 ^ nextBytes[i]); + } + } + return HashCode.fromBytesNoCopy(resultBytes); + } + + /** + * Returns a hash code, having the same bit length as each of the input hash codes, that combines + * the information of these hash codes in an unordered fashion. That is, whenever two equal hash + * codes are produced by two calls to this method, it is as likely as possible that each + * was computed from the same input hash codes in some order. + * + * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all + * have the same bit length + */ + public static HashCode combineUnordered(Iterable hashCodes) { + Iterator iterator = hashCodes.iterator(); + checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); + byte[] resultBytes = new byte[iterator.next().bits() / 8]; + for (HashCode hashCode : hashCodes) { + byte[] nextBytes = hashCode.asBytes(); + checkArgument( + nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); + for (int i = 0; i < nextBytes.length; i++) { + resultBytes[i] += nextBytes[i]; + } + } + return HashCode.fromBytesNoCopy(resultBytes); + } + + /** Checks that the passed argument is positive, and ceils it to a multiple of 32. */ + static int checkPositiveAndMakeMultipleOf32(int bits) { + checkArgument(bits > 0, "Number of bits must be positive"); + return (bits + 31) & ~31; + } + + /** + * Returns a hash function which computes its hash code by concatenating the hash codes of the + * underlying hash functions together. This can be useful if you need to generate hash codes of a + * specific length. + * + *

For example, if you need 1024-bit hash codes, you could join two {@link Hashing#sha512} hash + * functions together: {@code Hashing.concatenating(Hashing.sha512(), Hashing.sha512())}. + * + * @since 19.0 + */ + public static HashFunction concatenating( + HashFunction first, HashFunction second, HashFunction... rest) { + // We can't use Lists.asList() here because there's no hash->collect dependency + List list = new ArrayList<>(); + list.add(first); + list.add(second); + Collections.addAll(list, rest); + return new ConcatenatedHashFunction(list.toArray(new HashFunction[0])); + } + + /** + * Returns a hash function which computes its hash code by concatenating the hash codes of the + * underlying hash functions together. This can be useful if you need to generate hash codes of a + * specific length. + * + *

For example, if you need 1024-bit hash codes, you could join two {@link Hashing#sha512} hash + * functions together: {@code Hashing.concatenating(Hashing.sha512(), Hashing.sha512())}. + * + * @since 19.0 + */ + public static HashFunction concatenating(Iterable hashFunctions) { + checkNotNull(hashFunctions); + // We can't use Iterables.toArray() here because there's no hash->collect dependency + List list = new ArrayList<>(); + for (HashFunction hashFunction : hashFunctions) { + list.add(hashFunction); + } + checkArgument(!list.isEmpty(), "number of hash functions (%s) must be > 0", list.size()); + return new ConcatenatedHashFunction(list.toArray(new HashFunction[0])); + } + + private static final class ConcatenatedHashFunction extends AbstractCompositeHashFunction { + + private ConcatenatedHashFunction(HashFunction... functions) { + super(functions); + for (HashFunction function : functions) { + checkArgument( + function.bits() % 8 == 0, + "the number of bits (%s) in hashFunction (%s) must be divisible by 8", + function.bits(), + function); + } + } + + @Override + HashCode makeHash(Hasher[] hashers) { + byte[] bytes = new byte[bits() / 8]; + int i = 0; + for (Hasher hasher : hashers) { + HashCode newHash = hasher.hash(); + i += newHash.writeBytesTo(bytes, i, newHash.bits() / 8); + } + return HashCode.fromBytesNoCopy(bytes); + } + + @Override + public int bits() { + int bitSum = 0; + for (HashFunction function : functions) { + bitSum += function.bits(); + } + return bitSum; + } + + @Override + public boolean equals(Object object) { + if (object instanceof ConcatenatedHashFunction) { + ConcatenatedHashFunction other = (ConcatenatedHashFunction) object; + return Arrays.equals(functions, other.functions); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(functions); + } + } + + /** + * Linear CongruentialGenerator to use for consistent hashing. See + * http://en.wikipedia.org/wiki/Linear_congruential_generator + */ + private static final class LinearCongruentialGenerator { + private long state; + + public LinearCongruentialGenerator(long seed) { + this.state = seed; + } + + public double nextDouble() { + state = 2862933555777941757L * state + 1; + return ((double) ((int) (state >>> 33) + 1)) / 0x1.0p31; + } + } + + private Hashing() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashingInputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashingInputStream.java new file mode 100644 index 0000000000000..c3f889f9949a3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashingInputStream.java @@ -0,0 +1,109 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An {@link InputStream} that maintains a hash of the data read from it. + * + * @author Qian Huang + * @since 16.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class HashingInputStream extends FilterInputStream { + private final Hasher hasher; + + /** + * Creates an input stream that hashes using the given {@link HashFunction} and delegates all data + * read from it to the underlying {@link InputStream}. + * + *

The {@link InputStream} should not be read from before or after the hand-off. + */ + public HashingInputStream(HashFunction hashFunction, InputStream in) { + super(checkNotNull(in)); + this.hasher = checkNotNull(hashFunction.newHasher()); + } + + /** + * Reads the next byte of data from the underlying input stream and updates the hasher with the + * byte read. + */ + @Override + public int read() throws IOException { + int b = in.read(); + if (b != -1) { + hasher.putByte((byte) b); + } + return b; + } + + /** + * Reads the specified bytes of data from the underlying input stream and updates the hasher with + * the bytes read. + */ + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + int numOfBytesRead = in.read(bytes, off, len); + if (numOfBytesRead != -1) { + hasher.putBytes(bytes, off, numOfBytesRead); + } + return numOfBytesRead; + } + + /** + * mark() is not supported for HashingInputStream + * + * @return {@code false} always + */ + @Override + public boolean markSupported() { + return false; + } + + /** mark() is not supported for HashingInputStream */ + @Override + public void mark(int readlimit) {} + + /** + * reset() is not supported for HashingInputStream. + * + * @throws IOException this operation is not supported + */ + @Override + public void reset() throws IOException { + throw new IOException("reset not supported"); + } + + /** + * Returns the {@link HashCode} based on the data read from this stream. The result is unspecified + * if this method is called more than once on the same instance. + */ + public HashCode hash() { + return hasher.hash(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashingOutputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashingOutputStream.java new file mode 100644 index 0000000000000..b0cf6c4eecd90 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/HashingOutputStream.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An {@link OutputStream} that maintains a hash of the data written to it. + * + * @author Nick Piepmeier + * @since 16.0 + */ +@Beta +@ElementTypesAreNonnullByDefault +public final class HashingOutputStream extends FilterOutputStream { + private final Hasher hasher; + + /** + * Creates an output stream that hashes using the given {@link HashFunction}, and forwards all + * data written to it to the underlying {@link OutputStream}. + * + *

The {@link OutputStream} should not be written to before or after the hand-off. + */ + // TODO(user): Evaluate whether it makes sense to always piggyback the computation of a + // HashCode on an existing OutputStream, compared to creating a separate OutputStream that could + // be (optionally) be combined with another if needed (with something like + // MultiplexingOutputStream). + public HashingOutputStream(HashFunction hashFunction, OutputStream out) { + super(checkNotNull(out)); + this.hasher = checkNotNull(hashFunction.newHasher()); + } + + @Override + public void write(int b) throws IOException { + hasher.putByte((byte) b); + out.write(b); + } + + @Override + public void write(byte[] bytes, int off, int len) throws IOException { + hasher.putBytes(bytes, off, len); + out.write(bytes, off, len); + } + + /** + * Returns the {@link HashCode} based on the data written to this stream. The result is + * unspecified if this method is called more than once on the same instance. + */ + public HashCode hash() { + return hasher.hash(); + } + + // Overriding close() because FilterOutputStream's close() method pre-JDK8 has bad behavior: + // it silently ignores any exception thrown by flush(). Instead, just close the delegate stream. + // It should flush itself if necessary. + @Override + public void close() throws IOException { + out.close(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/IgnoreJRERequirement.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/IgnoreJRERequirement.java new file mode 100644 index 0000000000000..77b73f7fda79e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/IgnoreJRERequirement.java @@ -0,0 +1,35 @@ +/* + * 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.iotdb.google.common.hash; + +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@ElementTypesAreNonnullByDefault +@interface IgnoreJRERequirement {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ImmutableSupplier.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ImmutableSupplier.java new file mode 100644 index 0000000000000..90c65a53b2268 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ImmutableSupplier.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.base.Supplier; + +/** + * Explicitly named subinterface of {@link Supplier} that can be marked {@literal @}{@link + * Immutable}. + */ +// TODO(cpovirk): Should we just use ChecksumType directly instead of defining this type? +@ElementTypesAreNonnullByDefault +interface ImmutableSupplier extends Supplier {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Java8Compatibility.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Java8Compatibility.java new file mode 100644 index 0000000000000..77fe50319923e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Java8Compatibility.java @@ -0,0 +1,50 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.nio.Buffer; + +/** + * Wrappers around {@link Buffer} methods that are covariantly overridden in Java 9+. See + * https://github.com/google/guava/issues/3990 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class Java8Compatibility { + static void clear(Buffer b) { + b.clear(); + } + + static void flip(Buffer b) { + b.flip(); + } + + static void limit(Buffer b, int limit) { + b.limit(limit); + } + + static void position(Buffer b, int position) { + b.position(position); + } + + private Java8Compatibility() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LittleEndianByteArray.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LittleEndianByteArray.java new file mode 100644 index 0000000000000..b63596a0c8ef4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LittleEndianByteArray.java @@ -0,0 +1,270 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.primitives.Longs; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * Utility functions for loading and storing values from a byte array. + * + * @author Kevin Damm + * @author Kyle Maddison + */ +@ElementTypesAreNonnullByDefault +final class LittleEndianByteArray { + + /** The instance that actually does the work; delegates to Unsafe or a pure-Java fallback. */ + private static final LittleEndianBytes byteArray; + + /** + * Load 8 bytes into long in a little endian manner, from the substring between position and + * position + 8. The array must have at least 8 bytes from offset (inclusive). + * + * @param input the input bytes + * @param offset the offset into the array at which to start + * @return a long of a concatenated 8 bytes + */ + static long load64(byte[] input, int offset) { + // We don't want this in production code as this is the most critical part of the loop. + assert input.length >= offset + 8; + // Delegates to the fast (unsafe) version or the fallback. + return byteArray.getLongLittleEndian(input, offset); + } + + /** + * Similar to load64, but allows offset + 8 > input.length, padding the result with zeroes. This + * has to explicitly reverse the order of the bytes as it packs them into the result which makes + * it slower than the native version. + * + * @param input the input bytes + * @param offset the offset into the array at which to start reading + * @param length the number of bytes from the input to read + * @return a long of a concatenated 8 bytes + */ + static long load64Safely(byte[] input, int offset, int length) { + long result = 0; + // Due to the way we shift, we can stop iterating once we've run out of data, the rest + // of the result already being filled with zeros. + + // This loop is critical to performance, so please check HashBenchmark if altering it. + int limit = Math.min(length, 8); + for (int i = 0; i < limit; i++) { + // Shift value left while iterating logically through the array. + result |= (input[offset + i] & 0xFFL) << (i * 8); + } + return result; + } + + /** + * Store 8 bytes into the provided array at the indicated offset, using the value provided. + * + * @param sink the output byte array + * @param offset the offset into the array at which to start writing + * @param value the value to write + */ + static void store64(byte[] sink, int offset, long value) { + // We don't want to assert in production code. + assert offset >= 0 && offset + 8 <= sink.length; + // Delegates to the fast (unsafe)version or the fallback. + byteArray.putLongLittleEndian(sink, offset, value); + } + + /** + * Load 4 bytes from the provided array at the indicated offset. + * + * @param source the input bytes + * @param offset the offset into the array at which to start + * @return the value found in the array in the form of a long + */ + static int load32(byte[] source, int offset) { + // TODO(user): Measure the benefit of delegating this to LittleEndianBytes also. + return (source[offset] & 0xFF) + | ((source[offset + 1] & 0xFF) << 8) + | ((source[offset + 2] & 0xFF) << 16) + | ((source[offset + 3] & 0xFF) << 24); + } + + /** + * Indicates that the loading of Unsafe was successful and the load and store operations will be + * very efficient. May be useful for calling code to fall back on an alternative implementation + * that is slower than Unsafe.get/store but faster than the pure-Java mask-and-shift. + */ + static boolean usingUnsafe() { + return (byteArray instanceof UnsafeByteArray); + } + + /** + * Common interface for retrieving a 64-bit long from a little-endian byte array. + * + *

This abstraction allows us to use single-instruction load and put when available, or fall + * back on the slower approach of using Longs.fromBytes(byte...). + */ + private interface LittleEndianBytes { + long getLongLittleEndian(byte[] array, int offset); + + void putLongLittleEndian(byte[] array, int offset, long value); + } + + /** + * The only reference to Unsafe is in this nested class. We set things up so that if + * Unsafe.theUnsafe is inaccessible, the attempt to load the nested class fails, and the outer + * class's static initializer can fall back on a non-Unsafe version. + */ + private enum UnsafeByteArray implements LittleEndianBytes { + // Do *not* change the order of these constants! + UNSAFE_LITTLE_ENDIAN { + @Override + public long getLongLittleEndian(byte[] array, int offset) { + return theUnsafe.getLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET); + } + + @Override + public void putLongLittleEndian(byte[] array, int offset, long value) { + theUnsafe.putLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET, value); + } + }, + UNSAFE_BIG_ENDIAN { + @Override + public long getLongLittleEndian(byte[] array, int offset) { + long bigEndian = theUnsafe.getLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET); + // The hardware is big-endian, so we need to reverse the order of the bytes. + return Long.reverseBytes(bigEndian); + } + + @Override + public void putLongLittleEndian(byte[] array, int offset, long value) { + // Reverse the order of the bytes before storing, since we're on big-endian hardware. + long littleEndianValue = Long.reverseBytes(value); + theUnsafe.putLong(array, (long) offset + BYTE_ARRAY_BASE_OFFSET, littleEndianValue); + } + }; + + // Provides load and store operations that use native instructions to get better performance. + private static final Unsafe theUnsafe; + + // The offset to the first element in a byte array. + private static final int BYTE_ARRAY_BASE_OFFSET; + + /** + * Returns an Unsafe. Suitable for use in a 3rd party package. Replace with a simple call to + * Unsafe.getUnsafe when integrating into a JDK. + * + * @return an Unsafe instance if successful + */ + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException tryReflectionInstead) { + // We'll try reflection instead. + } + try { + return AccessController.doPrivileged( + (PrivilegedExceptionAction) + () -> { + Class k = Unsafe.class; + for (Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + throw new NoSuchFieldError("the Unsafe"); + }); + } catch (PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", e.getCause()); + } + } + + static { + theUnsafe = getUnsafe(); + BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + // sanity check - this should never fail + if (theUnsafe.arrayIndexScale(byte[].class) != 1) { + throw new AssertionError(); + } + } + } + + /** Fallback implementation for when Unsafe is not available in our current environment. */ + private enum JavaLittleEndianBytes implements LittleEndianBytes { + INSTANCE { + @Override + public long getLongLittleEndian(byte[] source, int offset) { + return Longs.fromBytes( + source[offset + 7], + source[offset + 6], + source[offset + 5], + source[offset + 4], + source[offset + 3], + source[offset + 2], + source[offset + 1], + source[offset]); + } + + @Override + public void putLongLittleEndian(byte[] sink, int offset, long value) { + long mask = 0xFFL; + for (int i = 0; i < 8; mask <<= 8, i++) { + sink[offset + i] = (byte) ((value & mask) >> (i * 8)); + } + } + } + } + + static { + LittleEndianBytes theGetter = JavaLittleEndianBytes.INSTANCE; + try { + /* + * UnsafeByteArray uses Unsafe.getLong() in an unsupported way, which is known to cause + * crashes on Android when running in 32-bit mode. For maximum safety, we shouldn't use + * Unsafe.getLong() at all, but the performance benefit on x86_64 is too great to ignore, so + * as a compromise, we enable the optimization only on platforms that we specifically know to + * work. + * + * In the future, the use of Unsafe.getLong() should be replaced by ByteBuffer.getLong(), + * which will have an efficient native implementation in JDK 9. + * + */ + String arch = System.getProperty("os.arch"); + if ("amd64".equals(arch) || "aarch64".equals(arch)) { + theGetter = + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN) + ? UnsafeByteArray.UNSAFE_LITTLE_ENDIAN + : UnsafeByteArray.UNSAFE_BIG_ENDIAN; + } + } catch (Throwable t) { + // ensure we really catch *everything* + } + byteArray = theGetter; + } + + /** Deter instantiation of this class. */ + private LittleEndianByteArray() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAddable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAddable.java new file mode 100644 index 0000000000000..4bd756554ae7f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAddable.java @@ -0,0 +1,34 @@ +/* + * 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.iotdb.google.common.hash; + +/** + * Abstract interface for objects that can concurrently add longs. + * + * @author Louis Wasserman + */ +@ElementTypesAreNonnullByDefault +interface LongAddable { + void increment(); + + void add(long x); + + long sum(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAddables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAddables.java new file mode 100644 index 0000000000000..cef5bf5976078 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAddables.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.base.Supplier; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Source of {@link LongAddable} objects that deals with GWT, Unsafe, and all that. + * + * @author Louis Wasserman + */ +@ElementTypesAreNonnullByDefault +final class LongAddables { + private static final Supplier SUPPLIER; + + static { + Supplier supplier; + try { + // trigger static initialization of the LongAdder class, which may fail + LongAdder unused = new LongAdder(); + supplier = + new Supplier() { + @Override + public LongAddable get() { + return new LongAdder(); + } + }; + } catch (Throwable t) { // we really want to catch *everything* + supplier = + new Supplier() { + @Override + public LongAddable get() { + return new PureJavaLongAddable(); + } + }; + } + SUPPLIER = supplier; + } + + public static LongAddable create() { + return SUPPLIER.get(); + } + + private static final class PureJavaLongAddable extends AtomicLong implements LongAddable { + @Override + public void increment() { + getAndIncrement(); + } + + @Override + public void add(long x) { + getAndAdd(x); + } + + @Override + public long sum() { + return get(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAdder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAdder.java new file mode 100644 index 0000000000000..b03b1a7a889da --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/LongAdder.java @@ -0,0 +1,200 @@ +/* + * 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.iotdb.google.common.hash; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLong; + +/** + * One or more variables that together maintain an initially zero {@code long} sum. When updates + * (method {@link #add}) are contended across threads, the set of variables may grow dynamically to + * reduce contention. Method {@link #sum} (or, equivalently, {@link #longValue}) returns the current + * total combined across the variables maintaining the sum. + * + *

This class is usually preferable to {@link AtomicLong} when multiple threads update a common + * sum that is used for purposes such as collecting statistics, not for fine-grained synchronization + * control. Under low update contention, the two classes have similar characteristics. But under + * high contention, expected throughput of this class is significantly higher, at the expense of + * higher space consumption. + * + *

This class extends {@link Number}, but does not define methods such as {@code + * equals}, {@code hashCode} and {@code compareTo} because instances are expected to be mutated, and + * so are not useful as collection keys. + * + *

jsr166e note: This class is targeted to be placed in java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +@ElementTypesAreNonnullByDefault +final class LongAdder extends Striped64 implements Serializable, LongAddable { + private static final long serialVersionUID = 7249069246863182397L; + + /** Version of plus for use in retryUpdate */ + @Override + final long fn(long v, long x) { + return v + x; + } + + /** Creates a new adder with initial sum of zero. */ + public LongAdder() {} + + /** + * Adds the given value. + * + * @param x the value to add + */ + @Override + public void add(long x) { + Cell[] as; + long b, v; + int[] hc; + Cell a; + int n; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + if ((hc = threadHashCode.get()) == null + || as == null + || (n = as.length) < 1 + || (a = as[(n - 1) & hc[0]]) == null + || !(uncontended = a.cas(v = a.value, v + x))) retryUpdate(x, hc, uncontended); + } + } + + /** Equivalent to {@code add(1)}. */ + @Override + public void increment() { + add(1L); + } + + /** Equivalent to {@code add(-1)}. */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an atomic snapshot; invocation in + * the absence of concurrent updates returns an accurate result, but concurrent updates that occur + * while the sum is being calculated might not be incorporated. + * + * @return the sum + */ + @Override + public long sum() { + long sum = base; + Cell[] as = cells; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may be a useful alternative to + * creating a new adder, but is only effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is known that no threads are + * concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link #reset}. This method may apply for + * example during quiescent points between multithreaded computations. If there are updates + * concurrent with this method, the returned value is not guaranteed to be the final + * value occurring before the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * + * @return the String representation of the {@link #sum} + */ + @Override + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + @Override + public long longValue() { + return sum(); + } + + /** Returns the {@link #sum} as an {@code int} after a narrowing primitive conversion. */ + @Override + public int intValue() { + return (int) sum(); + } + + /** Returns the {@link #sum} as a {@code float} after a widening primitive conversion. */ + @Override + public float floatValue() { + return (float) sum(); + } + + /** Returns the {@link #sum} as a {@code double} after a widening primitive conversion. */ + @Override + public double doubleValue() { + return (double) sum(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/MacHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/MacHashFunction.java new file mode 100644 index 0000000000000..ae50d43aedfb9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/MacHashFunction.java @@ -0,0 +1,146 @@ +/* + * 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.iotdb.google.common.hash; + +import javax.crypto.Mac; + +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * {@link HashFunction} adapter for {@link Mac} instances. + * + * @author Kurt Alfred Kluever + */ +@ElementTypesAreNonnullByDefault +final class MacHashFunction extends AbstractHashFunction { + + @SuppressWarnings("Immutable") // cloned before each use + private final Mac prototype; + + @SuppressWarnings("Immutable") // keys are immutable, but not provably so + private final Key key; + + private final String toString; + private final int bits; + private final boolean supportsClone; + + MacHashFunction(String algorithmName, Key key, String toString) { + this.prototype = getMac(algorithmName, key); + this.key = checkNotNull(key); + this.toString = checkNotNull(toString); + this.bits = prototype.getMacLength() * Byte.SIZE; + this.supportsClone = supportsClone(prototype); + } + + @Override + public int bits() { + return bits; + } + + private static boolean supportsClone(Mac mac) { + try { + Object unused = mac.clone(); + return true; + } catch (CloneNotSupportedException e) { + return false; + } + } + + private static Mac getMac(String algorithmName, Key key) { + try { + Mac mac = Mac.getInstance(algorithmName); + mac.init(key); + return mac; + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public Hasher newHasher() { + if (supportsClone) { + try { + return new MacHasher((Mac) prototype.clone()); + } catch (CloneNotSupportedException e) { + // falls through + } + } + return new MacHasher(getMac(prototype.getAlgorithm(), key)); + } + + @Override + public String toString() { + return toString; + } + + /** Hasher that updates a {@link Mac} (message authentication code). */ + private static final class MacHasher extends AbstractByteHasher { + private final Mac mac; + private boolean done; + + private MacHasher(Mac mac) { + this.mac = mac; + } + + @Override + protected void update(byte b) { + checkNotDone(); + mac.update(b); + } + + @Override + protected void update(byte[] b) { + checkNotDone(); + mac.update(b); + } + + @Override + protected void update(byte[] b, int off, int len) { + checkNotDone(); + mac.update(b, off, len); + } + + @Override + protected void update(ByteBuffer bytes) { + checkNotDone(); + checkNotNull(bytes); + mac.update(bytes); + } + + private void checkNotDone() { + checkState(!done, "Cannot re-use a Hasher after calling hash() on it"); + } + + @Override + public HashCode hash() { + checkNotDone(); + done = true; + return HashCode.fromBytesNoCopy(mac.doFinal()); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/MessageDigestHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/MessageDigestHashFunction.java new file mode 100644 index 0000000000000..1ca05876db852 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/MessageDigestHashFunction.java @@ -0,0 +1,174 @@ +/* + * 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.iotdb.google.common.hash; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * {@link HashFunction} adapter for {@link MessageDigest} instances. + * + * @author Kevin Bourrillion + * @author Dimitris Andreou + */ +@ElementTypesAreNonnullByDefault +final class MessageDigestHashFunction extends AbstractHashFunction implements Serializable { + + @SuppressWarnings("Immutable") // cloned before each use + private final MessageDigest prototype; + + private final int bytes; + private final boolean supportsClone; + private final String toString; + + MessageDigestHashFunction(String algorithmName, String toString) { + this.prototype = getMessageDigest(algorithmName); + this.bytes = prototype.getDigestLength(); + this.toString = checkNotNull(toString); + this.supportsClone = supportsClone(prototype); + } + + MessageDigestHashFunction(String algorithmName, int bytes, String toString) { + this.toString = checkNotNull(toString); + this.prototype = getMessageDigest(algorithmName); + int maxLength = prototype.getDigestLength(); + checkArgument( + bytes >= 4 && bytes <= maxLength, "bytes (%s) must be >= 4 and < %s", bytes, maxLength); + this.bytes = bytes; + this.supportsClone = supportsClone(prototype); + } + + private static boolean supportsClone(MessageDigest digest) { + try { + Object unused = digest.clone(); + return true; + } catch (CloneNotSupportedException e) { + return false; + } + } + + @Override + public int bits() { + return bytes * Byte.SIZE; + } + + @Override + public String toString() { + return toString; + } + + private static MessageDigest getMessageDigest(String algorithmName) { + try { + return MessageDigest.getInstance(algorithmName); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + @Override + public Hasher newHasher() { + if (supportsClone) { + try { + return new MessageDigestHasher((MessageDigest) prototype.clone(), bytes); + } catch (CloneNotSupportedException e) { + // falls through + } + } + return new MessageDigestHasher(getMessageDigest(prototype.getAlgorithm()), bytes); + } + + private static final class SerializedForm implements Serializable { + private final String algorithmName; + private final int bytes; + private final String toString; + + private SerializedForm(String algorithmName, int bytes, String toString) { + this.algorithmName = algorithmName; + this.bytes = bytes; + this.toString = toString; + } + + private Object readResolve() { + return new MessageDigestHashFunction(algorithmName, bytes, toString); + } + + private static final long serialVersionUID = 0; + } + + Object writeReplace() { + return new SerializedForm(prototype.getAlgorithm(), bytes, toString); + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + /** Hasher that updates a message digest. */ + private static final class MessageDigestHasher extends AbstractByteHasher { + private final MessageDigest digest; + private final int bytes; + private boolean done; + + private MessageDigestHasher(MessageDigest digest, int bytes) { + this.digest = digest; + this.bytes = bytes; + } + + @Override + protected void update(byte b) { + checkNotDone(); + digest.update(b); + } + + @Override + protected void update(byte[] b, int off, int len) { + checkNotDone(); + digest.update(b, off, len); + } + + @Override + protected void update(ByteBuffer bytes) { + checkNotDone(); + digest.update(bytes); + } + + private void checkNotDone() { + checkState(!done, "Cannot re-use a Hasher after calling hash() on it"); + } + + @Override + public HashCode hash() { + checkNotDone(); + done = true; + return (bytes == digest.getDigestLength()) + ? HashCode.fromBytesNoCopy(digest.digest()) + : HashCode.fromBytesNoCopy(Arrays.copyOf(digest.digest(), bytes)); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Murmur3_128HashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Murmur3_128HashFunction.java new file mode 100644 index 0000000000000..7190550decccd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Murmur3_128HashFunction.java @@ -0,0 +1,207 @@ +/* + * 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.iotdb.google.common.hash; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static org.apache.iotdb.google.common.primitives.UnsignedBytes.toInt; + +/** + * See MurmurHash3_x64_128 in the + * C++ implementation. + * + * @author Austin Appleby + * @author Dimitris Andreou + */ +@ElementTypesAreNonnullByDefault +final class Murmur3_128HashFunction extends AbstractHashFunction implements Serializable { + static final HashFunction MURMUR3_128 = new Murmur3_128HashFunction(0); + + static final HashFunction GOOD_FAST_HASH_128 = + new Murmur3_128HashFunction(Hashing.GOOD_FAST_HASH_SEED); + + // TODO(user): when the shortcuts are implemented, update BloomFilterStrategies + private final int seed; + + Murmur3_128HashFunction(int seed) { + this.seed = seed; + } + + @Override + public int bits() { + return 128; + } + + @Override + public Hasher newHasher() { + return new Murmur3_128Hasher(seed); + } + + @Override + public String toString() { + return "Hashing.murmur3_128(" + seed + ")"; + } + + @Override + public boolean equals(Object object) { + if (object instanceof Murmur3_128HashFunction) { + Murmur3_128HashFunction other = (Murmur3_128HashFunction) object; + return seed == other.seed; + } + return false; + } + + @Override + public int hashCode() { + return getClass().hashCode() ^ seed; + } + + private static final class Murmur3_128Hasher extends AbstractStreamingHasher { + private static final int CHUNK_SIZE = 16; + private static final long C1 = 0x87c37b91114253d5L; + private static final long C2 = 0x4cf5ad432745937fL; + private long h1; + private long h2; + private int length; + + Murmur3_128Hasher(int seed) { + super(CHUNK_SIZE); + this.h1 = seed; + this.h2 = seed; + this.length = 0; + } + + @Override + protected void process(ByteBuffer bb) { + long k1 = bb.getLong(); + long k2 = bb.getLong(); + bmix64(k1, k2); + length += CHUNK_SIZE; + } + + private void bmix64(long k1, long k2) { + h1 ^= mixK1(k1); + + h1 = Long.rotateLeft(h1, 27); + h1 += h2; + h1 = h1 * 5 + 0x52dce729; + + h2 ^= mixK2(k2); + + h2 = Long.rotateLeft(h2, 31); + h2 += h1; + h2 = h2 * 5 + 0x38495ab5; + } + + @Override + protected void processRemaining(ByteBuffer bb) { + long k1 = 0; + long k2 = 0; + length += bb.remaining(); + switch (bb.remaining()) { + case 15: + k2 ^= (long) toInt(bb.get(14)) << 48; // fall through + case 14: + k2 ^= (long) toInt(bb.get(13)) << 40; // fall through + case 13: + k2 ^= (long) toInt(bb.get(12)) << 32; // fall through + case 12: + k2 ^= (long) toInt(bb.get(11)) << 24; // fall through + case 11: + k2 ^= (long) toInt(bb.get(10)) << 16; // fall through + case 10: + k2 ^= (long) toInt(bb.get(9)) << 8; // fall through + case 9: + k2 ^= (long) toInt(bb.get(8)); // fall through + case 8: + k1 ^= bb.getLong(); + break; + case 7: + k1 ^= (long) toInt(bb.get(6)) << 48; // fall through + case 6: + k1 ^= (long) toInt(bb.get(5)) << 40; // fall through + case 5: + k1 ^= (long) toInt(bb.get(4)) << 32; // fall through + case 4: + k1 ^= (long) toInt(bb.get(3)) << 24; // fall through + case 3: + k1 ^= (long) toInt(bb.get(2)) << 16; // fall through + case 2: + k1 ^= (long) toInt(bb.get(1)) << 8; // fall through + case 1: + k1 ^= (long) toInt(bb.get(0)); + break; + default: + throw new AssertionError("Should never get here."); + } + h1 ^= mixK1(k1); + h2 ^= mixK2(k2); + } + + @Override + protected HashCode makeHash() { + h1 ^= length; + h2 ^= length; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + return HashCode.fromBytesNoCopy( + ByteBuffer.wrap(new byte[CHUNK_SIZE]) + .order(ByteOrder.LITTLE_ENDIAN) + .putLong(h1) + .putLong(h2) + .array()); + } + + private static long fmix64(long k) { + k ^= k >>> 33; + k *= 0xff51afd7ed558ccdL; + k ^= k >>> 33; + k *= 0xc4ceb9fe1a85ec53L; + k ^= k >>> 33; + return k; + } + + private static long mixK1(long k1) { + k1 *= C1; + k1 = Long.rotateLeft(k1, 31); + k1 *= C2; + return k1; + } + + private static long mixK2(long k2) { + k2 *= C2; + k2 = Long.rotateLeft(k2, 33); + k2 *= C1; + return k2; + } + } + + private static final long serialVersionUID = 0L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Murmur3_32HashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Murmur3_32HashFunction.java new file mode 100644 index 0000000000000..f5a3efda8b3ba --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Murmur3_32HashFunction.java @@ -0,0 +1,414 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.base.Charsets; +import org.apache.iotdb.google.common.primitives.Chars; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.primitives.Longs; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.primitives.UnsignedBytes.toInt; + +/** + * See MurmurHash3_x86_32 in the C++ + * implementation. + * + * @author Austin Appleby + * @author Dimitris Andreou + * @author Kurt Alfred Kluever + */ +@ElementTypesAreNonnullByDefault +final class Murmur3_32HashFunction extends AbstractHashFunction implements Serializable { + static final HashFunction MURMUR3_32 = + new Murmur3_32HashFunction(0, /* supplementaryPlaneFix= */ false); + static final HashFunction MURMUR3_32_FIXED = + new Murmur3_32HashFunction(0, /* supplementaryPlaneFix= */ true); + + // We can include the non-BMP fix here because Hashing.goodFastHash stresses that the hash is a + // temporary-use one. Therefore it shouldn't be persisted. + static final HashFunction GOOD_FAST_HASH_32 = + new Murmur3_32HashFunction(Hashing.GOOD_FAST_HASH_SEED, /* supplementaryPlaneFix= */ true); + + private static final int CHUNK_SIZE = 4; + + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + + private final int seed; + private final boolean supplementaryPlaneFix; + + Murmur3_32HashFunction(int seed, boolean supplementaryPlaneFix) { + this.seed = seed; + this.supplementaryPlaneFix = supplementaryPlaneFix; + } + + @Override + public int bits() { + return 32; + } + + @Override + public Hasher newHasher() { + return new Murmur3_32Hasher(seed); + } + + @Override + public String toString() { + return "Hashing.murmur3_32(" + seed + ")"; + } + + @Override + public boolean equals(Object object) { + if (object instanceof Murmur3_32HashFunction) { + Murmur3_32HashFunction other = (Murmur3_32HashFunction) object; + return seed == other.seed && supplementaryPlaneFix == other.supplementaryPlaneFix; + } + return false; + } + + @Override + public int hashCode() { + return getClass().hashCode() ^ seed; + } + + @Override + public HashCode hashInt(int input) { + int k1 = mixK1(input); + int h1 = mixH1(seed, k1); + + return fmix(h1, Ints.BYTES); + } + + @Override + public HashCode hashLong(long input) { + int low = (int) input; + int high = (int) (input >>> 32); + + int k1 = mixK1(low); + int h1 = mixH1(seed, k1); + + k1 = mixK1(high); + h1 = mixH1(h1, k1); + + return fmix(h1, Longs.BYTES); + } + + @Override + public HashCode hashUnencodedChars(CharSequence input) { + int h1 = seed; + + // step through the CharSequence 2 chars at a time + for (int i = 1; i < input.length(); i += 2) { + int k1 = input.charAt(i - 1) | (input.charAt(i) << 16); + k1 = mixK1(k1); + h1 = mixH1(h1, k1); + } + + // deal with any remaining characters + if ((input.length() & 1) == 1) { + int k1 = input.charAt(input.length() - 1); + k1 = mixK1(k1); + h1 ^= k1; + } + + return fmix(h1, Chars.BYTES * input.length()); + } + + @SuppressWarnings("deprecation") // need to use Charsets for Android tests to pass + @Override + public HashCode hashString(CharSequence input, Charset charset) { + if (Charsets.UTF_8.equals(charset)) { + int utf16Length = input.length(); + int h1 = seed; + int i = 0; + int len = 0; + + // This loop optimizes for pure ASCII. + while (i + 4 <= utf16Length) { + char c0 = input.charAt(i); + char c1 = input.charAt(i + 1); + char c2 = input.charAt(i + 2); + char c3 = input.charAt(i + 3); + if (c0 < 0x80 && c1 < 0x80 && c2 < 0x80 && c3 < 0x80) { + int k1 = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24); + k1 = mixK1(k1); + h1 = mixH1(h1, k1); + i += 4; + len += 4; + } else { + break; + } + } + + long buffer = 0; + int shift = 0; + for (; i < utf16Length; i++) { + char c = input.charAt(i); + if (c < 0x80) { + buffer |= (long) c << shift; + shift += 8; + len++; + } else if (c < 0x800) { + buffer |= charToTwoUtf8Bytes(c) << shift; + shift += 16; + len += 2; + } else if (c < Character.MIN_SURROGATE || c > Character.MAX_SURROGATE) { + buffer |= charToThreeUtf8Bytes(c) << shift; + shift += 24; + len += 3; + } else { + int codePoint = Character.codePointAt(input, i); + if (codePoint == c) { + // not a valid code point; let the JDK handle invalid Unicode + return hashBytes(input.toString().getBytes(charset)); + } + i++; + buffer |= codePointToFourUtf8Bytes(codePoint) << shift; + if (supplementaryPlaneFix) { // bug compatibility: earlier versions did not have this add + shift += 32; + } + len += 4; + } + + if (shift >= 32) { + int k1 = mixK1((int) buffer); + h1 = mixH1(h1, k1); + buffer = buffer >>> 32; + shift -= 32; + } + } + + int k1 = mixK1((int) buffer); + h1 ^= k1; + return fmix(h1, len); + } else { + return hashBytes(input.toString().getBytes(charset)); + } + } + + @Override + public HashCode hashBytes(byte[] input, int off, int len) { + checkPositionIndexes(off, off + len, input.length); + int h1 = seed; + int i; + for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) { + int k1 = mixK1(getIntLittleEndian(input, off + i)); + h1 = mixH1(h1, k1); + } + + int k1 = 0; + for (int shift = 0; i < len; i++, shift += 8) { + k1 ^= toInt(input[off + i]) << shift; + } + h1 ^= mixK1(k1); + return fmix(h1, len); + } + + private static int getIntLittleEndian(byte[] input, int offset) { + return Ints.fromBytes(input[offset + 3], input[offset + 2], input[offset + 1], input[offset]); + } + + private static int mixK1(int k1) { + k1 *= C1; + k1 = Integer.rotateLeft(k1, 15); + k1 *= C2; + return k1; + } + + private static int mixH1(int h1, int k1) { + h1 ^= k1; + h1 = Integer.rotateLeft(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + return h1; + } + + // Finalization mix - force all bits of a hash block to avalanche + private static HashCode fmix(int h1, int length) { + h1 ^= length; + h1 ^= h1 >>> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >>> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >>> 16; + return HashCode.fromInt(h1); + } + + private static final class Murmur3_32Hasher extends AbstractHasher { + private int h1; + private long buffer; + private int shift; + private int length; + private boolean isDone; + + Murmur3_32Hasher(int seed) { + this.h1 = seed; + this.length = 0; + isDone = false; + } + + private void update(int nBytes, long update) { + // 1 <= nBytes <= 4 + buffer |= (update & 0xFFFFFFFFL) << shift; + shift += nBytes * 8; + length += nBytes; + + if (shift >= 32) { + h1 = mixH1(h1, mixK1((int) buffer)); + buffer >>>= 32; + shift -= 32; + } + } + + @Override + public Hasher putByte(byte b) { + update(1, b & 0xFF); + return this; + } + + @Override + public Hasher putBytes(byte[] bytes, int off, int len) { + checkPositionIndexes(off, off + len, bytes.length); + int i; + for (i = 0; i + 4 <= len; i += 4) { + update(4, getIntLittleEndian(bytes, off + i)); + } + for (; i < len; i++) { + putByte(bytes[off + i]); + } + return this; + } + + @Override + public Hasher putBytes(ByteBuffer buffer) { + ByteOrder bo = buffer.order(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + while (buffer.remaining() >= 4) { + putInt(buffer.getInt()); + } + while (buffer.hasRemaining()) { + putByte(buffer.get()); + } + buffer.order(bo); + return this; + } + + @Override + public Hasher putInt(int i) { + update(4, i); + return this; + } + + @Override + public Hasher putLong(long l) { + update(4, (int) l); + update(4, l >>> 32); + return this; + } + + @Override + public Hasher putChar(char c) { + update(2, c); + return this; + } + + @SuppressWarnings("deprecation") // need to use Charsets for Android tests to pass + @Override + public Hasher putString(CharSequence input, Charset charset) { + if (Charsets.UTF_8.equals(charset)) { + int utf16Length = input.length(); + int i = 0; + + // This loop optimizes for pure ASCII. + while (i + 4 <= utf16Length) { + char c0 = input.charAt(i); + char c1 = input.charAt(i + 1); + char c2 = input.charAt(i + 2); + char c3 = input.charAt(i + 3); + if (c0 < 0x80 && c1 < 0x80 && c2 < 0x80 && c3 < 0x80) { + update(4, c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)); + i += 4; + } else { + break; + } + } + + for (; i < utf16Length; i++) { + char c = input.charAt(i); + if (c < 0x80) { + update(1, c); + } else if (c < 0x800) { + update(2, charToTwoUtf8Bytes(c)); + } else if (c < Character.MIN_SURROGATE || c > Character.MAX_SURROGATE) { + update(3, charToThreeUtf8Bytes(c)); + } else { + int codePoint = Character.codePointAt(input, i); + if (codePoint == c) { + // fall back to JDK getBytes instead of trying to handle invalid surrogates ourselves + putBytes(input.subSequence(i, utf16Length).toString().getBytes(charset)); + return this; + } + i++; + update(4, codePointToFourUtf8Bytes(codePoint)); + } + } + return this; + } else { + return super.putString(input, charset); + } + } + + @Override + public HashCode hash() { + checkState(!isDone); + isDone = true; + h1 ^= mixK1((int) buffer); + return fmix(h1, length); + } + } + + private static long codePointToFourUtf8Bytes(int codePoint) { + // codePoint has at most 21 bits + return ((0xFL << 4) | (codePoint >>> 18)) + | ((0x80L | (0x3F & (codePoint >>> 12))) << 8) + | ((0x80L | (0x3F & (codePoint >>> 6))) << 16) + | ((0x80L | (0x3F & codePoint)) << 24); + } + + private static long charToThreeUtf8Bytes(char c) { + return ((0x7L << 5) | (c >>> 12)) + | ((0x80 | (0x3F & (c >>> 6))) << 8) + | ((0x80 | (0x3F & c)) << 16); + } + + private static long charToTwoUtf8Bytes(char c) { + // c has at most 11 bits + return ((0x3L << 6) | (c >>> 6)) | ((0x80 | (0x3F & c)) << 8); + } + + private static final long serialVersionUID = 0L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ParametricNullness.java new file mode 100644 index 0000000000000..b8a69190ca3b1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/PrimitiveSink.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/PrimitiveSink.java new file mode 100644 index 0000000000000..e121160deff61 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/PrimitiveSink.java @@ -0,0 +1,117 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.Beta; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * An object which can receive a stream of primitive values. + * + * @author Kevin Bourrillion + * @since 12.0 (in 11.0 as {@code Sink}) + */ +@Beta +@ElementTypesAreNonnullByDefault +public interface PrimitiveSink { + /** + * Puts a byte into this sink. + * + * @param b a byte + * @return this instance + */ + PrimitiveSink putByte(byte b); + + /** + * Puts an array of bytes into this sink. + * + * @param bytes a byte array + * @return this instance + */ + PrimitiveSink putBytes(byte[] bytes); + + /** + * Puts a chunk of an array of bytes into this sink. {@code bytes[off]} is the first byte written, + * {@code bytes[off + len - 1]} is the last. + * + * @param bytes a byte array + * @param off the start offset in the array + * @param len the number of bytes to write + * @return this instance + * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > bytes.length} or + * {@code len < 0} + */ + PrimitiveSink putBytes(byte[] bytes, int off, int len); + + /** + * Puts the remaining bytes of a byte buffer into this sink. {@code bytes.position()} is the first + * byte written, {@code bytes.limit() - 1} is the last. The position of the buffer will be equal + * to the limit when this method returns. + * + * @param bytes a byte buffer + * @return this instance + * @since 23.0 + */ + PrimitiveSink putBytes(ByteBuffer bytes); + + /** Puts a short into this sink. */ + PrimitiveSink putShort(short s); + + /** Puts an int into this sink. */ + PrimitiveSink putInt(int i); + + /** Puts a long into this sink. */ + PrimitiveSink putLong(long l); + + /** Puts a float into this sink. */ + PrimitiveSink putFloat(float f); + + /** Puts a double into this sink. */ + PrimitiveSink putDouble(double d); + + /** Puts a boolean into this sink. */ + PrimitiveSink putBoolean(boolean b); + + /** Puts a character into this sink. */ + PrimitiveSink putChar(char c); + + /** + * Puts each 16-bit code unit from the {@link CharSequence} into this sink. + * + *

Warning: This method will produce different output than most other languages do when + * running on the equivalent input. For cross-language compatibility, use {@link #putString}, + * usually with a charset of UTF-8. For other use cases, use {@code putUnencodedChars}. + * + * @since 15.0 (since 11.0 as putString(CharSequence)) + */ + PrimitiveSink putUnencodedChars(CharSequence charSequence); + + /** + * Puts a string into this sink using the given charset. + * + *

Warning: This method, which reencodes the input before processing it, is useful only + * for cross-language compatibility. For other use cases, prefer {@link #putUnencodedChars}, which + * is faster, produces the same output across Java releases, and processes every {@code char} in + * the input, even if some are invalid. + */ + PrimitiveSink putString(CharSequence charSequence, Charset charset); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/SipHashFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/SipHashFunction.java new file mode 100644 index 0000000000000..157a93e7c1ff6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/SipHashFunction.java @@ -0,0 +1,182 @@ +/* + * 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.iotdb.google.common.hash; + +import java.io.Serializable; +import java.nio.ByteBuffer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * {@link HashFunction} implementation of SipHash-c-d. + * + * @author Kurt Alfred Kluever + * @author Jean-Philippe Aumasson + * @author Daniel J. Bernstein + */ +@ElementTypesAreNonnullByDefault +final class SipHashFunction extends AbstractHashFunction implements Serializable { + static final HashFunction SIP_HASH_24 = + new SipHashFunction(2, 4, 0x0706050403020100L, 0x0f0e0d0c0b0a0908L); + + // The number of compression rounds. + private final int c; + // The number of finalization rounds. + private final int d; + // Two 64-bit keys (represent a single 128-bit key). + private final long k0; + private final long k1; + + /** + * @param c the number of compression rounds (must be positive) + * @param d the number of finalization rounds (must be positive) + * @param k0 the first half of the key + * @param k1 the second half of the key + */ + SipHashFunction(int c, int d, long k0, long k1) { + checkArgument( + c > 0, "The number of SipRound iterations (c=%s) during Compression must be positive.", c); + checkArgument( + d > 0, "The number of SipRound iterations (d=%s) during Finalization must be positive.", d); + this.c = c; + this.d = d; + this.k0 = k0; + this.k1 = k1; + } + + @Override + public int bits() { + return 64; + } + + @Override + public Hasher newHasher() { + return new SipHasher(c, d, k0, k1); + } + + // TODO(kak): Implement and benchmark the hashFoo() shortcuts. + + @Override + public String toString() { + return "Hashing.sipHash" + c + "" + d + "(" + k0 + ", " + k1 + ")"; + } + + @Override + public boolean equals(Object object) { + if (object instanceof SipHashFunction) { + SipHashFunction other = (SipHashFunction) object; + return (c == other.c) && (d == other.d) && (k0 == other.k0) && (k1 == other.k1); + } + return false; + } + + @Override + public int hashCode() { + return (int) (getClass().hashCode() ^ c ^ d ^ k0 ^ k1); + } + + private static final class SipHasher extends AbstractStreamingHasher { + private static final int CHUNK_SIZE = 8; + + // The number of compression rounds. + private final int c; + // The number of finalization rounds. + private final int d; + + // Four 64-bit words of internal state. + // The initial state corresponds to the ASCII string "somepseudorandomlygeneratedbytes", + // big-endian encoded. There is nothing special about this value; the only requirement + // was some asymmetry so that the initial v0 and v1 differ from v2 and v3. + private long v0 = 0x736f6d6570736575L; + private long v1 = 0x646f72616e646f6dL; + private long v2 = 0x6c7967656e657261L; + private long v3 = 0x7465646279746573L; + + // The number of bytes in the input. + private long b = 0; + + // The final 64-bit chunk includes the last 0 through 7 bytes of m followed by null bytes + // and ending with a byte encoding the positive integer b mod 256. + private long finalM = 0; + + SipHasher(int c, int d, long k0, long k1) { + super(CHUNK_SIZE); + this.c = c; + this.d = d; + this.v0 ^= k0; + this.v1 ^= k1; + this.v2 ^= k0; + this.v3 ^= k1; + } + + @Override + protected void process(ByteBuffer buffer) { + b += CHUNK_SIZE; + processM(buffer.getLong()); + } + + @Override + protected void processRemaining(ByteBuffer buffer) { + b += buffer.remaining(); + for (int i = 0; buffer.hasRemaining(); i += 8) { + finalM ^= (buffer.get() & 0xFFL) << i; + } + } + + @Override + protected HashCode makeHash() { + // End with a byte encoding the positive integer b mod 256. + finalM ^= b << 56; + processM(finalM); + + // Finalization + v2 ^= 0xFFL; + sipRound(d); + return HashCode.fromLong(v0 ^ v1 ^ v2 ^ v3); + } + + private void processM(long m) { + v3 ^= m; + sipRound(c); + v0 ^= m; + } + + private void sipRound(int iterations) { + for (int i = 0; i < iterations; i++) { + v0 += v1; + v2 += v3; + v1 = Long.rotateLeft(v1, 13); + v3 = Long.rotateLeft(v3, 16); + v1 ^= v0; + v3 ^= v2; + v0 = Long.rotateLeft(v0, 32); + v2 += v1; + v0 += v3; + v1 = Long.rotateLeft(v1, 17); + v3 = Long.rotateLeft(v3, 21); + v1 ^= v2; + v3 ^= v0; + v2 = Long.rotateLeft(v2, 32); + } + } + } + + private static final long serialVersionUID = 0L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Striped64.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Striped64.java new file mode 100644 index 0000000000000..4c3c845e7987f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/Striped64.java @@ -0,0 +1,320 @@ +/* + * 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.iotdb.google.common.hash; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.Random; + +/** + * A package-local class holding common representation and mechanics for classes supporting dynamic + * striping on 64bit values. The class extends Number so that concrete subclasses must publicly do + * so. + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock; when the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed + * between pads, hoping that the JVM doesn't reorder them. + * + *

JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were + * provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + Cell(long x) { + value = x; + } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /** + * ThreadLocal holding a single-slot int array holding hash code. Unlike the JDK8 version of this + * class, we use a suboptimal int[] representation to avoid introducing a new type that can impede + * class-unloading when ThreadLocals are not removed. + */ + static final ThreadLocal threadHashCode = new ThreadLocal<>(); + + /** Generator of new random hash codes */ + static final Random rng = new Random(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** Table of cells. When non-null, size is a power of 2. */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as a fallback during table + * initialization races. Updated via CAS. + */ + transient volatile long base; + + /** Spinlock (locked via CAS) used when resizing and/or creating Cells. */ + transient volatile int busy; + + /** Package-private default constructor */ + Striped64() {} + + /** CASes the base field. */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + /** CASes the busy field from 0 to 1 to acquire lock. */ + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses should open-code this update + * function for most uses, but the virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, creating new Cells, and/or + * contention. See above for explanation. This method suffers the usual non-modularity problems of + * optimistic retry code, relying on rechecked sets of reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, int[] hc, boolean wasUncontended) { + int h; + if (hc == null) { + threadHashCode.set(hc = new int[1]); // Initialize randomly + int r = rng.nextInt(); // Avoid zero to allow xorShift rehash + h = hc[0] = (r == 0) ? 1 : r; + } else h = hc[0]; + boolean collide = false; // True if last slot nonempty + for (; ; ) { + Cell[] as; + Cell a; + int n; + long v; + if ((as = cells) != null && (n = as.length) > 0) { + if ((a = as[(n - 1) & h]) == null) { + if (busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; + int m, j; + if ((rs = cells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + busy = 0; + } + if (created) break; + continue; // Slot is now non-empty + } + } + collide = false; + } else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if (a.cas(v = a.value, fn(v, x))) break; + else if (n >= NCPU || cells != as) collide = false; // At max size or stale + else if (!collide) collide = true; + else if (busy == 0 && casBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) rs[i] = as[i]; + cells = rs; + } + } finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + hc[0] = h; // Record index for next time + } else if (busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + busy = 0; + } + if (init) break; + } else if (casBase(v = base, fn(v, x))) break; // Fall back on using base + } + } + + /** Sets base and all cells to the given value. */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if (as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if (a != null) a.value = initialValue; + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple call + * to Unsafe.getUnsafe when integrating into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException tryReflectionInstead) { + } + try { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction() { + @Override + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", e.getCause()); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/package-info.java new file mode 100644 index 0000000000000..94e524dc517ee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/hash/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.hash; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..c6ca8c2a05ec8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.html; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/HtmlEscapers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/HtmlEscapers.java new file mode 100644 index 0000000000000..7ccd73cf57586 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/HtmlEscapers.java @@ -0,0 +1,75 @@ +/* + * 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.iotdb.google.common.html; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.escape.Escaper; +import org.apache.iotdb.google.common.escape.Escapers; + +/** + * {@code Escaper} instances suitable for strings to be included in HTML attribute values and + * most elements' text contents. When possible, avoid manual escaping by using templating + * systems and high-level APIs that provide autoescaping. One Google-authored templating system + * available for external use is Closure + * Templates. + * + *

HTML escaping is particularly tricky: For example, some + * elements' text contents must not be HTML escaped. As a result, it is impossible to escape an + * HTML document correctly without domain-specific knowledge beyond what {@code HtmlEscapers} + * provides. We strongly encourage the use of HTML templating systems. + * + * @author Sven Mawson + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class HtmlEscapers { + /** + * Returns an {@link Escaper} instance that escapes HTML metacharacters as specified by HTML 4.01. The resulting strings can be used both in + * attribute values and in most elements' text contents, provided that the HTML + * document's character encoding can encode any non-ASCII code points in the input (as UTF-8 and + * other Unicode encodings can). + * + *

Note: This escaper only performs minimal escaping to make content structurally + * compatible with HTML. Specifically, it does not perform entity replacement (symbolic or + * numeric), so it does not replace non-ASCII code points with character references. This escaper + * escapes only the following five ASCII characters: {@code '"&<>}. + */ + public static Escaper htmlEscaper() { + return HTML_ESCAPER; + } + + // For each xxxEscaper() method, please add links to external reference pages + // that are considered authoritative for the behavior of that escaper. + + private static final Escaper HTML_ESCAPER = + Escapers.builder() + .addEscape('"', """) + // Note: "'" is not defined in HTML 4.01. + .addEscape('\'', "'") + .addEscape('&', "&") + .addEscape('<', "<") + .addEscape('>', ">") + .build(); + + private HtmlEscapers() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/ParametricNullness.java new file mode 100644 index 0000000000000..e453eb04451d8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.html; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/package-info.java new file mode 100644 index 0000000000000..4c5c1490cf67a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/html/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.html; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/AppendableWriter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/AppendableWriter.java new file mode 100644 index 0000000000000..ddbdc443a6931 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/AppendableWriter.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.Writer; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Writer that places all output on an {@link Appendable} target. If the target is {@link Flushable} + * or {@link Closeable}, flush()es and close()s will also be delegated to the target. + * + * @author Alan Green + * @author Sebastian Kanthak + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +class AppendableWriter extends Writer { + private final Appendable target; + private boolean closed; + + /** + * Creates a new writer that appends everything it writes to {@code target}. + * + * @param target target to which to append output + */ + AppendableWriter(Appendable target) { + this.target = checkNotNull(target); + } + + /* + * Abstract methods from Writer + */ + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + checkNotClosed(); + // It turns out that creating a new String is usually as fast, or faster + // than wrapping cbuf in a light-weight CharSequence. + target.append(new String(cbuf, off, len)); + } + + /* + * Override a few functions for performance reasons to avoid creating unnecessary strings. + */ + + @Override + public void write(int c) throws IOException { + checkNotClosed(); + target.append((char) c); + } + + @Override + public void write(String str) throws IOException { + checkNotNull(str); + checkNotClosed(); + target.append(str); + } + + @Override + public void write(String str, int off, int len) throws IOException { + checkNotNull(str); + checkNotClosed(); + // tricky: append takes start, end pair... + target.append(str, off, off + len); + } + + @Override + public void flush() throws IOException { + checkNotClosed(); + if (target instanceof Flushable) { + ((Flushable) target).flush(); + } + } + + @Override + public void close() throws IOException { + this.closed = true; + if (target instanceof Closeable) { + ((Closeable) target).close(); + } + } + + @Override + public Writer append(char c) throws IOException { + checkNotClosed(); + target.append(c); + return this; + } + + @Override + public Writer append(CharSequence charSeq) throws IOException { + checkNotClosed(); + target.append(charSeq); + return this; + } + + @Override + public Writer append(CharSequence charSeq, int start, int end) throws IOException { + checkNotClosed(); + target.append(charSeq, start, end); + return this; + } + + private void checkNotClosed() throws IOException { + if (closed) { + throw new IOException("Cannot write to a closed writer."); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/BaseEncoding.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/BaseEncoding.java new file mode 100644 index 0000000000000..e739e452a584f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/BaseEncoding.java @@ -0,0 +1,1254 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Ascii; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.Arrays; +import java.util.Objects; + +import static java.math.RoundingMode.CEILING; +import static java.math.RoundingMode.FLOOR; +import static java.math.RoundingMode.UNNECESSARY; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.math.IntMath.divide; +import static org.apache.iotdb.google.common.math.IntMath.log2; + +/** + * A binary encoding scheme for reversibly translating between byte sequences and printable ASCII + * strings. This class includes several constants for encoding schemes specified by RFC 4648. For example, the expression: + * + *

{@code
+ * BaseEncoding.base32().encode("foo".getBytes(Charsets.US_ASCII))
+ * }
+ * + *

returns the string {@code "MZXW6==="}, and + * + *

{@code
+ * byte[] decoded = BaseEncoding.base32().decode("MZXW6===");
+ * }
+ * + *

...returns the ASCII bytes of the string {@code "foo"}. + * + *

By default, {@code BaseEncoding}'s behavior is relatively strict and in accordance with RFC + * 4648. Decoding rejects characters in the wrong case, though padding is optional. To modify + * encoding and decoding behavior, use configuration methods to obtain a new encoding with modified + * behavior: + * + *

{@code
+ * BaseEncoding.base16().lowerCase().decode("deadbeef");
+ * }
+ * + *

Warning: BaseEncoding instances are immutable. Invoking a configuration method has no effect + * on the receiving instance; you must store and use the new encoding instance it returns, instead. + * + *

{@code
+ * // Do NOT do this
+ * BaseEncoding hex = BaseEncoding.base16();
+ * hex.lowerCase(); // does nothing!
+ * return hex.decode("deadbeef"); // throws an IllegalArgumentException
+ * }
+ * + *

It is guaranteed that {@code encoding.decode(encoding.encode(x))} is always equal to {@code + * x}, but the reverse does not necessarily hold. + * + * + * + * + * + * + * + * + * + *
Encodings
Encoding + * Alphabet + * {@code char:byte} ratio + * Default padding + * Comments + *
{@link #base16()} + * 0-9 A-F + * 2.00 + * N/A + * Traditional hexadecimal. Defaults to upper case. + *
{@link #base32()} + * A-Z 2-7 + * 1.60 + * = + * Human-readable; no possibility of mixing up 0/O or 1/I. Defaults to upper case. + *
{@link #base32Hex()} + * 0-9 A-V + * 1.60 + * = + * "Numerical" base 32; extended from the traditional hex alphabet. Defaults to upper case. + *
{@link #base64()} + * A-Z a-z 0-9 + / + * 1.33 + * = + * + *
{@link #base64Url()} + * A-Z a-z 0-9 - _ + * 1.33 + * = + * Safe to use as filenames, or to pass in URLs without escaping + *
+ * + *

All instances of this class are immutable, so they may be stored safely as static constants. + * + * @author Louis Wasserman + * @since 14.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class BaseEncoding { + // TODO(lowasser): consider making encodeTo(Appendable, byte[], int, int) public. + + BaseEncoding() {} + + /** + * Exception indicating invalid base-encoded input encountered while decoding. + * + * @author Louis Wasserman + * @since 15.0 + */ + public static final class DecodingException extends IOException { + DecodingException(String message) { + super(message); + } + + DecodingException(Throwable cause) { + super(cause); + } + } + + /** Encodes the specified byte array, and returns the encoded {@code String}. */ + public String encode(byte[] bytes) { + return encode(bytes, 0, bytes.length); + } + + /** + * Encodes the specified range of the specified byte array, and returns the encoded {@code + * String}. + */ + public final String encode(byte[] bytes, int off, int len) { + checkPositionIndexes(off, off + len, bytes.length); + StringBuilder result = new StringBuilder(maxEncodedSize(len)); + try { + encodeTo(result, bytes, off, len); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return result.toString(); + } + + /** + * Returns an {@code OutputStream} that encodes bytes using this encoding into the specified + * {@code Writer}. When the returned {@code OutputStream} is closed, so is the backing {@code + * Writer}. + */ + @J2ktIncompatible + @GwtIncompatible // Writer,OutputStream + public abstract OutputStream encodingStream(Writer writer); + + /** + * Returns a {@code ByteSink} that writes base-encoded bytes to the specified {@code CharSink}. + */ + @J2ktIncompatible + @GwtIncompatible // ByteSink,CharSink + public final ByteSink encodingSink(CharSink encodedSink) { + checkNotNull(encodedSink); + return new ByteSink() { + @Override + public OutputStream openStream() throws IOException { + return encodingStream(encodedSink.openStream()); + } + }; + } + + // TODO(lowasser): document the extent of leniency, probably after adding ignore(CharMatcher) + + private static byte[] extract(byte[] result, int length) { + if (length == result.length) { + return result; + } + byte[] trunc = new byte[length]; + System.arraycopy(result, 0, trunc, 0, length); + return trunc; + } + + /** + * Determines whether the specified character sequence is a valid encoded string according to this + * encoding. + * + * @since 20.0 + */ + public abstract boolean canDecode(CharSequence chars); + + /** + * Decodes the specified character sequence, and returns the resulting {@code byte[]}. This is the + * inverse operation to {@link #encode(byte[])}. + * + * @throws IllegalArgumentException if the input is not a valid encoded string according to this + * encoding. + */ + public final byte[] decode(CharSequence chars) { + try { + return decodeChecked(chars); + } catch (DecodingException badInput) { + throw new IllegalArgumentException(badInput); + } + } + + /** + * Decodes the specified character sequence, and returns the resulting {@code byte[]}. This is the + * inverse operation to {@link #encode(byte[])}. + * + * @throws DecodingException if the input is not a valid encoded string according to this + * encoding. + */ + final byte[] decodeChecked(CharSequence chars) throws DecodingException { + chars = trimTrailingPadding(chars); + byte[] tmp = new byte[maxDecodedSize(chars.length())]; + int len = decodeTo(tmp, chars); + return extract(tmp, len); + } + + /** + * Returns an {@code InputStream} that decodes base-encoded input from the specified {@code + * Reader}. The returned stream throws a {@link DecodingException} upon decoding-specific errors. + */ + @J2ktIncompatible + @GwtIncompatible // Reader,InputStream + public abstract InputStream decodingStream(Reader reader); + + /** + * Returns a {@code ByteSource} that reads base-encoded bytes from the specified {@code + * CharSource}. + */ + @J2ktIncompatible + @GwtIncompatible // ByteSource,CharSource + public final ByteSource decodingSource(CharSource encodedSource) { + checkNotNull(encodedSource); + return new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return decodingStream(encodedSource.openStream()); + } + }; + } + + // Implementations for encoding/decoding + + abstract int maxEncodedSize(int bytes); + + abstract void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOException; + + abstract int maxDecodedSize(int chars); + + abstract int decodeTo(byte[] target, CharSequence chars) throws DecodingException; + + CharSequence trimTrailingPadding(CharSequence chars) { + return checkNotNull(chars); + } + + // Modified encoding generators + + /** + * Returns an encoding that behaves equivalently to this encoding, but omits any padding + * characters as specified by RFC 4648 + * section 3.2, Padding of Encoded Data. + */ + public abstract BaseEncoding omitPadding(); + + /** + * Returns an encoding that behaves equivalently to this encoding, but uses an alternate character + * for padding. + * + * @throws IllegalArgumentException if this padding character is already used in the alphabet or a + * separator + */ + public abstract BaseEncoding withPadChar(char padChar); + + /** + * Returns an encoding that behaves equivalently to this encoding, but adds a separator string + * after every {@code n} characters. Any occurrences of any characters that occur in the separator + * are skipped over in decoding. + * + * @throws IllegalArgumentException if any alphabet or padding characters appear in the separator + * string, or if {@code n <= 0} + * @throws UnsupportedOperationException if this encoding already uses a separator + */ + public abstract BaseEncoding withSeparator(String separator, int n); + + /** + * Returns an encoding that behaves equivalently to this encoding, but encodes and decodes with + * uppercase letters. Padding and separator characters remain in their original case. + * + * @throws IllegalStateException if the alphabet used by this encoding contains mixed upper- and + * lower-case characters + */ + public abstract BaseEncoding upperCase(); + + /** + * Returns an encoding that behaves equivalently to this encoding, but encodes and decodes with + * lowercase letters. Padding and separator characters remain in their original case. + * + * @throws IllegalStateException if the alphabet used by this encoding contains mixed upper- and + * lower-case characters + */ + public abstract BaseEncoding lowerCase(); + + /** + * Returns an encoding that behaves equivalently to this encoding, but decodes letters without + * regard to case. + * + * @throws IllegalStateException if the alphabet used by this encoding contains mixed upper- and + * lower-case characters + * @since 32.0.0 + */ + public abstract BaseEncoding ignoreCase(); + + private static final BaseEncoding BASE64 = + new Base64Encoding( + "base64()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", '='); + + /** + * The "base64" base encoding specified by RFC 4648 section 4, Base 64 Encoding. + * (This is the same as the base 64 encoding from RFC 3548.) + * + *

The character {@code '='} is used for padding, but can be {@linkplain #omitPadding() + * omitted} or {@linkplain #withPadChar(char) replaced}. + * + *

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in + * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}. + */ + public static BaseEncoding base64() { + return BASE64; + } + + private static final BaseEncoding BASE64_URL = + new Base64Encoding( + "base64Url()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", '='); + + /** + * The "base64url" encoding specified by RFC 4648 section 5, Base 64 Encoding + * with URL and Filename Safe Alphabet, also sometimes referred to as the "web safe Base64." (This + * is the same as the base 64 encoding with URL and filename safe alphabet from RFC 3548.) + * + *

The character {@code '='} is used for padding, but can be {@linkplain #omitPadding() + * omitted} or {@linkplain #withPadChar(char) replaced}. + * + *

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in + * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}. + */ + public static BaseEncoding base64Url() { + return BASE64_URL; + } + + private static final BaseEncoding BASE32 = + new StandardBaseEncoding("base32()", "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", '='); + + /** + * The "base32" encoding specified by RFC + * 4648 section 6, Base 32 Encoding. (This is the same as the base 32 encoding from RFC 3548.) + * + *

The character {@code '='} is used for padding, but can be {@linkplain #omitPadding() + * omitted} or {@linkplain #withPadChar(char) replaced}. + * + *

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in + * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}. + */ + public static BaseEncoding base32() { + return BASE32; + } + + private static final BaseEncoding BASE32_HEX = + new StandardBaseEncoding("base32Hex()", "0123456789ABCDEFGHIJKLMNOPQRSTUV", '='); + + /** + * The "base32hex" encoding specified by RFC 4648 section 7, Base 32 Encoding + * with Extended Hex Alphabet. There is no corresponding encoding in RFC 3548. + * + *

The character {@code '='} is used for padding, but can be {@linkplain #omitPadding() + * omitted} or {@linkplain #withPadChar(char) replaced}. + * + *

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in + * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}. + */ + public static BaseEncoding base32Hex() { + return BASE32_HEX; + } + + private static final BaseEncoding BASE16 = new Base16Encoding("base16()", "0123456789ABCDEF"); + + /** + * The "base16" encoding specified by RFC + * 4648 section 8, Base 16 Encoding. (This is the same as the base 16 encoding from RFC 3548.) This is commonly known as + * "hexadecimal" format. + * + *

No padding is necessary in base 16, so {@link #withPadChar(char)} and {@link #omitPadding()} + * have no effect. + * + *

No line feeds are added by default, as per RFC 4648 section 3.1, Line Feeds in + * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}. + */ + public static BaseEncoding base16() { + return BASE16; + } + + private static final class Alphabet { + private final String name; + // this is meant to be immutable -- don't modify it! + private final char[] chars; + final int mask; + final int bitsPerChar; + final int charsPerChunk; + final int bytesPerChunk; + private final byte[] decodabet; + private final boolean[] validPadding; + private final boolean ignoreCase; + + Alphabet(String name, char[] chars) { + this(name, chars, decodabetFor(chars), /* ignoreCase= */ false); + } + + private Alphabet(String name, char[] chars, byte[] decodabet, boolean ignoreCase) { + this.name = checkNotNull(name); + this.chars = checkNotNull(chars); + try { + this.bitsPerChar = log2(chars.length, UNNECESSARY); + } catch (ArithmeticException e) { + throw new IllegalArgumentException("Illegal alphabet length " + chars.length, e); + } + + // Compute how input bytes are chunked. For example, with base64 we chunk every 3 bytes into + // 4 characters. We have bitsPerChar == 6, charsPerChunk == 4, and bytesPerChunk == 3. + // We're looking for the smallest charsPerChunk such that bitsPerChar * charsPerChunk is a + // multiple of 8. A multiple of 8 has 3 low zero bits, so we just need to figure out how many + // extra zero bits we need to add to the end of bitsPerChar to get 3 in total. + // The logic here would be wrong for bitsPerChar > 8, but since we require distinct ASCII + // characters that can't happen. + int zeroesInBitsPerChar = Integer.numberOfTrailingZeros(bitsPerChar); + this.charsPerChunk = 1 << (3 - zeroesInBitsPerChar); + this.bytesPerChunk = bitsPerChar >> zeroesInBitsPerChar; + + this.mask = chars.length - 1; + + this.decodabet = decodabet; + + boolean[] validPadding = new boolean[charsPerChunk]; + for (int i = 0; i < bytesPerChunk; i++) { + validPadding[divide(i * 8, bitsPerChar, CEILING)] = true; + } + this.validPadding = validPadding; + this.ignoreCase = ignoreCase; + } + + private static byte[] decodabetFor(char[] chars) { + byte[] decodabet = new byte[Ascii.MAX + 1]; + Arrays.fill(decodabet, (byte) -1); + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + checkArgument(c < decodabet.length, "Non-ASCII character: %s", c); + checkArgument(decodabet[c] == -1, "Duplicate character: %s", c); + decodabet[c] = (byte) i; + } + return decodabet; + } + + /** Returns an equivalent {@code Alphabet} except it ignores case. */ + Alphabet ignoreCase() { + if (ignoreCase) { + return this; + } + + // We can't use .clone() because of GWT. + byte[] newDecodabet = Arrays.copyOf(decodabet, decodabet.length); + for (int upper = 'A'; upper <= 'Z'; upper++) { + int lower = upper | 0x20; + byte decodeUpper = decodabet[upper]; + byte decodeLower = decodabet[lower]; + if (decodeUpper == -1) { + newDecodabet[upper] = decodeLower; + } else { + checkState( + decodeLower == -1, + "Can't ignoreCase() since '%s' and '%s' encode different values", + (char) upper, + (char) lower); + newDecodabet[lower] = decodeUpper; + } + } + return new Alphabet(name + ".ignoreCase()", chars, newDecodabet, /* ignoreCase= */ true); + } + + char encode(int bits) { + return chars[bits]; + } + + boolean isValidPaddingStartPosition(int index) { + return validPadding[index % charsPerChunk]; + } + + boolean canDecode(char ch) { + return ch <= Ascii.MAX && decodabet[ch] != -1; + } + + int decode(char ch) throws DecodingException { + if (ch > Ascii.MAX) { + throw new DecodingException("Unrecognized character: 0x" + Integer.toHexString(ch)); + } + int result = decodabet[ch]; + if (result == -1) { + if (ch <= 0x20 || ch == Ascii.MAX) { + throw new DecodingException("Unrecognized character: 0x" + Integer.toHexString(ch)); + } else { + throw new DecodingException("Unrecognized character: " + ch); + } + } + return result; + } + + private boolean hasLowerCase() { + for (char c : chars) { + if (Ascii.isLowerCase(c)) { + return true; + } + } + return false; + } + + private boolean hasUpperCase() { + for (char c : chars) { + if (Ascii.isUpperCase(c)) { + return true; + } + } + return false; + } + + Alphabet upperCase() { + if (!hasLowerCase()) { + return this; + } + checkState(!hasUpperCase(), "Cannot call upperCase() on a mixed-case alphabet"); + char[] upperCased = new char[chars.length]; + for (int i = 0; i < chars.length; i++) { + upperCased[i] = Ascii.toUpperCase(chars[i]); + } + Alphabet upperCase = new Alphabet(name + ".upperCase()", upperCased); + return ignoreCase ? upperCase.ignoreCase() : upperCase; + } + + Alphabet lowerCase() { + if (!hasUpperCase()) { + return this; + } + checkState(!hasLowerCase(), "Cannot call lowerCase() on a mixed-case alphabet"); + char[] lowerCased = new char[chars.length]; + for (int i = 0; i < chars.length; i++) { + lowerCased[i] = Ascii.toLowerCase(chars[i]); + } + Alphabet lowerCase = new Alphabet(name + ".lowerCase()", lowerCased); + return ignoreCase ? lowerCase.ignoreCase() : lowerCase; + } + + public boolean matches(char c) { + return c < decodabet.length && decodabet[c] != -1; + } + + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object other) { + if (other instanceof Alphabet) { + Alphabet that = (Alphabet) other; + return this.ignoreCase == that.ignoreCase && Arrays.equals(this.chars, that.chars); + } + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(chars) + (ignoreCase ? 1231 : 1237); + } + } + + static class StandardBaseEncoding extends BaseEncoding { + final Alphabet alphabet; + + final Character paddingChar; + + StandardBaseEncoding(String name, String alphabetChars, Character paddingChar) { + this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar); + } + + StandardBaseEncoding(Alphabet alphabet, Character paddingChar) { + this.alphabet = checkNotNull(alphabet); + checkArgument( + paddingChar == null || !alphabet.matches(paddingChar), + "Padding character %s was already in alphabet", + paddingChar); + this.paddingChar = paddingChar; + } + + @Override + int maxEncodedSize(int bytes) { + return alphabet.charsPerChunk * divide(bytes, alphabet.bytesPerChunk, CEILING); + } + + @J2ktIncompatible + @GwtIncompatible // Writer,OutputStream + @Override + public OutputStream encodingStream(Writer out) { + checkNotNull(out); + return new OutputStream() { + int bitBuffer = 0; + int bitBufferLength = 0; + int writtenChars = 0; + + @Override + public void write(int b) throws IOException { + bitBuffer <<= 8; + bitBuffer |= b & 0xFF; + bitBufferLength += 8; + while (bitBufferLength >= alphabet.bitsPerChar) { + int charIndex = (bitBuffer >> (bitBufferLength - alphabet.bitsPerChar)) & alphabet.mask; + out.write(alphabet.encode(charIndex)); + writtenChars++; + bitBufferLength -= alphabet.bitsPerChar; + } + } + + @Override + public void flush() throws IOException { + out.flush(); + } + + @Override + public void close() throws IOException { + if (bitBufferLength > 0) { + int charIndex = (bitBuffer << (alphabet.bitsPerChar - bitBufferLength)) & alphabet.mask; + out.write(alphabet.encode(charIndex)); + writtenChars++; + if (paddingChar != null) { + while (writtenChars % alphabet.charsPerChunk != 0) { + out.write(paddingChar.charValue()); + writtenChars++; + } + } + } + out.close(); + } + }; + } + + @Override + void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOException { + checkNotNull(target); + checkPositionIndexes(off, off + len, bytes.length); + for (int i = 0; i < len; i += alphabet.bytesPerChunk) { + encodeChunkTo(target, bytes, off + i, Math.min(alphabet.bytesPerChunk, len - i)); + } + } + + void encodeChunkTo(Appendable target, byte[] bytes, int off, int len) throws IOException { + checkNotNull(target); + checkPositionIndexes(off, off + len, bytes.length); + checkArgument(len <= alphabet.bytesPerChunk); + long bitBuffer = 0; + for (int i = 0; i < len; ++i) { + bitBuffer |= bytes[off + i] & 0xFF; + bitBuffer <<= 8; // Add additional zero byte in the end. + } + // Position of first character is length of bitBuffer minus bitsPerChar. + int bitOffset = (len + 1) * 8 - alphabet.bitsPerChar; + int bitsProcessed = 0; + while (bitsProcessed < len * 8) { + int charIndex = (int) (bitBuffer >>> (bitOffset - bitsProcessed)) & alphabet.mask; + target.append(alphabet.encode(charIndex)); + bitsProcessed += alphabet.bitsPerChar; + } + if (paddingChar != null) { + while (bitsProcessed < alphabet.bytesPerChunk * 8) { + target.append(paddingChar.charValue()); + bitsProcessed += alphabet.bitsPerChar; + } + } + } + + @Override + int maxDecodedSize(int chars) { + return (int) ((alphabet.bitsPerChar * (long) chars + 7L) / 8L); + } + + @Override + CharSequence trimTrailingPadding(CharSequence chars) { + checkNotNull(chars); + if (paddingChar == null) { + return chars; + } + char padChar = paddingChar.charValue(); + int l; + for (l = chars.length() - 1; l >= 0; l--) { + if (chars.charAt(l) != padChar) { + break; + } + } + return chars.subSequence(0, l + 1); + } + + @Override + public boolean canDecode(CharSequence chars) { + checkNotNull(chars); + chars = trimTrailingPadding(chars); + if (!alphabet.isValidPaddingStartPosition(chars.length())) { + return false; + } + for (int i = 0; i < chars.length(); i++) { + if (!alphabet.canDecode(chars.charAt(i))) { + return false; + } + } + return true; + } + + @Override + int decodeTo(byte[] target, CharSequence chars) throws DecodingException { + checkNotNull(target); + chars = trimTrailingPadding(chars); + if (!alphabet.isValidPaddingStartPosition(chars.length())) { + throw new DecodingException("Invalid input length " + chars.length()); + } + int bytesWritten = 0; + for (int charIdx = 0; charIdx < chars.length(); charIdx += alphabet.charsPerChunk) { + long chunk = 0; + int charsProcessed = 0; + for (int i = 0; i < alphabet.charsPerChunk; i++) { + chunk <<= alphabet.bitsPerChar; + if (charIdx + i < chars.length()) { + chunk |= alphabet.decode(chars.charAt(charIdx + charsProcessed++)); + } + } + int minOffset = alphabet.bytesPerChunk * 8 - charsProcessed * alphabet.bitsPerChar; + for (int offset = (alphabet.bytesPerChunk - 1) * 8; offset >= minOffset; offset -= 8) { + target[bytesWritten++] = (byte) ((chunk >>> offset) & 0xFF); + } + } + return bytesWritten; + } + + @Override + @J2ktIncompatible + @GwtIncompatible // Reader,InputStream + public InputStream decodingStream(Reader reader) { + checkNotNull(reader); + return new InputStream() { + int bitBuffer = 0; + int bitBufferLength = 0; + int readChars = 0; + boolean hitPadding = false; + + @Override + public int read() throws IOException { + while (true) { + int readChar = reader.read(); + if (readChar == -1) { + if (!hitPadding && !alphabet.isValidPaddingStartPosition(readChars)) { + throw new DecodingException("Invalid input length " + readChars); + } + return -1; + } + readChars++; + char ch = (char) readChar; + if (paddingChar != null && paddingChar.charValue() == ch) { + if (!hitPadding + && (readChars == 1 || !alphabet.isValidPaddingStartPosition(readChars - 1))) { + throw new DecodingException("Padding cannot start at index " + readChars); + } + hitPadding = true; + } else if (hitPadding) { + throw new DecodingException( + "Expected padding character but found '" + ch + "' at index " + readChars); + } else { + bitBuffer <<= alphabet.bitsPerChar; + bitBuffer |= alphabet.decode(ch); + bitBufferLength += alphabet.bitsPerChar; + + if (bitBufferLength >= 8) { + bitBufferLength -= 8; + return (bitBuffer >> bitBufferLength) & 0xFF; + } + } + } + } + + @Override + public int read(byte[] buf, int off, int len) throws IOException { + // Overriding this to work around the fact that InputStream's default implementation of + // this method will silently swallow exceptions thrown by the single-byte read() method + // (other than on the first call to it), which in this case can cause invalid encoded + // strings to not throw an exception. + // See https://github.com/google/guava/issues/3542 + checkPositionIndexes(off, off + len, buf.length); + + int i = off; + for (; i < off + len; i++) { + int b = read(); + if (b == -1) { + int read = i - off; + return read == 0 ? -1 : read; + } + buf[i] = (byte) b; + } + return i - off; + } + + @Override + public void close() throws IOException { + reader.close(); + } + }; + } + + @Override + public BaseEncoding omitPadding() { + return (paddingChar == null) ? this : newInstance(alphabet, null); + } + + @Override + public BaseEncoding withPadChar(char padChar) { + if (8 % alphabet.bitsPerChar == 0 + || (paddingChar != null && paddingChar.charValue() == padChar)) { + return this; + } else { + return newInstance(alphabet, padChar); + } + } + + @Override + public BaseEncoding withSeparator(String separator, int afterEveryChars) { + for (int i = 0; i < separator.length(); i++) { + checkArgument( + !alphabet.matches(separator.charAt(i)), + "Separator (%s) cannot contain alphabet characters", + separator); + } + if (paddingChar != null) { + checkArgument( + separator.indexOf(paddingChar.charValue()) < 0, + "Separator (%s) cannot contain padding character", + separator); + } + return new SeparatedBaseEncoding(this, separator, afterEveryChars); + } + + private volatile BaseEncoding upperCase; + private volatile BaseEncoding lowerCase; + private volatile BaseEncoding ignoreCase; + + @Override + public BaseEncoding upperCase() { + BaseEncoding result = upperCase; + if (result == null) { + Alphabet upper = alphabet.upperCase(); + result = upperCase = (upper == alphabet) ? this : newInstance(upper, paddingChar); + } + return result; + } + + @Override + public BaseEncoding lowerCase() { + BaseEncoding result = lowerCase; + if (result == null) { + Alphabet lower = alphabet.lowerCase(); + result = lowerCase = (lower == alphabet) ? this : newInstance(lower, paddingChar); + } + return result; + } + + @Override + public BaseEncoding ignoreCase() { + BaseEncoding result = ignoreCase; + if (result == null) { + Alphabet ignore = alphabet.ignoreCase(); + result = ignoreCase = (ignore == alphabet) ? this : newInstance(ignore, paddingChar); + } + return result; + } + + BaseEncoding newInstance(Alphabet alphabet, Character paddingChar) { + return new StandardBaseEncoding(alphabet, paddingChar); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("BaseEncoding."); + builder.append(alphabet); + if (8 % alphabet.bitsPerChar != 0) { + if (paddingChar == null) { + builder.append(".omitPadding()"); + } else { + builder.append(".withPadChar('").append(paddingChar).append("')"); + } + } + return builder.toString(); + } + + @Override + public boolean equals(Object other) { + if (other instanceof StandardBaseEncoding) { + StandardBaseEncoding that = (StandardBaseEncoding) other; + return this.alphabet.equals(that.alphabet) + && Objects.equals(this.paddingChar, that.paddingChar); + } + return false; + } + + @Override + public int hashCode() { + return alphabet.hashCode() ^ Objects.hashCode(paddingChar); + } + } + + static final class Base16Encoding extends StandardBaseEncoding { + final char[] encoding = new char[512]; + + Base16Encoding(String name, String alphabetChars) { + this(new Alphabet(name, alphabetChars.toCharArray())); + } + + private Base16Encoding(Alphabet alphabet) { + super(alphabet, null); + checkArgument(alphabet.chars.length == 16); + for (int i = 0; i < 256; ++i) { + encoding[i] = alphabet.encode(i >>> 4); + encoding[i | 0x100] = alphabet.encode(i & 0xF); + } + } + + @Override + void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOException { + checkNotNull(target); + checkPositionIndexes(off, off + len, bytes.length); + for (int i = 0; i < len; ++i) { + int b = bytes[off + i] & 0xFF; + target.append(encoding[b]); + target.append(encoding[b | 0x100]); + } + } + + @Override + int decodeTo(byte[] target, CharSequence chars) throws DecodingException { + checkNotNull(target); + if (chars.length() % 2 == 1) { + throw new DecodingException("Invalid input length " + chars.length()); + } + int bytesWritten = 0; + for (int i = 0; i < chars.length(); i += 2) { + int decoded = alphabet.decode(chars.charAt(i)) << 4 | alphabet.decode(chars.charAt(i + 1)); + target[bytesWritten++] = (byte) decoded; + } + return bytesWritten; + } + + @Override + BaseEncoding newInstance(Alphabet alphabet, Character paddingChar) { + return new Base16Encoding(alphabet); + } + } + + static final class Base64Encoding extends StandardBaseEncoding { + Base64Encoding(String name, String alphabetChars, Character paddingChar) { + this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar); + } + + private Base64Encoding(Alphabet alphabet, Character paddingChar) { + super(alphabet, paddingChar); + checkArgument(alphabet.chars.length == 64); + } + + @Override + void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOException { + checkNotNull(target); + checkPositionIndexes(off, off + len, bytes.length); + int i = off; + for (int remaining = len; remaining >= 3; remaining -= 3) { + int chunk = (bytes[i++] & 0xFF) << 16 | (bytes[i++] & 0xFF) << 8 | bytes[i++] & 0xFF; + target.append(alphabet.encode(chunk >>> 18)); + target.append(alphabet.encode((chunk >>> 12) & 0x3F)); + target.append(alphabet.encode((chunk >>> 6) & 0x3F)); + target.append(alphabet.encode(chunk & 0x3F)); + } + if (i < off + len) { + encodeChunkTo(target, bytes, i, off + len - i); + } + } + + @Override + int decodeTo(byte[] target, CharSequence chars) throws DecodingException { + checkNotNull(target); + chars = trimTrailingPadding(chars); + if (!alphabet.isValidPaddingStartPosition(chars.length())) { + throw new DecodingException("Invalid input length " + chars.length()); + } + int bytesWritten = 0; + for (int i = 0; i < chars.length(); ) { + int chunk = alphabet.decode(chars.charAt(i++)) << 18; + chunk |= alphabet.decode(chars.charAt(i++)) << 12; + target[bytesWritten++] = (byte) (chunk >>> 16); + if (i < chars.length()) { + chunk |= alphabet.decode(chars.charAt(i++)) << 6; + target[bytesWritten++] = (byte) ((chunk >>> 8) & 0xFF); + if (i < chars.length()) { + chunk |= alphabet.decode(chars.charAt(i++)); + target[bytesWritten++] = (byte) (chunk & 0xFF); + } + } + } + return bytesWritten; + } + + @Override + BaseEncoding newInstance(Alphabet alphabet, Character paddingChar) { + return new Base64Encoding(alphabet, paddingChar); + } + } + + @J2ktIncompatible + @GwtIncompatible + static Reader ignoringReader(Reader delegate, String toIgnore) { + checkNotNull(delegate); + checkNotNull(toIgnore); + return new Reader() { + @Override + public int read() throws IOException { + int readChar; + do { + readChar = delegate.read(); + } while (readChar != -1 && toIgnore.indexOf((char) readChar) >= 0); + return readChar; + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void close() throws IOException { + delegate.close(); + } + }; + } + + static Appendable separatingAppendable( + Appendable delegate, String separator, int afterEveryChars) { + checkNotNull(delegate); + checkNotNull(separator); + checkArgument(afterEveryChars > 0); + return new Appendable() { + int charsUntilSeparator = afterEveryChars; + + @Override + public Appendable append(char c) throws IOException { + if (charsUntilSeparator == 0) { + delegate.append(separator); + charsUntilSeparator = afterEveryChars; + } + delegate.append(c); + charsUntilSeparator--; + return this; + } + + @Override + public Appendable append(CharSequence chars, int off, int len) { + throw new UnsupportedOperationException(); + } + + @Override + public Appendable append(CharSequence chars) { + throw new UnsupportedOperationException(); + } + }; + } + + @J2ktIncompatible + @GwtIncompatible // Writer + static Writer separatingWriter(Writer delegate, String separator, int afterEveryChars) { + Appendable separatingAppendable = separatingAppendable(delegate, separator, afterEveryChars); + return new Writer() { + @Override + public void write(int c) throws IOException { + separatingAppendable.append((char) c); + } + + @Override + public void write(char[] chars, int off, int len) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() throws IOException { + delegate.close(); + } + }; + } + + static final class SeparatedBaseEncoding extends BaseEncoding { + private final BaseEncoding delegate; + private final String separator; + private final int afterEveryChars; + + SeparatedBaseEncoding(BaseEncoding delegate, String separator, int afterEveryChars) { + this.delegate = checkNotNull(delegate); + this.separator = checkNotNull(separator); + this.afterEveryChars = afterEveryChars; + checkArgument( + afterEveryChars > 0, "Cannot add a separator after every %s chars", afterEveryChars); + } + + @Override + CharSequence trimTrailingPadding(CharSequence chars) { + return delegate.trimTrailingPadding(chars); + } + + @Override + int maxEncodedSize(int bytes) { + int unseparatedSize = delegate.maxEncodedSize(bytes); + return unseparatedSize + + separator.length() * divide(Math.max(0, unseparatedSize - 1), afterEveryChars, FLOOR); + } + + @J2ktIncompatible + @GwtIncompatible // Writer,OutputStream + @Override + public OutputStream encodingStream(Writer output) { + return delegate.encodingStream(separatingWriter(output, separator, afterEveryChars)); + } + + @Override + void encodeTo(Appendable target, byte[] bytes, int off, int len) throws IOException { + delegate.encodeTo(separatingAppendable(target, separator, afterEveryChars), bytes, off, len); + } + + @Override + int maxDecodedSize(int chars) { + return delegate.maxDecodedSize(chars); + } + + @Override + public boolean canDecode(CharSequence chars) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < chars.length(); i++) { + char c = chars.charAt(i); + if (separator.indexOf(c) < 0) { + builder.append(c); + } + } + return delegate.canDecode(builder); + } + + @Override + int decodeTo(byte[] target, CharSequence chars) throws DecodingException { + StringBuilder stripped = new StringBuilder(chars.length()); + for (int i = 0; i < chars.length(); i++) { + char c = chars.charAt(i); + if (separator.indexOf(c) < 0) { + stripped.append(c); + } + } + return delegate.decodeTo(target, stripped); + } + + @Override + @J2ktIncompatible + @GwtIncompatible // Reader,InputStream + public InputStream decodingStream(Reader reader) { + return delegate.decodingStream(ignoringReader(reader, separator)); + } + + @Override + public BaseEncoding omitPadding() { + return delegate.omitPadding().withSeparator(separator, afterEveryChars); + } + + @Override + public BaseEncoding withPadChar(char padChar) { + return delegate.withPadChar(padChar).withSeparator(separator, afterEveryChars); + } + + @Override + public BaseEncoding withSeparator(String separator, int afterEveryChars) { + throw new UnsupportedOperationException("Already have a separator"); + } + + @Override + public BaseEncoding upperCase() { + return delegate.upperCase().withSeparator(separator, afterEveryChars); + } + + @Override + public BaseEncoding lowerCase() { + return delegate.lowerCase().withSeparator(separator, afterEveryChars); + } + + @Override + public BaseEncoding ignoreCase() { + return delegate.ignoreCase().withSeparator(separator, afterEveryChars); + } + + @Override + public String toString() { + return delegate + ".withSeparator(\"" + separator + "\", " + afterEveryChars + ")"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteArrayDataInput.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteArrayDataInput.java new file mode 100644 index 0000000000000..f1acd8a8aa438 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteArrayDataInput.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.DataInput; +import java.io.IOException; + +/** + * An extension of {@code DataInput} for reading from in-memory byte arrays; its methods offer + * identical functionality but do not throw {@link IOException}. + * + *

Warning: The caller is responsible for not attempting to read past the end of the + * array. If any method encounters the end of the array prematurely, it throws {@link + * IllegalStateException} to signify programmer error. This behavior is a technical violation + * of the supertype's contract, which specifies a checked exception. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface ByteArrayDataInput extends DataInput { + @Override + void readFully(byte b[]); + + @Override + void readFully(byte b[], int off, int len); + + // not guaranteed to skip n bytes so result should NOT be ignored + // use ByteStreams.skipFully or one of the read methods instead + @Override + int skipBytes(int n); + + // to skip a byte + @Override + boolean readBoolean(); + + // to skip a byte + @Override + byte readByte(); + + // to skip a byte + @Override + int readUnsignedByte(); + + // to skip some bytes + @Override + short readShort(); + + // to skip some bytes + @Override + int readUnsignedShort(); + + // to skip some bytes + @Override + char readChar(); + + // to skip some bytes + @Override + int readInt(); + + // to skip some bytes + @Override + long readLong(); + + // to skip some bytes + @Override + float readFloat(); + + // to skip some bytes + @Override + double readDouble(); + + // to skip a line + @Override + String readLine(); + + // to skip a field + @Override + String readUTF(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteArrayDataOutput.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteArrayDataOutput.java new file mode 100644 index 0000000000000..b995f4043509e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteArrayDataOutput.java @@ -0,0 +1,88 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.DataOutput; +import java.io.IOException; + +/** + * An extension of {@code DataOutput} for writing to in-memory byte arrays; its methods offer + * identical functionality but do not throw {@link IOException}. + * + * @author Jayaprabhakar Kadarkarai + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface ByteArrayDataOutput extends DataOutput { + @Override + void write(int b); + + @Override + void write(byte b[]); + + @Override + void write(byte b[], int off, int len); + + @Override + void writeBoolean(boolean v); + + @Override + void writeByte(int v); + + @Override + void writeShort(int v); + + @Override + void writeChar(int v); + + @Override + void writeInt(int v); + + @Override + void writeLong(long v); + + @Override + void writeFloat(float v); + + @Override + void writeDouble(double v); + + @Override + void writeChars(String s); + + @Override + void writeUTF(String s); + + /** + * @deprecated This method is dangerous as it discards the high byte of every character. For + * UTF-8, use {@code write(s.getBytes(StandardCharsets.UTF_8))}. + */ + @Deprecated + @Override + void writeBytes(String s); + + /** Returns the contents that have been written to this instance, as a byte array. */ + byte[] toByteArray(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteProcessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteProcessor.java new file mode 100644 index 0000000000000..c07a8a6b92d8e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteProcessor.java @@ -0,0 +1,55 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; + +/** + * A callback interface to process bytes from a stream. + * + *

{@link #processBytes} will be called for each chunk of data that is read, and should return + * {@code false} when you want to stop processing. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface ByteProcessor { + /** + * This method will be called for each chunk of bytes in an input stream. The implementation + * should process the bytes from {@code buf[off]} through {@code buf[off + len - 1]} (inclusive). + * + * @param buf the byte array containing the data to process + * @param off the initial offset into the array + * @param len the length of data to be processed + * @return true to continue processing, false to stop + */ + // some uses know that their processor never returns false + boolean processBytes(byte[] buf, int off, int len) throws IOException; + + /** Return the result of processing all the bytes. */ + @ParametricNullness + T getResult(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteSink.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteSink.java new file mode 100644 index 0000000000000..493489d23e53c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteSink.java @@ -0,0 +1,164 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A destination to which bytes can be written, such as a file. Unlike an {@link OutputStream}, a + * {@code ByteSink} is not an open, stateful stream that can be written to and closed. Instead, it + * is an immutable supplier of {@code OutputStream} instances. + * + *

{@code ByteSink} provides two kinds of methods: + * + *

    + *
  • Methods that return a stream: These methods should return a new, independent + * instance each time they are called. The caller is responsible for ensuring that the + * returned stream is closed. + *
  • Convenience methods: These are implementations of common operations that are + * typically implemented by opening a stream using one of the methods in the first category, + * doing something and finally closing the stream or channel that was opened. + *
+ * + * @since 14.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ByteSink { + + /** Constructor for use by subclasses. */ + protected ByteSink() {} + + /** + * Returns a {@link CharSink} view of this {@code ByteSink} that writes characters to this sink as + * bytes encoded with the given {@link Charset charset}. + */ + public CharSink asCharSink(Charset charset) { + return new AsCharSink(charset); + } + + /** + * Opens a new {@link OutputStream} for writing to this sink. This method returns a new, + * independent stream each time it is called. + * + *

The caller is responsible for ensuring that the returned stream is closed. + * + * @throws IOException if an I/O error occurs while opening the stream + */ + public abstract OutputStream openStream() throws IOException; + + /** + * Opens a new buffered {@link OutputStream} for writing to this sink. The returned stream is not + * required to be a {@link BufferedOutputStream} in order to allow implementations to simply + * delegate to {@link #openStream()} when the stream returned by that method does not benefit from + * additional buffering (for example, a {@code ByteArrayOutputStream}). This method returns a new, + * independent stream each time it is called. + * + *

The caller is responsible for ensuring that the returned stream is closed. + * + * @throws IOException if an I/O error occurs while opening the stream + * @since 15.0 (in 14.0 with return type {@link BufferedOutputStream}) + */ + public OutputStream openBufferedStream() throws IOException { + OutputStream out = openStream(); + return (out instanceof BufferedOutputStream) + ? (BufferedOutputStream) out + : new BufferedOutputStream(out); + } + + /** + * Writes all the given bytes to this sink. + * + * @throws IOException if an I/O occurs while writing to this sink + */ + public void write(byte[] bytes) throws IOException { + checkNotNull(bytes); + + Closer closer = Closer.create(); + try { + OutputStream out = closer.register(openStream()); + out.write(bytes); + out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Writes all the bytes from the given {@code InputStream} to this sink. Does not close {@code + * input}. + * + * @return the number of bytes written + * @throws IOException if an I/O occurs while reading from {@code input} or writing to this sink + */ + public long writeFrom(InputStream input) throws IOException { + checkNotNull(input); + + Closer closer = Closer.create(); + try { + OutputStream out = closer.register(openStream()); + long written = ByteStreams.copy(input, out); + out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 + return written; + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * A char sink that encodes written characters with a charset and writes resulting bytes to this + * byte sink. + */ + private final class AsCharSink extends CharSink { + + private final Charset charset; + + private AsCharSink(Charset charset) { + this.charset = checkNotNull(charset); + } + + @Override + public Writer openStream() throws IOException { + return new OutputStreamWriter(ByteSink.this.openStream(), charset); + } + + @Override + public String toString() { + return ByteSink.this.toString() + ".asCharSink(" + charset + ")"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteSource.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteSource.java new file mode 100644 index 0000000000000..662a4c5496c86 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteSource.java @@ -0,0 +1,760 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Ascii; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.hash.Funnels; +import org.apache.iotdb.google.common.hash.HashCode; +import org.apache.iotdb.google.common.hash.HashFunction; +import org.apache.iotdb.google.common.hash.Hasher; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.io.ByteStreams.createBuffer; +import static org.apache.iotdb.google.common.io.ByteStreams.skipUpTo; + +/** + * A readable source of bytes, such as a file. Unlike an {@link InputStream}, a {@code ByteSource} + * is not an open, stateful stream for input that can be read and closed. Instead, it is an + * immutable supplier of {@code InputStream} instances. + * + *

{@code ByteSource} provides two kinds of methods: + * + *

    + *
  • Methods that return a stream: These methods should return a new, independent + * instance each time they are called. The caller is responsible for ensuring that the + * returned stream is closed. + *
  • Convenience methods: These are implementations of common operations that are + * typically implemented by opening a stream using one of the methods in the first category, + * doing something and finally closing the stream that was opened. + *
+ * + *

Note: In general, {@code ByteSource} is intended to be used for "file-like" sources + * that provide streams that are: + * + *

    + *
  • Finite: Many operations, such as {@link #size()} and {@link #read()}, will either + * block indefinitely or fail if the source creates an infinite stream. + *
  • Non-destructive: A destructive stream will consume or otherwise alter the + * bytes of the source as they are read from it. A source that provides such streams will not + * be reusable, and operations that read from the stream (including {@link #size()}, in some + * implementations) will prevent further operations from completing as expected. + *
+ * + * @since 14.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ByteSource { + + /** Constructor for use by subclasses. */ + protected ByteSource() {} + + /** + * Returns a {@link CharSource} view of this byte source that decodes bytes read from this source + * as characters using the given {@link Charset}. + * + *

If {@link CharSource#asByteSource} is called on the returned source with the same charset, + * the default implementation of this method will ensure that the original {@code ByteSource} is + * returned, rather than round-trip encoding. Subclasses that override this method should behave + * the same way. + */ + public CharSource asCharSource(Charset charset) { + return new AsCharSource(charset); + } + + /** + * Opens a new {@link InputStream} for reading from this source. This method returns a new, + * independent stream each time it is called. + * + *

The caller is responsible for ensuring that the returned stream is closed. + * + * @throws IOException if an I/O error occurs while opening the stream + */ + public abstract InputStream openStream() throws IOException; + + /** + * Opens a new buffered {@link InputStream} for reading from this source. The returned stream is + * not required to be a {@link BufferedInputStream} in order to allow implementations to simply + * delegate to {@link #openStream()} when the stream returned by that method does not benefit from + * additional buffering (for example, a {@code ByteArrayInputStream}). This method returns a new, + * independent stream each time it is called. + * + *

The caller is responsible for ensuring that the returned stream is closed. + * + * @throws IOException if an I/O error occurs while opening the stream + * @since 15.0 (in 14.0 with return type {@link BufferedInputStream}) + */ + public InputStream openBufferedStream() throws IOException { + InputStream in = openStream(); + return (in instanceof BufferedInputStream) + ? (BufferedInputStream) in + : new BufferedInputStream(in); + } + + /** + * Returns a view of a slice of this byte source that is at most {@code length} bytes long + * starting at the given {@code offset}. If {@code offset} is greater than the size of this + * source, the returned source will be empty. If {@code offset + length} is greater than the size + * of this source, the returned source will contain the slice starting at {@code offset} and + * ending at the end of this source. + * + * @throws IllegalArgumentException if {@code offset} or {@code length} is negative + */ + public ByteSource slice(long offset, long length) { + return new SlicedByteSource(offset, length); + } + + /** + * Returns whether the source has zero bytes. The default implementation first checks {@link + * #sizeIfKnown}, returning true if it's known to be zero and false if it's known to be non-zero. + * If the size is not known, it falls back to opening a stream and checking for EOF. + * + *

Note that, in cases where {@code sizeIfKnown} returns zero, it is possible that bytes + * are actually available for reading. (For example, some special files may return a size of 0 + * despite actually having content when read.) This means that a source may return {@code true} + * from {@code isEmpty()} despite having readable content. + * + * @throws IOException if an I/O error occurs + * @since 15.0 + */ + public boolean isEmpty() throws IOException { + Optional sizeIfKnown = sizeIfKnown(); + if (sizeIfKnown.isPresent()) { + return sizeIfKnown.get() == 0L; + } + Closer closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + return in.read() == -1; + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Returns the size of this source in bytes, if the size can be easily determined without actually + * opening the data stream. + * + *

The default implementation returns {@link Optional#absent}. Some sources, such as a file, + * may return a non-absent value. Note that in such cases, it is possible that this method + * will return a different number of bytes than would be returned by reading all of the bytes (for + * example, some special files may return a size of 0 despite actually having content when read). + * + *

Additionally, for mutable sources such as files, a subsequent read may return a different + * number of bytes if the contents are changed. + * + * @since 19.0 + */ + public Optional sizeIfKnown() { + return Optional.absent(); + } + + /** + * Returns the size of this source in bytes, even if doing so requires opening and traversing an + * entire stream. To avoid a potentially expensive operation, see {@link #sizeIfKnown}. + * + *

The default implementation calls {@link #sizeIfKnown} and returns the value if present. If + * absent, it will fall back to a heavyweight operation that will open a stream, read (or {@link + * InputStream#skip(long) skip}, if possible) to the end of the stream and return the total number + * of bytes that were read. + * + *

Note that for some sources that implement {@link #sizeIfKnown} to provide a more efficient + * implementation, it is possible that this method will return a different number of bytes + * than would be returned by reading all of the bytes (for example, some special files may return + * a size of 0 despite actually having content when read). + * + *

In either case, for mutable sources such as files, a subsequent read may return a different + * number of bytes if the contents are changed. + * + * @throws IOException if an I/O error occurs while reading the size of this source + */ + public long size() throws IOException { + Optional sizeIfKnown = sizeIfKnown(); + if (sizeIfKnown.isPresent()) { + return sizeIfKnown.get(); + } + + Closer closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + return countBySkipping(in); + } catch (IOException e) { + // skip may not be supported... at any rate, try reading + } finally { + closer.close(); + } + + closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + return ByteStreams.exhaust(in); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** Counts the bytes in the given input stream using skip if possible. */ + private long countBySkipping(InputStream in) throws IOException { + long count = 0; + long skipped; + while ((skipped = skipUpTo(in, Integer.MAX_VALUE)) > 0) { + count += skipped; + } + return count; + } + + /** + * Copies the contents of this byte source to the given {@code OutputStream}. Does not close + * {@code output}. + * + * @return the number of bytes copied + * @throws IOException if an I/O error occurs while reading from this source or writing to {@code + * output} + */ + public long copyTo(OutputStream output) throws IOException { + checkNotNull(output); + + Closer closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + return ByteStreams.copy(in, output); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Copies the contents of this byte source to the given {@code ByteSink}. + * + * @return the number of bytes copied + * @throws IOException if an I/O error occurs while reading from this source or writing to {@code + * sink} + */ + public long copyTo(ByteSink sink) throws IOException { + checkNotNull(sink); + + Closer closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + OutputStream out = closer.register(sink.openStream()); + return ByteStreams.copy(in, out); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads the full contents of this byte source as a byte array. + * + * @throws IOException if an I/O error occurs while reading from this source + */ + public byte[] read() throws IOException { + Closer closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + Optional size = sizeIfKnown(); + return size.isPresent() + ? ByteStreams.toByteArray(in, size.get()) + : ByteStreams.toByteArray(in); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads the contents of this byte source using the given {@code processor} to process bytes as + * they are read. Stops when all bytes have been read or the consumer returns {@code false}. + * Returns the result produced by the processor. + * + * @throws IOException if an I/O error occurs while reading from this source or if {@code + * processor} throws an {@code IOException} + * @since 16.0 + */ + // some processors won't return a useful result + @ParametricNullness + public T read(ByteProcessor processor) throws IOException { + checkNotNull(processor); + + Closer closer = Closer.create(); + try { + InputStream in = closer.register(openStream()); + return ByteStreams.readBytes(in, processor); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Hashes the contents of this byte source using the given hash function. + * + * @throws IOException if an I/O error occurs while reading from this source + */ + public HashCode hash(HashFunction hashFunction) throws IOException { + Hasher hasher = hashFunction.newHasher(); + copyTo(Funnels.asOutputStream(hasher)); + return hasher.hash(); + } + + /** + * Checks that the contents of this byte source are equal to the contents of the given byte + * source. + * + * @throws IOException if an I/O error occurs while reading from this source or {@code other} + */ + public boolean contentEquals(ByteSource other) throws IOException { + checkNotNull(other); + + byte[] buf1 = createBuffer(); + byte[] buf2 = createBuffer(); + + Closer closer = Closer.create(); + try { + InputStream in1 = closer.register(openStream()); + InputStream in2 = closer.register(other.openStream()); + while (true) { + int read1 = ByteStreams.read(in1, buf1, 0, buf1.length); + int read2 = ByteStreams.read(in2, buf2, 0, buf2.length); + if (read1 != read2 || !Arrays.equals(buf1, buf2)) { + return false; + } else if (read1 != buf1.length) { + return true; + } + } + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from + * the source will contain the concatenated data from the streams of the underlying sources. + * + *

Only one underlying stream will be open at a time. Closing the concatenated stream will + * close the open underlying stream. + * + * @param sources the sources to concatenate + * @return a {@code ByteSource} containing the concatenated data + * @since 15.0 + */ + public static ByteSource concat(Iterable sources) { + return new ConcatenatedByteSource(sources); + } + + /** + * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from + * the source will contain the concatenated data from the streams of the underlying sources. + * + *

Only one underlying stream will be open at a time. Closing the concatenated stream will + * close the open underlying stream. + * + *

Note: The input {@code Iterator} will be copied to an {@code ImmutableList} when this method + * is called. This will fail if the iterator is infinite and may cause problems if the iterator + * eagerly fetches data for each source when iterated (rather than producing sources that only + * load data through their streams). Prefer using the {@link #concat(Iterable)} overload if + * possible. + * + * @param sources the sources to concatenate + * @return a {@code ByteSource} containing the concatenated data + * @throws NullPointerException if any of {@code sources} is {@code null} + * @since 15.0 + */ + public static ByteSource concat(Iterator sources) { + return concat(ImmutableList.copyOf(sources)); + } + + /** + * Concatenates multiple {@link ByteSource} instances into a single source. Streams returned from + * the source will contain the concatenated data from the streams of the underlying sources. + * + *

Only one underlying stream will be open at a time. Closing the concatenated stream will + * close the open underlying stream. + * + * @param sources the sources to concatenate + * @return a {@code ByteSource} containing the concatenated data + * @throws NullPointerException if any of {@code sources} is {@code null} + * @since 15.0 + */ + public static ByteSource concat(ByteSource... sources) { + return concat(ImmutableList.copyOf(sources)); + } + + /** + * Returns a view of the given byte array as a {@link ByteSource}. To view only a specific range + * in the array, use {@code ByteSource.wrap(b).slice(offset, length)}. + * + *

Note that the given byte array may be passed directly to methods on, for example, {@code + * OutputStream} (when {@code copyTo(OutputStream)} is called on the resulting {@code + * ByteSource}). This could allow a malicious {@code OutputStream} implementation to modify the + * contents of the array, but provides better performance in the normal case. + * + * @since 15.0 (since 14.0 as {@code ByteStreams.asByteSource(byte[])}). + */ + public static ByteSource wrap(byte[] b) { + return new ByteArrayByteSource(b); + } + + /** + * Returns an immutable {@link ByteSource} that contains no bytes. + * + * @since 15.0 + */ + public static ByteSource empty() { + return EmptyByteSource.INSTANCE; + } + + /** + * A char source that reads bytes from this source and decodes them as characters using a charset. + */ + class AsCharSource extends CharSource { + + final Charset charset; + + AsCharSource(Charset charset) { + this.charset = checkNotNull(charset); + } + + @Override + public ByteSource asByteSource(Charset charset) { + if (charset.equals(this.charset)) { + return ByteSource.this; + } + return super.asByteSource(charset); + } + + @Override + public Reader openStream() throws IOException { + return new InputStreamReader(ByteSource.this.openStream(), charset); + } + + @Override + public String read() throws IOException { + // Reading all the data as a byte array is more efficient than the default read() + // implementation because: + // 1. the string constructor can avoid an extra copy most of the time by correctly sizing the + // internal char array (hard to avoid using StringBuilder) + // 2. we avoid extra copies into temporary buffers altogether + // The downside is that this will cause us to store the file bytes in memory twice for a short + // amount of time. + return new String(ByteSource.this.read(), charset); + } + + @Override + public String toString() { + return ByteSource.this.toString() + ".asCharSource(" + charset + ")"; + } + } + + /** A view of a subsection of the containing byte source. */ + private final class SlicedByteSource extends ByteSource { + + final long offset; + final long length; + + SlicedByteSource(long offset, long length) { + checkArgument(offset >= 0, "offset (%s) may not be negative", offset); + checkArgument(length >= 0, "length (%s) may not be negative", length); + this.offset = offset; + this.length = length; + } + + @Override + public InputStream openStream() throws IOException { + return sliceStream(ByteSource.this.openStream()); + } + + @Override + public InputStream openBufferedStream() throws IOException { + return sliceStream(ByteSource.this.openBufferedStream()); + } + + private InputStream sliceStream(InputStream in) throws IOException { + if (offset > 0) { + long skipped; + try { + skipped = ByteStreams.skipUpTo(in, offset); + } catch (Throwable e) { + Closer closer = Closer.create(); + closer.register(in); + try { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + if (skipped < offset) { + // offset was beyond EOF + in.close(); + return new ByteArrayInputStream(new byte[0]); + } + } + return ByteStreams.limit(in, length); + } + + @Override + public ByteSource slice(long offset, long length) { + checkArgument(offset >= 0, "offset (%s) may not be negative", offset); + checkArgument(length >= 0, "length (%s) may not be negative", length); + long maxLength = this.length - offset; + return maxLength <= 0 + ? ByteSource.empty() + : ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength)); + } + + @Override + public boolean isEmpty() throws IOException { + return length == 0 || super.isEmpty(); + } + + @Override + public Optional sizeIfKnown() { + Optional optionalUnslicedSize = ByteSource.this.sizeIfKnown(); + if (optionalUnslicedSize.isPresent()) { + long unslicedSize = optionalUnslicedSize.get(); + long off = Math.min(offset, unslicedSize); + return Optional.of(Math.min(length, unslicedSize - off)); + } + return Optional.absent(); + } + + @Override + public String toString() { + return ByteSource.this.toString() + ".slice(" + offset + ", " + length + ")"; + } + } + + private static class ByteArrayByteSource extends ByteSource { + + final byte[] bytes; + final int offset; + final int length; + + ByteArrayByteSource(byte[] bytes) { + this(bytes, 0, bytes.length); + } + + // NOTE: Preconditions are enforced by slice, the only non-trivial caller. + ByteArrayByteSource(byte[] bytes, int offset, int length) { + this.bytes = bytes; + this.offset = offset; + this.length = length; + } + + @Override + public InputStream openStream() { + return new ByteArrayInputStream(bytes, offset, length); + } + + @Override + public InputStream openBufferedStream() { + return openStream(); + } + + @Override + public boolean isEmpty() { + return length == 0; + } + + @Override + public long size() { + return length; + } + + @Override + public Optional sizeIfKnown() { + return Optional.of((long) length); + } + + @Override + public byte[] read() { + return Arrays.copyOfRange(bytes, offset, offset + length); + } + + @SuppressWarnings("CheckReturnValue") // it doesn't matter what processBytes returns here + @Override + @ParametricNullness + public T read(ByteProcessor processor) throws IOException { + processor.processBytes(bytes, offset, length); + return processor.getResult(); + } + + @Override + public long copyTo(OutputStream output) throws IOException { + output.write(bytes, offset, length); + return length; + } + + @Override + public HashCode hash(HashFunction hashFunction) throws IOException { + return hashFunction.hashBytes(bytes, offset, length); + } + + @Override + public ByteSource slice(long offset, long length) { + checkArgument(offset >= 0, "offset (%s) may not be negative", offset); + checkArgument(length >= 0, "length (%s) may not be negative", length); + + offset = Math.min(offset, this.length); + length = Math.min(length, this.length - offset); + int newOffset = this.offset + (int) offset; + return new ByteArrayByteSource(bytes, newOffset, (int) length); + } + + @Override + public String toString() { + return "ByteSource.wrap(" + + Ascii.truncate(BaseEncoding.base16().encode(bytes, offset, length), 30, "...") + + ")"; + } + } + + private static final class EmptyByteSource extends ByteArrayByteSource { + + static final EmptyByteSource INSTANCE = new EmptyByteSource(); + + EmptyByteSource() { + super(new byte[0]); + } + + @Override + public CharSource asCharSource(Charset charset) { + checkNotNull(charset); + return CharSource.empty(); + } + + @Override + public byte[] read() { + return bytes; // length is 0, no need to clone + } + + @Override + public String toString() { + return "ByteSource.empty()"; + } + } + + private static final class ConcatenatedByteSource extends ByteSource { + + final Iterable sources; + + ConcatenatedByteSource(Iterable sources) { + this.sources = checkNotNull(sources); + } + + @Override + public InputStream openStream() throws IOException { + return new MultiInputStream(sources.iterator()); + } + + @Override + public boolean isEmpty() throws IOException { + for (ByteSource source : sources) { + if (!source.isEmpty()) { + return false; + } + } + return true; + } + + @Override + public Optional sizeIfKnown() { + if (!(sources instanceof Collection)) { + // Infinite Iterables can cause problems here. Of course, it's true that most of the other + // methods on this class also have potential problems with infinite Iterables. But unlike + // those, this method can cause issues even if the user is dealing with a (finite) slice() + // of this source, since the slice's sizeIfKnown() method needs to know the size of the + // underlying source to know what its size actually is. + return Optional.absent(); + } + long result = 0L; + for (ByteSource source : sources) { + Optional sizeIfKnown = source.sizeIfKnown(); + if (!sizeIfKnown.isPresent()) { + return Optional.absent(); + } + result += sizeIfKnown.get(); + if (result < 0) { + // Overflow (or one or more sources that returned a negative size, but all bets are off in + // that case) + // Can't represent anything higher, and realistically there probably isn't anything that + // can actually be done anyway with the supposed 8+ exbibytes of data the source is + // claiming to have if we get here, so just stop. + return Optional.of(Long.MAX_VALUE); + } + } + return Optional.of(result); + } + + @Override + public long size() throws IOException { + long result = 0L; + for (ByteSource source : sources) { + result += source.size(); + if (result < 0) { + // Overflow (or one or more sources that returned a negative size, but all bets are off in + // that case) + // Can't represent anything higher, and realistically there probably isn't anything that + // can actually be done anyway with the supposed 8+ exbibytes of data the source is + // claiming to have if we get here, so just stop. + return Long.MAX_VALUE; + } + } + return result; + } + + @Override + public String toString() { + return "ByteSource.concat(" + sources + ")"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteStreams.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteStreams.java new file mode 100644 index 0000000000000..cca5d0e379d50 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ByteStreams.java @@ -0,0 +1,938 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.math.IntMath; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Queue; + +import static java.lang.Math.max; +import static java.lang.Math.min; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Provides utility methods for working with byte arrays and I/O streams. + * + * @author Chris Nokleberg + * @author Colin Decker + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class ByteStreams { + + private static final int BUFFER_SIZE = 8192; + + /** Creates a new byte array for buffering reads or writes. */ + static byte[] createBuffer() { + return new byte[BUFFER_SIZE]; + } + + /** + * There are three methods to implement {@link FileChannel#transferTo(long, long, + * WritableByteChannel)}: + * + *

    + *
  1. Use sendfile(2) or equivalent. Requires that both the input channel and the output + * channel have their own file descriptors. Generally this only happens when both channels + * are files or sockets. This performs zero copies - the bytes never enter userspace. + *
  2. Use mmap(2) or equivalent. Requires that either the input channel or the output channel + * have file descriptors. Bytes are copied from the file into a kernel buffer, then directly + * into the other buffer (userspace). Note that if the file is very large, a naive + * implementation will effectively put the whole file in memory. On many systems with paging + * and virtual memory, this is not a problem - because it is mapped read-only, the kernel + * can always page it to disk "for free". However, on systems where killing processes + * happens all the time in normal conditions (i.e., android) the OS must make a tradeoff + * between paging memory and killing other processes - so allocating a gigantic buffer and + * then sequentially accessing it could result in other processes dying. This is solvable + * via madvise(2), but that obviously doesn't exist in java. + *
  3. Ordinary copy. Kernel copies bytes into a kernel buffer, from a kernel buffer into a + * userspace buffer (byte[] or ByteBuffer), then copies them from that buffer into the + * destination channel. + *
+ * + * This value is intended to be large enough to make the overhead of system calls negligible, + * without being so large that it causes problems for systems with atypical memory management if + * approaches 2 or 3 are used. + */ + private static final int ZERO_COPY_CHUNK_SIZE = 512 * 1024; + + private ByteStreams() {} + + /** + * Copies all bytes from the input stream to the output stream. Does not close or flush either + * stream. + * + *

Java 9 users and later: this method should be treated as deprecated; use the + * equivalent {@link InputStream#transferTo} method instead. + * + * @param from the input stream to read from + * @param to the output stream to write to + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(InputStream from, OutputStream to) throws IOException { + checkNotNull(from); + checkNotNull(to); + byte[] buf = createBuffer(); + long total = 0; + while (true) { + int r = from.read(buf); + if (r == -1) { + break; + } + to.write(buf, 0, r); + total += r; + } + return total; + } + + /** + * Copies all bytes from the readable channel to the writable channel. Does not close or flush + * either channel. + * + * @param from the readable channel to read from + * @param to the writable channel to write to + * @return the number of bytes copied + * @throws IOException if an I/O error occurs + */ + public static long copy(ReadableByteChannel from, WritableByteChannel to) throws IOException { + checkNotNull(from); + checkNotNull(to); + if (from instanceof FileChannel) { + FileChannel sourceChannel = (FileChannel) from; + long oldPosition = sourceChannel.position(); + long position = oldPosition; + long copied; + do { + copied = sourceChannel.transferTo(position, ZERO_COPY_CHUNK_SIZE, to); + position += copied; + sourceChannel.position(position); + } while (copied > 0 || position < sourceChannel.size()); + return position - oldPosition; + } + + ByteBuffer buf = ByteBuffer.wrap(createBuffer()); + long total = 0; + while (from.read(buf) != -1) { + Java8Compatibility.flip(buf); + while (buf.hasRemaining()) { + total += to.write(buf); + } + Java8Compatibility.clear(buf); + } + return total; + } + + /** Max array length on JVM. */ + private static final int MAX_ARRAY_LEN = Integer.MAX_VALUE - 8; + + /** Large enough to never need to expand, given the geometric progression of buffer sizes. */ + private static final int TO_BYTE_ARRAY_DEQUE_SIZE = 20; + + /** + * Returns a byte array containing the bytes from the buffers already in {@code bufs} (which have + * a total combined length of {@code totalLen} bytes) followed by all bytes remaining in the given + * input stream. + */ + private static byte[] toByteArrayInternal(InputStream in, Queue bufs, int totalLen) + throws IOException { + // Roughly size to match what has been read already. Some file systems, such as procfs, return 0 + // as their length. These files are very small, so it's wasteful to allocate an 8KB buffer. + int initialBufferSize = min(BUFFER_SIZE, max(128, Integer.highestOneBit(totalLen) * 2)); + // Starting with an 8k buffer, double the size of each successive buffer. Smaller buffers + // quadruple in size until they reach 8k, to minimize the number of small reads for longer + // streams. Buffers are retained in a deque so that there's no copying between buffers while + // reading and so all of the bytes in each new allocated buffer are available for reading from + // the stream. + for (int bufSize = initialBufferSize; + totalLen < MAX_ARRAY_LEN; + bufSize = IntMath.saturatedMultiply(bufSize, bufSize < 4096 ? 4 : 2)) { + byte[] buf = new byte[min(bufSize, MAX_ARRAY_LEN - totalLen)]; + bufs.add(buf); + int off = 0; + while (off < buf.length) { + // always OK to fill buf; its size plus the rest of bufs is never more than MAX_ARRAY_LEN + int r = in.read(buf, off, buf.length - off); + if (r == -1) { + return combineBuffers(bufs, totalLen); + } + off += r; + totalLen += r; + } + } + + // read MAX_ARRAY_LEN bytes without seeing end of stream + if (in.read() == -1) { + // oh, there's the end of the stream + return combineBuffers(bufs, MAX_ARRAY_LEN); + } else { + throw new OutOfMemoryError("input is too large to fit in a byte array"); + } + } + + private static byte[] combineBuffers(Queue bufs, int totalLen) { + if (bufs.isEmpty()) { + return new byte[0]; + } + byte[] result = bufs.remove(); + if (result.length == totalLen) { + return result; + } + int remaining = totalLen - result.length; + result = Arrays.copyOf(result, totalLen); + while (remaining > 0) { + byte[] buf = bufs.remove(); + int bytesToCopy = min(remaining, buf.length); + int resultOffset = totalLen - remaining; + System.arraycopy(buf, 0, result, resultOffset, bytesToCopy); + remaining -= bytesToCopy; + } + return result; + } + + /** + * Reads all bytes from an input stream into a byte array. Does not close the stream. + * + * @param in the input stream to read from + * @return a byte array containing all the bytes from the stream + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(InputStream in) throws IOException { + checkNotNull(in); + return toByteArrayInternal(in, new ArrayDeque(TO_BYTE_ARRAY_DEQUE_SIZE), 0); + } + + /** + * Reads all bytes from an input stream into a byte array. The given expected size is used to + * create an initial byte array, but if the actual number of bytes read from the stream differs, + * the correct result will be returned anyway. + */ + static byte[] toByteArray(InputStream in, long expectedSize) throws IOException { + checkArgument(expectedSize >= 0, "expectedSize (%s) must be non-negative", expectedSize); + if (expectedSize > MAX_ARRAY_LEN) { + throw new OutOfMemoryError(expectedSize + " bytes is too large to fit in a byte array"); + } + + byte[] bytes = new byte[(int) expectedSize]; + int remaining = (int) expectedSize; + + while (remaining > 0) { + int off = (int) expectedSize - remaining; + int read = in.read(bytes, off, remaining); + if (read == -1) { + // end of stream before reading expectedSize bytes + // just return the bytes read so far + return Arrays.copyOf(bytes, off); + } + remaining -= read; + } + + // bytes is now full + int b = in.read(); + if (b == -1) { + return bytes; + } + + // the stream was longer, so read the rest normally + Queue bufs = new ArrayDeque<>(TO_BYTE_ARRAY_DEQUE_SIZE + 2); + bufs.add(bytes); + bufs.add(new byte[] {(byte) b}); + return toByteArrayInternal(in, bufs, bytes.length + 1); + } + + /** + * Reads and discards data from the given {@code InputStream} until the end of the stream is + * reached. Returns the total number of bytes read. Does not close the stream. + * + * @since 20.0 + */ + public static long exhaust(InputStream in) throws IOException { + long total = 0; + long read; + byte[] buf = createBuffer(); + while ((read = in.read(buf)) != -1) { + total += read; + } + return total; + } + + /** + * Returns a new {@link ByteArrayDataInput} instance to read from the {@code bytes} array from the + * beginning. + */ + public static ByteArrayDataInput newDataInput(byte[] bytes) { + return newDataInput(new ByteArrayInputStream(bytes)); + } + + /** + * Returns a new {@link ByteArrayDataInput} instance to read from the {@code bytes} array, + * starting at the given position. + * + * @throws IndexOutOfBoundsException if {@code start} is negative or greater than the length of + * the array + */ + public static ByteArrayDataInput newDataInput(byte[] bytes, int start) { + checkPositionIndex(start, bytes.length); + return newDataInput(new ByteArrayInputStream(bytes, start, bytes.length - start)); + } + + /** + * Returns a new {@link ByteArrayDataInput} instance to read from the given {@code + * ByteArrayInputStream}. The given input stream is not reset before being read from by the + * returned {@code ByteArrayDataInput}. + * + * @since 17.0 + */ + public static ByteArrayDataInput newDataInput(ByteArrayInputStream byteArrayInputStream) { + return new ByteArrayDataInputStream(checkNotNull(byteArrayInputStream)); + } + + private static class ByteArrayDataInputStream implements ByteArrayDataInput { + final DataInput input; + + ByteArrayDataInputStream(ByteArrayInputStream byteArrayInputStream) { + this.input = new DataInputStream(byteArrayInputStream); + } + + @Override + public void readFully(byte b[]) { + try { + input.readFully(b); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public void readFully(byte b[], int off, int len) { + try { + input.readFully(b, off, len); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public int skipBytes(int n) { + try { + return input.skipBytes(n); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public boolean readBoolean() { + try { + return input.readBoolean(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public byte readByte() { + try { + return input.readByte(); + } catch (EOFException e) { + throw new IllegalStateException(e); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public int readUnsignedByte() { + try { + return input.readUnsignedByte(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public short readShort() { + try { + return input.readShort(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public int readUnsignedShort() { + try { + return input.readUnsignedShort(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public char readChar() { + try { + return input.readChar(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public int readInt() { + try { + return input.readInt(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public long readLong() { + try { + return input.readLong(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public float readFloat() { + try { + return input.readFloat(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public double readDouble() { + try { + return input.readDouble(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public String readLine() { + try { + return input.readLine(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public String readUTF() { + try { + return input.readUTF(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + } + + /** Returns a new {@link ByteArrayDataOutput} instance with a default size. */ + public static ByteArrayDataOutput newDataOutput() { + return newDataOutput(new ByteArrayOutputStream()); + } + + /** + * Returns a new {@link ByteArrayDataOutput} instance sized to hold {@code size} bytes before + * resizing. + * + * @throws IllegalArgumentException if {@code size} is negative + */ + public static ByteArrayDataOutput newDataOutput(int size) { + // When called at high frequency, boxing size generates too much garbage, + // so avoid doing that if we can. + if (size < 0) { + throw new IllegalArgumentException(String.format("Invalid size: %s", size)); + } + return newDataOutput(new ByteArrayOutputStream(size)); + } + + /** + * Returns a new {@link ByteArrayDataOutput} instance which writes to the given {@code + * ByteArrayOutputStream}. The given output stream is not reset before being written to by the + * returned {@code ByteArrayDataOutput} and new data will be appended to any existing content. + * + *

Note that if the given output stream was not empty or is modified after the {@code + * ByteArrayDataOutput} is created, the contract for {@link ByteArrayDataOutput#toByteArray} will + * not be honored (the bytes returned in the byte array may not be exactly what was written via + * calls to {@code ByteArrayDataOutput}). + * + * @since 17.0 + */ + public static ByteArrayDataOutput newDataOutput(ByteArrayOutputStream byteArrayOutputStream) { + return new ByteArrayDataOutputStream(checkNotNull(byteArrayOutputStream)); + } + + private static class ByteArrayDataOutputStream implements ByteArrayDataOutput { + + final DataOutput output; + final ByteArrayOutputStream byteArrayOutputStream; + + ByteArrayDataOutputStream(ByteArrayOutputStream byteArrayOutputStream) { + this.byteArrayOutputStream = byteArrayOutputStream; + output = new DataOutputStream(byteArrayOutputStream); + } + + @Override + public void write(int b) { + try { + output.write(b); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void write(byte[] b) { + try { + output.write(b); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void write(byte[] b, int off, int len) { + try { + output.write(b, off, len); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeBoolean(boolean v) { + try { + output.writeBoolean(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeByte(int v) { + try { + output.writeByte(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeBytes(String s) { + try { + output.writeBytes(s); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeChar(int v) { + try { + output.writeChar(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeChars(String s) { + try { + output.writeChars(s); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeDouble(double v) { + try { + output.writeDouble(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeFloat(float v) { + try { + output.writeFloat(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeInt(int v) { + try { + output.writeInt(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeLong(long v) { + try { + output.writeLong(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeShort(int v) { + try { + output.writeShort(v); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public void writeUTF(String s) { + try { + output.writeUTF(s); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + @Override + public byte[] toByteArray() { + return byteArrayOutputStream.toByteArray(); + } + } + + private static final OutputStream NULL_OUTPUT_STREAM = + new OutputStream() { + /** Discards the specified byte. */ + @Override + public void write(int b) {} + + /** Discards the specified byte array. */ + @Override + public void write(byte[] b) { + checkNotNull(b); + } + + /** Discards the specified byte array. */ + @Override + public void write(byte[] b, int off, int len) { + checkNotNull(b); + checkPositionIndexes(off, off + len, b.length); + } + + @Override + public String toString() { + return "ByteStreams.nullOutputStream()"; + } + }; + + /** + * Returns an {@link OutputStream} that simply discards written bytes. + * + * @since 14.0 (since 1.0 as org.apache.iotdb.google.common.io.NullOutputStream) + */ + public static OutputStream nullOutputStream() { + return NULL_OUTPUT_STREAM; + } + + /** + * Wraps a {@link InputStream}, limiting the number of bytes which can be read. + * + * @param in the input stream to be wrapped + * @param limit the maximum number of bytes to be read + * @return a length-limited {@link InputStream} + * @since 14.0 (since 1.0 as org.apache.iotdb.google.common.io.LimitInputStream) + */ + public static InputStream limit(InputStream in, long limit) { + return new LimitedInputStream(in, limit); + } + + private static final class LimitedInputStream extends FilterInputStream { + + private long left; + private long mark = -1; + + LimitedInputStream(InputStream in, long limit) { + super(in); + checkNotNull(in); + checkArgument(limit >= 0, "limit must be non-negative"); + left = limit; + } + + @Override + public int available() throws IOException { + return (int) Math.min(in.available(), left); + } + + // it's okay to mark even if mark isn't supported, as reset won't work + @Override + public synchronized void mark(int readLimit) { + in.mark(readLimit); + mark = left; + } + + @Override + public int read() throws IOException { + if (left == 0) { + return -1; + } + + int result = in.read(); + if (result != -1) { + --left; + } + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (left == 0) { + return -1; + } + + len = (int) Math.min(len, left); + int result = in.read(b, off, len); + if (result != -1) { + left -= result; + } + return result; + } + + @Override + public synchronized void reset() throws IOException { + if (!in.markSupported()) { + throw new IOException("Mark not supported"); + } + if (mark == -1) { + throw new IOException("Mark not set"); + } + + in.reset(); + left = mark; + } + + @Override + public long skip(long n) throws IOException { + n = Math.min(n, left); + long skipped = in.skip(n); + left -= skipped; + return skipped; + } + } + + /** + * Attempts to read enough bytes from the stream to fill the given byte array, with the same + * behavior as {@link DataInput#readFully(byte[])}. Does not close the stream. + * + * @param in the input stream to read from. + * @param b the buffer into which the data is read. + * @throws EOFException if this stream reaches the end before reading all the bytes. + * @throws IOException if an I/O error occurs. + */ + public static void readFully(InputStream in, byte[] b) throws IOException { + readFully(in, b, 0, b.length); + } + + /** + * Attempts to read {@code len} bytes from the stream into the given array starting at {@code + * off}, with the same behavior as {@link DataInput#readFully(byte[], int, int)}. Does not close + * the stream. + * + * @param in the input stream to read from. + * @param b the buffer into which the data is read. + * @param off an int specifying the offset into the data. + * @param len an int specifying the number of bytes to read. + * @throws EOFException if this stream reaches the end before reading all the bytes. + * @throws IOException if an I/O error occurs. + */ + public static void readFully(InputStream in, byte[] b, int off, int len) throws IOException { + int read = read(in, b, off, len); + if (read != len) { + throw new EOFException( + "reached end of stream after reading " + read + " bytes; " + len + " bytes expected"); + } + } + + /** + * Discards {@code n} bytes of data from the input stream. This method will block until the full + * amount has been skipped. Does not close the stream. + * + * @param in the input stream to read from + * @param n the number of bytes to skip + * @throws EOFException if this stream reaches the end before skipping all the bytes + * @throws IOException if an I/O error occurs, or the stream does not support skipping + */ + public static void skipFully(InputStream in, long n) throws IOException { + long skipped = skipUpTo(in, n); + if (skipped < n) { + throw new EOFException( + "reached end of stream after skipping " + skipped + " bytes; " + n + " bytes expected"); + } + } + + /** + * Discards up to {@code n} bytes of data from the input stream. This method will block until + * either the full amount has been skipped or until the end of the stream is reached, whichever + * happens first. Returns the total number of bytes skipped. + */ + static long skipUpTo(InputStream in, long n) throws IOException { + long totalSkipped = 0; + // A buffer is allocated if skipSafely does not skip any bytes. + byte[] buf = null; + + while (totalSkipped < n) { + long remaining = n - totalSkipped; + long skipped = skipSafely(in, remaining); + + if (skipped == 0) { + // Do a buffered read since skipSafely could return 0 repeatedly, for example if + // in.available() always returns 0 (the default). + int skip = (int) Math.min(remaining, BUFFER_SIZE); + if (buf == null) { + // Allocate a buffer bounded by the maximum size that can be requested, for + // example an array of BUFFER_SIZE is unnecessary when the value of remaining + // is smaller. + buf = new byte[skip]; + } + if ((skipped = in.read(buf, 0, skip)) == -1) { + // Reached EOF + break; + } + } + + totalSkipped += skipped; + } + + return totalSkipped; + } + + /** + * Attempts to skip up to {@code n} bytes from the given input stream, but not more than {@code + * in.available()} bytes. This prevents {@code FileInputStream} from skipping more bytes than + * actually remain in the file, something that it {@linkplain java.io.FileInputStream#skip(long) + * specifies} it can do in its Javadoc despite the fact that it is violating the contract of + * {@code InputStream.skip()}. + */ + private static long skipSafely(InputStream in, long n) throws IOException { + int available = in.available(); + return available == 0 ? 0 : in.skip(Math.min(available, n)); + } + + /** + * Process the bytes of the given input stream using the given processor. + * + * @param input the input stream to process + * @param processor the object to which to pass the bytes of the stream + * @return the result of the byte processor + * @throws IOException if an I/O error occurs + * @since 14.0 + */ + // some processors won't return a useful result + @ParametricNullness + public static T readBytes(InputStream input, ByteProcessor processor) + throws IOException { + checkNotNull(input); + checkNotNull(processor); + + byte[] buf = createBuffer(); + int read; + do { + read = input.read(buf); + } while (read != -1 && processor.processBytes(buf, 0, read)); + return processor.getResult(); + } + + /** + * Reads some bytes from an input stream and stores them into the buffer array {@code b}. This + * method blocks until {@code len} bytes of input data have been read into the array, or end of + * file is detected. The number of bytes read is returned, possibly zero. Does not close the + * stream. + * + *

A caller can detect EOF if the number of bytes read is less than {@code len}. All subsequent + * calls on the same stream will return zero. + * + *

If {@code b} is null, a {@code NullPointerException} is thrown. If {@code off} is negative, + * or {@code len} is negative, or {@code off+len} is greater than the length of the array {@code + * b}, then an {@code IndexOutOfBoundsException} is thrown. If {@code len} is zero, then no bytes + * are read. Otherwise, the first byte read is stored into element {@code b[off]}, the next one + * into {@code b[off+1]}, and so on. The number of bytes read is, at most, equal to {@code len}. + * + * @param in the input stream to read from + * @param b the buffer into which the data is read + * @param off an int specifying the offset into the data + * @param len an int specifying the number of bytes to read + * @return the number of bytes read + * @throws IOException if an I/O error occurs + * @throws IndexOutOfBoundsException if {@code off} is negative, if {@code len} is negative, or if + * {@code off + len} is greater than {@code b.length} + */ + // Sometimes you don't care how many bytes you actually read, I guess. + // (You know that it's either going to read len bytes or stop at EOF.) + public static int read(InputStream in, byte[] b, int off, int len) throws IOException { + checkNotNull(in); + checkNotNull(b); + if (len < 0) { + throw new IndexOutOfBoundsException(String.format("len (%s) cannot be negative", len)); + } + checkPositionIndexes(off, off + len, b.length); + int total = 0; + while (total < len) { + int result = in.read(b, off + total, len - total); + if (result == -1) { + break; + } + total += result; + } + return total; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSequenceReader.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSequenceReader.java new file mode 100644 index 0000000000000..e911070c0695e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSequenceReader.java @@ -0,0 +1,156 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * A {@link Reader} that reads the characters in a {@link CharSequence}. Like {@code StringReader}, + * but works with any {@link CharSequence}. + * + * @author Colin Decker + */ +// TODO(cgdecker): make this public? as a type, or a method in CharStreams? +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class CharSequenceReader extends Reader { + + private CharSequence seq; + private int pos; + private int mark; + + /** Creates a new reader wrapping the given character sequence. */ + public CharSequenceReader(CharSequence seq) { + this.seq = checkNotNull(seq); + } + + private void checkOpen() throws IOException { + if (seq == null) { + throw new IOException("reader closed"); + } + } + + private boolean hasRemaining() { + return remaining() > 0; + } + + private int remaining() { + requireNonNull(seq); // safe as long as we call this only after checkOpen + return seq.length() - pos; + } + + /* + * To avoid the need to call requireNonNull so much, we could consider more clever approaches, + * such as: + * + * - Make checkOpen return the non-null `seq`. Then callers can assign that to a local variable or + * even back to `this.seq`. However, that may suggest that we're defending against concurrent + * mutation, which is not an actual risk because we use `synchronized`. + * - Make `remaining` require a non-null `seq` argument. But this is a bit weird because the + * method, while it would avoid the instance field `seq` would still access the instance field + * `pos`. + */ + + @Override + public synchronized int read(CharBuffer target) throws IOException { + checkNotNull(target); + checkOpen(); + requireNonNull(seq); // safe because of checkOpen + if (!hasRemaining()) { + return -1; + } + int charsToRead = Math.min(target.remaining(), remaining()); + for (int i = 0; i < charsToRead; i++) { + target.put(seq.charAt(pos++)); + } + return charsToRead; + } + + @Override + public synchronized int read() throws IOException { + checkOpen(); + requireNonNull(seq); // safe because of checkOpen + return hasRemaining() ? seq.charAt(pos++) : -1; + } + + @Override + public synchronized int read(char[] cbuf, int off, int len) throws IOException { + checkPositionIndexes(off, off + len, cbuf.length); + checkOpen(); + requireNonNull(seq); // safe because of checkOpen + if (!hasRemaining()) { + return -1; + } + int charsToRead = Math.min(len, remaining()); + for (int i = 0; i < charsToRead; i++) { + cbuf[off + i] = seq.charAt(pos++); + } + return charsToRead; + } + + @Override + public synchronized long skip(long n) throws IOException { + checkArgument(n >= 0, "n (%s) may not be negative", n); + checkOpen(); + int charsToSkip = (int) Math.min(remaining(), n); // safe because remaining is an int + pos += charsToSkip; + return charsToSkip; + } + + @Override + public synchronized boolean ready() throws IOException { + checkOpen(); + return true; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void mark(int readAheadLimit) throws IOException { + checkArgument(readAheadLimit >= 0, "readAheadLimit (%s) may not be negative", readAheadLimit); + checkOpen(); + mark = pos; + } + + @Override + public synchronized void reset() throws IOException { + checkOpen(); + pos = mark; + } + + @Override + public synchronized void close() throws IOException { + seq = null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSink.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSink.java new file mode 100644 index 0000000000000..740966ab67316 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSink.java @@ -0,0 +1,194 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A destination to which characters can be written, such as a text file. Unlike a {@link Writer}, a + * {@code CharSink} is not an open, stateful stream that can be written to and closed. Instead, it + * is an immutable supplier of {@code Writer} instances. + * + *

{@code CharSink} provides two kinds of methods: + * + *

    + *
  • Methods that return a writer: These methods should return a new, independent + * instance each time they are called. The caller is responsible for ensuring that the + * returned writer is closed. + *
  • Convenience methods: These are implementations of common operations that are + * typically implemented by opening a writer using one of the methods in the first category, + * doing something and finally closing the writer that was opened. + *
+ * + *

Any {@link ByteSink} may be viewed as a {@code CharSink} with a specific {@linkplain Charset + * character encoding} using {@link ByteSink#asCharSink(Charset)}. Characters written to the + * resulting {@code CharSink} will written to the {@code ByteSink} as encoded bytes. + * + * @since 14.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class CharSink { + + /** Constructor for use by subclasses. */ + protected CharSink() {} + + /** + * Opens a new {@link Writer} for writing to this sink. This method returns a new, independent + * writer each time it is called. + * + *

The caller is responsible for ensuring that the returned writer is closed. + * + * @throws IOException if an I/O error occurs while opening the writer + */ + public abstract Writer openStream() throws IOException; + + /** + * Opens a new buffered {@link Writer} for writing to this sink. The returned stream is not + * required to be a {@link BufferedWriter} in order to allow implementations to simply delegate to + * {@link #openStream()} when the stream returned by that method does not benefit from additional + * buffering. This method returns a new, independent writer each time it is called. + * + *

The caller is responsible for ensuring that the returned writer is closed. + * + * @throws IOException if an I/O error occurs while opening the writer + * @since 15.0 (in 14.0 with return type {@link BufferedWriter}) + */ + public Writer openBufferedStream() throws IOException { + Writer writer = openStream(); + return (writer instanceof BufferedWriter) + ? (BufferedWriter) writer + : new BufferedWriter(writer); + } + + /** + * Writes the given character sequence to this sink. + * + * @throws IOException if an I/O error while writing to this sink + */ + public void write(CharSequence charSequence) throws IOException { + checkNotNull(charSequence); + + Closer closer = Closer.create(); + try { + Writer out = closer.register(openStream()); + out.append(charSequence); + out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Writes the given lines of text to this sink with each line (including the last) terminated with + * the operating system's default line separator. This method is equivalent to {@code + * writeLines(lines, System.getProperty("line.separator"))}. + * + * @throws IOException if an I/O error occurs while writing to this sink + */ + public void writeLines(Iterable lines) throws IOException { + writeLines(lines, System.getProperty("line.separator")); + } + + /** + * Writes the given lines of text to this sink with each line (including the last) terminated with + * the given line separator. + * + * @throws IOException if an I/O error occurs while writing to this sink + */ + public void writeLines(Iterable lines, String lineSeparator) + throws IOException { + writeLines(lines.iterator(), lineSeparator); + } + + /** + * Writes the given lines of text to this sink with each line (including the last) terminated with + * the operating system's default line separator. This method is equivalent to {@code + * writeLines(lines, System.getProperty("line.separator"))}. + * + * @throws IOException if an I/O error occurs while writing to this sink + * @since 22.0 + */ + public void writeLines(Stream lines) throws IOException { + writeLines(lines, System.getProperty("line.separator")); + } + + /** + * Writes the given lines of text to this sink with each line (including the last) terminated with + * the given line separator. + * + * @throws IOException if an I/O error occurs while writing to this sink + * @since 22.0 + */ + public void writeLines(Stream lines, String lineSeparator) + throws IOException { + writeLines(lines.iterator(), lineSeparator); + } + + private void writeLines(Iterator lines, String lineSeparator) + throws IOException { + checkNotNull(lineSeparator); + + try (Writer out = openBufferedStream()) { + while (lines.hasNext()) { + out.append(lines.next()).append(lineSeparator); + } + } + } + + /** + * Writes all the text from the given {@link Readable} (such as a {@link Reader}) to this sink. + * Does not close {@code readable} if it is {@code Closeable}. + * + * @return the number of characters written + * @throws IOException if an I/O error occurs while reading from {@code readable} or writing to + * this sink + */ + public long writeFrom(Readable readable) throws IOException { + checkNotNull(readable); + + Closer closer = Closer.create(); + try { + Writer out = closer.register(openStream()); + long written = CharStreams.copy(readable, out); + out.flush(); // https://code.google.com/p/guava-libraries/issues/detail?id=1330 + return written; + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSource.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSource.java new file mode 100644 index 0000000000000..9e3e45a1b0f68 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharSource.java @@ -0,0 +1,733 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Ascii; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Splitter; +import org.apache.iotdb.google.common.collect.AbstractIterator; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Streams; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A readable source of characters, such as a text file. Unlike a {@link Reader}, a {@code + * CharSource} is not an open, stateful stream of characters that can be read and closed. Instead, + * it is an immutable supplier of {@code Reader} instances. + * + *

{@code CharSource} provides two kinds of methods: + * + *

    + *
  • Methods that return a reader: These methods should return a new, independent + * instance each time they are called. The caller is responsible for ensuring that the + * returned reader is closed. + *
  • Convenience methods: These are implementations of common operations that are + * typically implemented by opening a reader using one of the methods in the first category, + * doing something and finally closing the reader that was opened. + *
+ * + *

Several methods in this class, such as {@link #readLines()}, break the contents of the source + * into lines. Like {@link BufferedReader}, these methods break lines on any of {@code \n}, {@code + * \r} or {@code \r\n}, do not include the line separator in each line and do not consider there to + * be an empty line at the end if the contents are terminated with a line separator. + * + *

Any {@link ByteSource} containing text encoded with a specific {@linkplain Charset character + * encoding} may be viewed as a {@code CharSource} using {@link ByteSource#asCharSource(Charset)}. + * + *

Note: In general, {@code CharSource} is intended to be used for "file-like" sources + * that provide readers that are: + * + *

    + *
  • Finite: Many operations, such as {@link #length()} and {@link #read()}, will either + * block indefinitely or fail if the source creates an infinite reader. + *
  • Non-destructive: A destructive reader will consume or otherwise alter the + * source as they are read from it. A source that provides such readers will not be reusable, + * and operations that read from the stream (including {@link #length()}, in some + * implementations) will prevent further operations from completing as expected. + *
+ * + * @since 14.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class CharSource { + + /** Constructor for use by subclasses. */ + protected CharSource() {} + + /** + * Returns a {@link ByteSource} view of this char source that encodes chars read from this source + * as bytes using the given {@link Charset}. + * + *

If {@link ByteSource#asCharSource} is called on the returned source with the same charset, + * the default implementation of this method will ensure that the original {@code CharSource} is + * returned, rather than round-trip encoding. Subclasses that override this method should behave + * the same way. + * + * @since 20.0 + */ + public ByteSource asByteSource(Charset charset) { + return new AsByteSource(charset); + } + + /** + * Opens a new {@link Reader} for reading from this source. This method returns a new, independent + * reader each time it is called. + * + *

The caller is responsible for ensuring that the returned reader is closed. + * + * @throws IOException if an I/O error occurs while opening the reader + */ + public abstract Reader openStream() throws IOException; + + /** + * Opens a new {@link BufferedReader} for reading from this source. This method returns a new, + * independent reader each time it is called. + * + *

The caller is responsible for ensuring that the returned reader is closed. + * + * @throws IOException if an I/O error occurs while of opening the reader + */ + public BufferedReader openBufferedStream() throws IOException { + Reader reader = openStream(); + return (reader instanceof BufferedReader) + ? (BufferedReader) reader + : new BufferedReader(reader); + } + + /** + * Opens a new {@link Stream} for reading text one line at a time from this source. This method + * returns a new, independent stream each time it is called. + * + *

The returned stream is lazy and only reads from the source in the terminal operation. If an + * I/O error occurs while the stream is reading from the source or when the stream is closed, an + * {@link UncheckedIOException} is thrown. + * + *

Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + *

The caller is responsible for ensuring that the returned stream is closed. For example: + * + *

{@code
+   * try (Stream lines = source.lines()) {
+   *   lines.map(...)
+   *      .filter(...)
+   *      .forEach(...);
+   * }
+   * }
+ * + * @throws IOException if an I/O error occurs while opening the stream + * @since 22.0 + */ + public Stream lines() throws IOException { + BufferedReader reader = openBufferedStream(); + return reader + .lines() + .onClose( + () -> { + try { + reader.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + /** + * Returns the size of this source in chars, if the size can be easily determined without actually + * opening the data stream. + * + *

The default implementation returns {@link Optional#absent}. Some sources, such as a {@code + * CharSequence}, may return a non-absent value. Note that in such cases, it is possible + * that this method will return a different number of chars than would be returned by reading all + * of the chars. + * + *

Additionally, for mutable sources such as {@code StringBuilder}s, a subsequent read may + * return a different number of chars if the contents are changed. + * + * @since 19.0 + */ + public Optional lengthIfKnown() { + return Optional.absent(); + } + + /** + * Returns the length of this source in chars, even if doing so requires opening and traversing an + * entire stream. To avoid a potentially expensive operation, see {@link #lengthIfKnown}. + * + *

The default implementation calls {@link #lengthIfKnown} and returns the value if present. If + * absent, it will fall back to a heavyweight operation that will open a stream, {@link + * Reader#skip(long) skip} to the end of the stream, and return the total number of chars that + * were skipped. + * + *

Note that for sources that implement {@link #lengthIfKnown} to provide a more efficient + * implementation, it is possible that this method will return a different number of chars + * than would be returned by reading all of the chars. + * + *

In either case, for mutable sources such as files, a subsequent read may return a different + * number of chars if the contents are changed. + * + * @throws IOException if an I/O error occurs while reading the length of this source + * @since 19.0 + */ + public long length() throws IOException { + Optional lengthIfKnown = lengthIfKnown(); + if (lengthIfKnown.isPresent()) { + return lengthIfKnown.get(); + } + + Closer closer = Closer.create(); + try { + Reader reader = closer.register(openStream()); + return countBySkipping(reader); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + private long countBySkipping(Reader reader) throws IOException { + long count = 0; + long read; + while ((read = reader.skip(Long.MAX_VALUE)) != 0) { + count += read; + } + return count; + } + + /** + * Appends the contents of this source to the given {@link Appendable} (such as a {@link Writer}). + * Does not close {@code appendable} if it is {@code Closeable}. + * + * @return the number of characters copied + * @throws IOException if an I/O error occurs while reading from this source or writing to {@code + * appendable} + */ + public long copyTo(Appendable appendable) throws IOException { + checkNotNull(appendable); + + Closer closer = Closer.create(); + try { + Reader reader = closer.register(openStream()); + return CharStreams.copy(reader, appendable); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Copies the contents of this source to the given sink. + * + * @return the number of characters copied + * @throws IOException if an I/O error occurs while reading from this source or writing to {@code + * sink} + */ + public long copyTo(CharSink sink) throws IOException { + checkNotNull(sink); + + Closer closer = Closer.create(); + try { + Reader reader = closer.register(openStream()); + Writer writer = closer.register(sink.openStream()); + return CharStreams.copy(reader, writer); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads the contents of this source as a string. + * + * @throws IOException if an I/O error occurs while reading from this source + */ + public String read() throws IOException { + Closer closer = Closer.create(); + try { + Reader reader = closer.register(openStream()); + return CharStreams.toString(reader); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads the first line of this source as a string. Returns {@code null} if this source is empty. + * + *

Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + * @throws IOException if an I/O error occurs while reading from this source + */ + public String readFirstLine() throws IOException { + Closer closer = Closer.create(); + try { + BufferedReader reader = closer.register(openBufferedStream()); + return reader.readLine(); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads all the lines of this source as a list of strings. The returned list will be empty if + * this source is empty. + * + *

Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + * @throws IOException if an I/O error occurs while reading from this source + */ + public ImmutableList readLines() throws IOException { + Closer closer = Closer.create(); + try { + BufferedReader reader = closer.register(openBufferedStream()); + List result = Lists.newArrayList(); + String line; + while ((line = reader.readLine()) != null) { + result.add(line); + } + return ImmutableList.copyOf(result); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads lines of text from this source, processing each line as it is read using the given {@link + * LineProcessor processor}. Stops when all lines have been processed or the processor returns + * {@code false} and returns the result produced by the processor. + * + *

Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + * @throws IOException if an I/O error occurs while reading from this source or if {@code + * processor} throws an {@code IOException} + * @since 16.0 + */ + // some processors won't return a useful result + @ParametricNullness + public T readLines(LineProcessor processor) throws IOException { + checkNotNull(processor); + + Closer closer = Closer.create(); + try { + Reader reader = closer.register(openStream()); + return CharStreams.readLines(reader, processor); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Reads all lines of text from this source, running the given {@code action} for each line as it + * is read. + * + *

Like {@link BufferedReader#readLine()}, this method considers a line to be a sequence of + * text that is terminated by (but does not include) one of {@code \r\n}, {@code \r} or {@code + * \n}. If the source's content does not end in a line termination sequence, it is treated as if + * it does. + * + * @throws IOException if an I/O error occurs while reading from this source or if {@code action} + * throws an {@code UncheckedIOException} + * @since 22.0 + */ + public void forEachLine(Consumer action) throws IOException { + try (Stream lines = lines()) { + // The lines should be ordered regardless in most cases, but use forEachOrdered to be sure + lines.forEachOrdered(action); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + + /** + * Returns whether the source has zero chars. The default implementation first checks {@link + * #lengthIfKnown}, returning true if it's known to be zero and false if it's known to be + * non-zero. If the length is not known, it falls back to opening a stream and checking for EOF. + * + *

Note that, in cases where {@code lengthIfKnown} returns zero, it is possible that + * chars are actually available for reading. This means that a source may return {@code true} from + * {@code isEmpty()} despite having readable content. + * + * @throws IOException if an I/O error occurs + * @since 15.0 + */ + public boolean isEmpty() throws IOException { + Optional lengthIfKnown = lengthIfKnown(); + if (lengthIfKnown.isPresent()) { + return lengthIfKnown.get() == 0L; + } + Closer closer = Closer.create(); + try { + Reader reader = closer.register(openStream()); + return reader.read() == -1; + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from + * the source will contain the concatenated data from the streams of the underlying sources. + * + *

Only one underlying stream will be open at a time. Closing the concatenated stream will + * close the open underlying stream. + * + * @param sources the sources to concatenate + * @return a {@code CharSource} containing the concatenated data + * @since 15.0 + */ + public static CharSource concat(Iterable sources) { + return new ConcatenatedCharSource(sources); + } + + /** + * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from + * the source will contain the concatenated data from the streams of the underlying sources. + * + *

Only one underlying stream will be open at a time. Closing the concatenated stream will + * close the open underlying stream. + * + *

Note: The input {@code Iterator} will be copied to an {@code ImmutableList} when this method + * is called. This will fail if the iterator is infinite and may cause problems if the iterator + * eagerly fetches data for each source when iterated (rather than producing sources that only + * load data through their streams). Prefer using the {@link #concat(Iterable)} overload if + * possible. + * + * @param sources the sources to concatenate + * @return a {@code CharSource} containing the concatenated data + * @throws NullPointerException if any of {@code sources} is {@code null} + * @since 15.0 + */ + public static CharSource concat(Iterator sources) { + return concat(ImmutableList.copyOf(sources)); + } + + /** + * Concatenates multiple {@link CharSource} instances into a single source. Streams returned from + * the source will contain the concatenated data from the streams of the underlying sources. + * + *

Only one underlying stream will be open at a time. Closing the concatenated stream will + * close the open underlying stream. + * + * @param sources the sources to concatenate + * @return a {@code CharSource} containing the concatenated data + * @throws NullPointerException if any of {@code sources} is {@code null} + * @since 15.0 + */ + public static CharSource concat(CharSource... sources) { + return concat(ImmutableList.copyOf(sources)); + } + + /** + * Returns a view of the given character sequence as a {@link CharSource}. The behavior of the + * returned {@code CharSource} and any {@code Reader} instances created by it is unspecified if + * the {@code charSequence} is mutated while it is being read, so don't do that. + * + * @since 15.0 (since 14.0 as {@code CharStreams.asCharSource(String)}) + */ + public static CharSource wrap(CharSequence charSequence) { + return charSequence instanceof String + ? new StringCharSource((String) charSequence) + : new CharSequenceCharSource(charSequence); + } + + /** + * Returns an immutable {@link CharSource} that contains no characters. + * + * @since 15.0 + */ + public static CharSource empty() { + return EmptyCharSource.INSTANCE; + } + + /** A byte source that reads chars from this source and encodes them as bytes using a charset. */ + private final class AsByteSource extends ByteSource { + + final Charset charset; + + AsByteSource(Charset charset) { + this.charset = checkNotNull(charset); + } + + @Override + public CharSource asCharSource(Charset charset) { + if (charset.equals(this.charset)) { + return CharSource.this; + } + return super.asCharSource(charset); + } + + @Override + public InputStream openStream() throws IOException { + return new ReaderInputStream(CharSource.this.openStream(), charset, 8192); + } + + @Override + public String toString() { + return CharSource.this.toString() + ".asByteSource(" + charset + ")"; + } + } + + private static class CharSequenceCharSource extends CharSource { + + private static final Splitter LINE_SPLITTER = Splitter.onPattern("\r\n|\n|\r"); + + protected final CharSequence seq; + + protected CharSequenceCharSource(CharSequence seq) { + this.seq = checkNotNull(seq); + } + + @Override + public Reader openStream() { + return new CharSequenceReader(seq); + } + + @Override + public String read() { + return seq.toString(); + } + + @Override + public boolean isEmpty() { + return seq.length() == 0; + } + + @Override + public long length() { + return seq.length(); + } + + @Override + public Optional lengthIfKnown() { + return Optional.of((long) seq.length()); + } + + /** + * Returns an iterator over the lines in the string. If the string ends in a newline, a final + * empty string is not included, to match the behavior of BufferedReader/LineReader.readLine(). + */ + private Iterator linesIterator() { + return new AbstractIterator() { + Iterator lines = LINE_SPLITTER.split(seq).iterator(); + + @Override + protected String computeNext() { + if (lines.hasNext()) { + String next = lines.next(); + // skip last line if it's empty + if (lines.hasNext() || !next.isEmpty()) { + return next; + } + } + return endOfData(); + } + }; + } + + @Override + public Stream lines() { + return Streams.stream(linesIterator()); + } + + @Override + public String readFirstLine() { + Iterator lines = linesIterator(); + return lines.hasNext() ? lines.next() : null; + } + + @Override + public ImmutableList readLines() { + return ImmutableList.copyOf(linesIterator()); + } + + @Override + @ParametricNullness + public T readLines(LineProcessor processor) throws IOException { + Iterator lines = linesIterator(); + while (lines.hasNext()) { + if (!processor.processLine(lines.next())) { + break; + } + } + return processor.getResult(); + } + + @Override + public String toString() { + return "CharSource.wrap(" + Ascii.truncate(seq, 30, "...") + ")"; + } + } + + /** + * Subclass specialized for string instances. + * + *

Since Strings are immutable and built into the jdk we can optimize some operations + * + *

    + *
  • use {@link StringReader} instead of {@link CharSequenceReader}. It is faster since it can + * use {@link String#getChars(int, int, char[], int)} instead of copying characters one by + * one with {@link CharSequence#charAt(int)}. + *
  • use {@link Appendable#append(CharSequence)} in {@link #copyTo(Appendable)} and {@link + * #copyTo(CharSink)}. We know this is correct since strings are immutable and so the length + * can't change, and it is faster because many writers and appendables are optimized for + * appending string instances. + *
+ */ + private static class StringCharSource extends CharSequenceCharSource { + protected StringCharSource(String seq) { + super(seq); + } + + @Override + public Reader openStream() { + return new StringReader((String) seq); + } + + @Override + public long copyTo(Appendable appendable) throws IOException { + appendable.append(seq); + return seq.length(); + } + + @Override + public long copyTo(CharSink sink) throws IOException { + checkNotNull(sink); + Closer closer = Closer.create(); + try { + Writer writer = closer.register(sink.openStream()); + writer.write((String) seq); + return seq.length(); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + } + + private static final class EmptyCharSource extends StringCharSource { + + private static final EmptyCharSource INSTANCE = new EmptyCharSource(); + + private EmptyCharSource() { + super(""); + } + + @Override + public String toString() { + return "CharSource.empty()"; + } + } + + private static final class ConcatenatedCharSource extends CharSource { + + private final Iterable sources; + + ConcatenatedCharSource(Iterable sources) { + this.sources = checkNotNull(sources); + } + + @Override + public Reader openStream() throws IOException { + return new MultiReader(sources.iterator()); + } + + @Override + public boolean isEmpty() throws IOException { + for (CharSource source : sources) { + if (!source.isEmpty()) { + return false; + } + } + return true; + } + + @Override + public Optional lengthIfKnown() { + long result = 0L; + for (CharSource source : sources) { + Optional lengthIfKnown = source.lengthIfKnown(); + if (!lengthIfKnown.isPresent()) { + return Optional.absent(); + } + result += lengthIfKnown.get(); + } + return Optional.of(result); + } + + @Override + public long length() throws IOException { + long result = 0L; + for (CharSource source : sources) { + result += source.length(); + } + return result; + } + + @Override + public String toString() { + return "CharSource.concat(" + sources + ")"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharStreams.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharStreams.java new file mode 100644 index 0000000000000..aa69caef6c3bb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CharStreams.java @@ -0,0 +1,348 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Provides utility methods for working with character streams. + * + *

Some of the methods in this class take arguments with a generic type of {@code Readable & + * Closeable}. A {@link java.io.Reader} implements both of those interfaces. Similarly for {@code + * Appendable & Closeable} and {@link java.io.Writer}. + * + * @author Chris Nokleberg + * @author Bin Zhu + * @author Colin Decker + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class CharStreams { + + // 2K chars (4K bytes) + private static final int DEFAULT_BUF_SIZE = 0x800; + + /** Creates a new {@code CharBuffer} for buffering reads or writes. */ + static CharBuffer createBuffer() { + return CharBuffer.allocate(DEFAULT_BUF_SIZE); + } + + private CharStreams() {} + + /** + * Copies all characters between the {@link Readable} and {@link Appendable} objects. Does not + * close or flush either object. + * + * @param from the object to read from + * @param to the object to write to + * @return the number of characters copied + * @throws IOException if an I/O error occurs + */ + public static long copy(Readable from, Appendable to) throws IOException { + // The most common case is that from is a Reader (like InputStreamReader or StringReader) so + // take advantage of that. + if (from instanceof Reader) { + // optimize for common output types which are optimized to deal with char[] + if (to instanceof StringBuilder) { + return copyReaderToBuilder((Reader) from, (StringBuilder) to); + } else { + return copyReaderToWriter((Reader) from, asWriter(to)); + } + } + + checkNotNull(from); + checkNotNull(to); + long total = 0; + CharBuffer buf = createBuffer(); + while (from.read(buf) != -1) { + Java8Compatibility.flip(buf); + to.append(buf); + total += buf.remaining(); + Java8Compatibility.clear(buf); + } + return total; + } + + // TODO(lukes): consider allowing callers to pass in a buffer to use, some callers would be able + // to reuse buffers, others would be able to size them more appropriately than the constant + // defaults + + /** + * Copies all characters between the {@link Reader} and {@link StringBuilder} objects. Does not + * close or flush the reader. + * + *

This is identical to {@link #copy(Readable, Appendable)} but optimized for these specific + * types. CharBuffer has poor performance when being written into or read out of so round tripping + * all the bytes through the buffer takes a long time. With these specialized types we can just + * use a char array. + * + * @param from the object to read from + * @param to the object to write to + * @return the number of characters copied + * @throws IOException if an I/O error occurs + */ + static long copyReaderToBuilder(Reader from, StringBuilder to) throws IOException { + checkNotNull(from); + checkNotNull(to); + char[] buf = new char[DEFAULT_BUF_SIZE]; + int nRead; + long total = 0; + while ((nRead = from.read(buf)) != -1) { + to.append(buf, 0, nRead); + total += nRead; + } + return total; + } + + /** + * Copies all characters between the {@link Reader} and {@link Writer} objects. Does not close or + * flush the reader or writer. + * + *

This is identical to {@link #copy(Readable, Appendable)} but optimized for these specific + * types. CharBuffer has poor performance when being written into or read out of so round tripping + * all the bytes through the buffer takes a long time. With these specialized types we can just + * use a char array. + * + * @param from the object to read from + * @param to the object to write to + * @return the number of characters copied + * @throws IOException if an I/O error occurs + */ + static long copyReaderToWriter(Reader from, Writer to) throws IOException { + checkNotNull(from); + checkNotNull(to); + char[] buf = new char[DEFAULT_BUF_SIZE]; + int nRead; + long total = 0; + while ((nRead = from.read(buf)) != -1) { + to.write(buf, 0, nRead); + total += nRead; + } + return total; + } + + /** + * Reads all characters from a {@link Readable} object into a {@link String}. Does not close the + * {@code Readable}. + * + * @param r the object to read from + * @return a string containing all the characters + * @throws IOException if an I/O error occurs + */ + public static String toString(Readable r) throws IOException { + return toStringBuilder(r).toString(); + } + + /** + * Reads all characters from a {@link Readable} object into a new {@link StringBuilder} instance. + * Does not close the {@code Readable}. + * + * @param r the object to read from + * @return a {@link StringBuilder} containing all the characters + * @throws IOException if an I/O error occurs + */ + private static StringBuilder toStringBuilder(Readable r) throws IOException { + StringBuilder sb = new StringBuilder(); + if (r instanceof Reader) { + copyReaderToBuilder((Reader) r, sb); + } else { + copy(r, sb); + } + return sb; + } + + /** + * Reads all of the lines from a {@link Readable} object. The lines do not include + * line-termination characters, but do include other leading and trailing whitespace. + * + *

Does not close the {@code Readable}. If reading files or resources you should use the {@link + * Files#readLines} and {@link Resources#readLines} methods. + * + * @param r the object to read from + * @return a mutable {@link List} containing all the lines + * @throws IOException if an I/O error occurs + */ + public static List readLines(Readable r) throws IOException { + List result = new ArrayList<>(); + LineReader lineReader = new LineReader(r); + String line; + while ((line = lineReader.readLine()) != null) { + result.add(line); + } + return result; + } + + /** + * Streams lines from a {@link Readable} object, stopping when the processor returns {@code false} + * or all lines have been read and returning the result produced by the processor. Does not close + * {@code readable}. Note that this method may not fully consume the contents of {@code readable} + * if the processor stops processing early. + * + * @throws IOException if an I/O error occurs + * @since 14.0 + */ + // some processors won't return a useful result + @ParametricNullness + public static T readLines(Readable readable, LineProcessor processor) + throws IOException { + checkNotNull(readable); + checkNotNull(processor); + + LineReader lineReader = new LineReader(readable); + String line; + while ((line = lineReader.readLine()) != null) { + if (!processor.processLine(line)) { + break; + } + } + return processor.getResult(); + } + + /** + * Reads and discards data from the given {@code Readable} until the end of the stream is reached. + * Returns the total number of chars read. Does not close the stream. + * + * @since 20.0 + */ + public static long exhaust(Readable readable) throws IOException { + long total = 0; + long read; + CharBuffer buf = createBuffer(); + while ((read = readable.read(buf)) != -1) { + total += read; + Java8Compatibility.clear(buf); + } + return total; + } + + /** + * Discards {@code n} characters of data from the reader. This method will block until the full + * amount has been skipped. Does not close the reader. + * + * @param reader the reader to read from + * @param n the number of characters to skip + * @throws EOFException if this stream reaches the end before skipping all the characters + * @throws IOException if an I/O error occurs + */ + public static void skipFully(Reader reader, long n) throws IOException { + checkNotNull(reader); + while (n > 0) { + long amt = reader.skip(n); + if (amt == 0) { + throw new EOFException(); + } + n -= amt; + } + } + + /** + * Returns a {@link Writer} that simply discards written chars. + * + * @since 15.0 + */ + public static Writer nullWriter() { + return NullWriter.INSTANCE; + } + + private static final class NullWriter extends Writer { + + private static final NullWriter INSTANCE = new NullWriter(); + + @Override + public void write(int c) {} + + @Override + public void write(char[] cbuf) { + checkNotNull(cbuf); + } + + @Override + public void write(char[] cbuf, int off, int len) { + checkPositionIndexes(off, off + len, cbuf.length); + } + + @Override + public void write(String str) { + checkNotNull(str); + } + + @Override + public void write(String str, int off, int len) { + checkPositionIndexes(off, off + len, str.length()); + } + + @Override + public Writer append(CharSequence csq) { + return this; + } + + @Override + public Writer append(CharSequence csq, int start, int end) { + checkPositionIndexes(start, end, csq == null ? "null".length() : csq.length()); + return this; + } + + @Override + public Writer append(char c) { + return this; + } + + @Override + public void flush() {} + + @Override + public void close() {} + + @Override + public String toString() { + return "CharStreams.nullWriter()"; + } + } + + /** + * Returns a Writer that sends all output to the given {@link Appendable} target. Closing the + * writer will close the target if it is {@link Closeable}, and flushing the writer will flush the + * target if it is {@link java.io.Flushable}. + * + * @param target the object to which output will be sent + * @return a new Writer object, unless target is a Writer, in which case the target is returned + */ + public static Writer asWriter(Appendable target) { + if (target instanceof Writer) { + return (Writer) target; + } + return new AppendableWriter(target); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Closeables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Closeables.java new file mode 100644 index 0000000000000..f95a824e3fb8b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Closeables.java @@ -0,0 +1,135 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility methods for working with {@link Closeable} objects. + * + * @author Michael Lancaster + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Closeables { + @VisibleForTesting static final Logger logger = Logger.getLogger(Closeables.class.getName()); + + private Closeables() {} + + /** + * Closes a {@link Closeable}, with control over whether an {@code IOException} may be thrown. + * This is primarily useful in a finally block, where a thrown exception needs to be logged but + * not propagated (otherwise the original exception will be lost). + * + *

If {@code swallowIOException} is true then we never throw {@code IOException} but merely log + * it. + * + *

Example: + * + *

{@code
+   * public void useStreamNicely() throws IOException {
+   *   SomeStream stream = new SomeStream("foo");
+   *   boolean threw = true;
+   *   try {
+   *     // ... code which does something with the stream ...
+   *     threw = false;
+   *   } finally {
+   *     // If an exception occurs, rethrow it only if threw==false:
+   *     Closeables.close(stream, threw);
+   *   }
+   * }
+   * }
+ * + * @param closeable the {@code Closeable} object to be closed, or null, in which case this method + * does nothing + * @param swallowIOException if true, don't propagate IO exceptions thrown by the {@code close} + * methods + * @throws IOException if {@code swallowIOException} is false and {@code close} throws an {@code + * IOException}. + */ + public static void close(Closeable closeable, boolean swallowIOException) throws IOException { + if (closeable == null) { + return; + } + try { + closeable.close(); + } catch (IOException e) { + if (swallowIOException) { + logger.log(Level.WARNING, "IOException thrown while closing Closeable.", e); + } else { + throw e; + } + } + } + + /** + * Closes the given {@link InputStream}, logging any {@code IOException} that's thrown rather than + * propagating it. + * + *

While it's not safe in the general case to ignore exceptions that are thrown when closing an + * I/O resource, it should generally be safe in the case of a resource that's being used only for + * reading, such as an {@code InputStream}. Unlike with writable resources, there's no chance that + * a failure that occurs when closing the stream indicates a meaningful problem such as a failure + * to flush all bytes to the underlying resource. + * + * @param inputStream the input stream to be closed, or {@code null} in which case this method + * does nothing + * @since 17.0 + */ + public static void closeQuietly(InputStream inputStream) { + try { + close(inputStream, true); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } + + /** + * Closes the given {@link Reader}, logging any {@code IOException} that's thrown rather than + * propagating it. + * + *

While it's not safe in the general case to ignore exceptions that are thrown when closing an + * I/O resource, it should generally be safe in the case of a resource that's being used only for + * reading, such as a {@code Reader}. Unlike with writable resources, there's no chance that a + * failure that occurs when closing the reader indicates a meaningful problem such as a failure to + * flush all bytes to the underlying resource. + * + * @param reader the reader to be closed, or {@code null} in which case this method does nothing + * @since 17.0 + */ + public static void closeQuietly(Reader reader) { + try { + close(reader, true); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Closer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Closer.java new file mode 100644 index 0000000000000..fd61be1e828ee --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Closer.java @@ -0,0 +1,297 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Throwables; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.logging.Level; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Closeable} that collects {@code Closeable} resources and closes them all when it is + * {@linkplain #close closed}. This is intended to approximately emulate the behavior of Java 7's try-with-resources statement in JDK6-compatible code. Running on Java 7, code using this + * should be approximately equivalent in behavior to the same code written with try-with-resources. + * Running on Java 6, exceptions that cannot be thrown must be logged rather than being added to the + * thrown exception as a suppressed exception. + * + *

This class is intended to be used in the following pattern: + * + *

{@code
+ * Closer closer = Closer.create();
+ * try {
+ *   InputStream in = closer.register(openInputStream());
+ *   OutputStream out = closer.register(openOutputStream());
+ *   // do stuff
+ * } catch (Throwable e) {
+ *   // ensure that any checked exception types other than IOException that could be thrown are
+ *   // provided here, e.g. throw closer.rethrow(e, CheckedException.class);
+ *   throw closer.rethrow(e);
+ * } finally {
+ *   closer.close();
+ * }
+ * }
+ * + *

Note that this try-catch-finally block is not equivalent to a try-catch-finally block using + * try-with-resources. To get the equivalent of that, you must wrap the above code in another + * try block in order to catch any exception that may be thrown (including from the call to {@code + * close()}). + * + *

This pattern ensures the following: + * + *

    + *
  • Each {@code Closeable} resource that is successfully registered will be closed later. + *
  • If a {@code Throwable} is thrown in the try block, no exceptions that occur when attempting + * to close resources will be thrown from the finally block. The throwable from the try block + * will be thrown. + *
  • If no exceptions or errors were thrown in the try block, the first exception thrown + * by an attempt to close a resource will be thrown. + *
  • Any exception caught when attempting to close a resource that is not thrown (because + * another exception is already being thrown) is suppressed. + *
+ * + *

An exception that is suppressed is not thrown. The method of suppression used depends on the + * version of Java the code is running on: + * + *

    + *
  • Java 7+: Exceptions are suppressed by adding them to the exception that will + * be thrown using {@code Throwable.addSuppressed(Throwable)}. + *
  • Java 6: Exceptions are suppressed by logging them instead. + *
+ * + * @author Colin Decker + * @since 14.0 + */ +// Coffee's for {@link Closer closers} only. +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Closer implements Closeable { + + /** The suppressor implementation to use for the current Java version. */ + private static final Suppressor SUPPRESSOR; + + static { + SuppressingSuppressor suppressingSuppressor = SuppressingSuppressor.tryCreate(); + SUPPRESSOR = suppressingSuppressor == null ? LoggingSuppressor.INSTANCE : suppressingSuppressor; + } + + /** Creates a new {@link Closer}. */ + public static Closer create() { + return new Closer(SUPPRESSOR); + } + + @VisibleForTesting final Suppressor suppressor; + + // only need space for 2 elements in most cases, so try to use the smallest array possible + private final Deque stack = new ArrayDeque<>(4); + private Throwable thrown; + + @VisibleForTesting + Closer(Suppressor suppressor) { + this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests + } + + /** + * Registers the given {@code closeable} to be closed when this {@code Closer} is {@linkplain + * #close closed}. + * + * @return the given {@code closeable} + */ + // close. this word no longer has any meaning to me. + @ParametricNullness + public C register(@ParametricNullness C closeable) { + if (closeable != null) { + stack.addFirst(closeable); + } + + return closeable; + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an {@code + * IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown wrapped + * in a {@code RuntimeException}. Note: Be sure to declare all of the checked exception + * types your try block can throw when calling an overload of this method so as to avoid losing + * the original exception type. + * + *

This method always throws, and as such should be called as {@code throw closer.rethrow(e);} + * to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + */ + public RuntimeException rethrow(Throwable e) throws IOException { + checkNotNull(e); + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + throw new RuntimeException(e); + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an {@code + * IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the given type. + * Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. Note: Be sure to + * declare all of the checked exception types your try block can throw when calling an overload of + * this method so as to avoid losing the original exception type. + * + *

This method always throws, and as such should be called as {@code throw closer.rethrow(e, + * ...);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + * @throws X when the given throwable is of the declared type X + */ + public RuntimeException rethrow(Throwable e, Class declaredType) + throws IOException, X { + checkNotNull(e); + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType); + throw new RuntimeException(e); + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an {@code + * IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either of the + * given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. Note: + * Be sure to declare all of the checked exception types your try block can throw when calling an + * overload of this method so as to avoid losing the original exception type. + * + *

This method always throws, and as such should be called as {@code throw closer.rethrow(e, + * ...);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + * @throws X1 when the given throwable is of the declared type X1 + * @throws X2 when the given throwable is of the declared type X2 + */ + public RuntimeException rethrow( + Throwable e, Class declaredType1, Class declaredType2) throws IOException, X1, X2 { + checkNotNull(e); + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType1, declaredType2); + throw new RuntimeException(e); + } + + /** + * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an + * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods, + * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the + * first exception to be thrown from an attempt to close a closeable will be thrown and any + * additional exceptions that are thrown after that will be suppressed. + */ + @Override + public void close() throws IOException { + Throwable throwable = thrown; + + // close closeables in LIFO order + while (!stack.isEmpty()) { + Closeable closeable = stack.removeFirst(); + try { + closeable.close(); + } catch (Throwable e) { + if (throwable == null) { + throwable = e; + } else { + suppressor.suppress(closeable, throwable, e); + } + } + } + + if (thrown == null && throwable != null) { + Throwables.propagateIfPossible(throwable, IOException.class); + throw new AssertionError(throwable); // not possible + } + } + + /** Suppression strategy interface. */ + @VisibleForTesting + interface Suppressor { + /** + * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close + * the given closeable. {@code thrown} is the exception that is actually being thrown from the + * method. Implementations of this method should not throw under any circumstances. + */ + void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); + } + + /** Suppresses exceptions by logging them. */ + @VisibleForTesting + static final class LoggingSuppressor implements Suppressor { + + static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); + + @Override + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + // log to the same place as Closeables + Closeables.logger.log( + Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); + } + } + + /** + * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's + * addSuppressed(Throwable) mechanism. + */ + @VisibleForTesting + static final class SuppressingSuppressor implements Suppressor { + static SuppressingSuppressor tryCreate() { + Method addSuppressed; + try { + addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); + } catch (Throwable e) { + return null; + } + return new SuppressingSuppressor(addSuppressed); + } + + private final Method addSuppressed; + + private SuppressingSuppressor(Method addSuppressed) { + this.addSuppressed = addSuppressed; + } + + @Override + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + // ensure no exceptions from addSuppressed + if (thrown == suppressed) { + return; + } + try { + addSuppressed.invoke(thrown, suppressed); + } catch (Throwable e) { + // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging + LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CountingInputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CountingInputStream.java new file mode 100644 index 0000000000000..bcbb4f7acabdb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CountingInputStream.java @@ -0,0 +1,103 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An {@link InputStream} that counts the number of bytes read. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class CountingInputStream extends FilterInputStream { + + private long count; + private long mark = -1; + + /** + * Wraps another input stream, counting the number of bytes read. + * + * @param in the input stream to be wrapped + */ + public CountingInputStream(InputStream in) { + super(checkNotNull(in)); + } + + /** Returns the number of bytes read. */ + public long getCount() { + return count; + } + + @Override + public int read() throws IOException { + int result = in.read(); + if (result != -1) { + count++; + } + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int result = in.read(b, off, len); + if (result != -1) { + count += result; + } + return result; + } + + @Override + public long skip(long n) throws IOException { + long result = in.skip(n); + count += result; + return result; + } + + @Override + public synchronized void mark(int readlimit) { + in.mark(readlimit); + mark = count; + // it's okay to mark even if mark isn't supported, as reset won't work + } + + @Override + public synchronized void reset() throws IOException { + if (!in.markSupported()) { + throw new IOException("Mark not supported"); + } + if (mark == -1) { + throw new IOException("Mark not set"); + } + + in.reset(); + count = mark; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CountingOutputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CountingOutputStream.java new file mode 100644 index 0000000000000..292c3d40d9dc4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/CountingOutputStream.java @@ -0,0 +1,77 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An OutputStream that counts the number of bytes written. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class CountingOutputStream extends FilterOutputStream { + + private long count; + + /** + * Wraps another output stream, counting the number of bytes written. + * + * @param out the output stream to be wrapped + */ + public CountingOutputStream(OutputStream out) { + super(checkNotNull(out)); + } + + /** Returns the number of bytes written. */ + public long getCount() { + return count; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + count += len; + } + + @Override + public void write(int b) throws IOException { + out.write(b); + count++; + } + + // Overriding close() because FilterOutputStream's close() method pre-JDK8 has bad behavior: + // it silently ignores any exception thrown by flush(). Instead, just close the delegate stream. + // It should flush itself if necessary. + @Override + public void close() throws IOException { + out.close(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..65631c1d54ccb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/FileBackedOutputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/FileBackedOutputStream.java new file mode 100644 index 0000000000000..a0e00170b90a6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/FileBackedOutputStream.java @@ -0,0 +1,254 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * An {@link OutputStream} that starts buffering to a byte array, but switches to file buffering + * once the data reaches a configurable size. + * + *

When this stream creates a temporary file, it restricts the file's permissions to the current + * user or, in the case of Android, the current app. If that is not possible (as is the case under + * the very old Android Ice Cream Sandwich release), then this stream throws an exception instead of + * creating a file that would be more accessible. (This behavior is new in Guava 32.0.0. Previous + * versions would create a file that is more accessible, as discussed in Guava issue 2575. TODO: b/283778848 - Fill + * in CVE number once it's available.) + * + *

Temporary files created by this stream may live in the local filesystem until either: + * + *

    + *
  • {@link #reset} is called (removing the data in this stream and deleting the file), or... + *
  • this stream (or, more precisely, its {@link #asByteSource} view) is finalized during + * garbage collection, AND this stream was not constructed with {@linkplain + * #FileBackedOutputStream(int) the 1-arg constructor} or the {@linkplain + * #FileBackedOutputStream(int, boolean) 2-arg constructor} passing {@code false} in the + * second parameter. + *
+ * + *

This class is thread-safe. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@Beta +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class FileBackedOutputStream extends OutputStream { + private final int fileThreshold; + private final boolean resetOnFinalize; + private final ByteSource source; + + private OutputStream out; + + private MemoryOutput memory; + + private File file; + + /** ByteArrayOutputStream that exposes its internals. */ + private static class MemoryOutput extends ByteArrayOutputStream { + byte[] getBuffer() { + return buf; + } + + int getCount() { + return count; + } + } + + /** Returns the file holding the data (possibly null). */ + @VisibleForTesting + synchronized File getFile() { + return file; + } + + /** + * Creates a new instance that uses the given file threshold, and does not reset the data when the + * {@link ByteSource} returned by {@link #asByteSource} is finalized. + * + * @param fileThreshold the number of bytes before the stream should switch to buffering to a file + * @throws IllegalArgumentException if {@code fileThreshold} is negative + */ + public FileBackedOutputStream(int fileThreshold) { + this(fileThreshold, false); + } + + /** + * Creates a new instance that uses the given file threshold, and optionally resets the data when + * the {@link ByteSource} returned by {@link #asByteSource} is finalized. + * + * @param fileThreshold the number of bytes before the stream should switch to buffering to a file + * @param resetOnFinalize if true, the {@link #reset} method will be called when the {@link + * ByteSource} returned by {@link #asByteSource} is finalized. + * @throws IllegalArgumentException if {@code fileThreshold} is negative + */ + public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) { + checkArgument( + fileThreshold >= 0, "fileThreshold must be non-negative, but was %s", fileThreshold); + this.fileThreshold = fileThreshold; + this.resetOnFinalize = resetOnFinalize; + memory = new MemoryOutput(); + out = memory; + + if (resetOnFinalize) { + source = + new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return openInputStream(); + } + + @Override + protected void finalize() { + try { + reset(); + } catch (Throwable t) { + t.printStackTrace(System.err); + } + } + }; + } else { + source = + new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return openInputStream(); + } + }; + } + } + + /** + * Returns a readable {@link ByteSource} view of the data that has been written to this stream. + * + * @since 15.0 + */ + public ByteSource asByteSource() { + return source; + } + + private synchronized InputStream openInputStream() throws IOException { + if (file != null) { + return new FileInputStream(file); + } else { + // requireNonNull is safe because we always have either `file` or `memory`. + requireNonNull(memory); + return new ByteArrayInputStream(memory.getBuffer(), 0, memory.getCount()); + } + } + + /** + * Calls {@link #close} if not already closed, and then resets this object back to its initial + * state, for reuse. If data was buffered to a file, it will be deleted. + * + * @throws IOException if an I/O error occurred while deleting the file buffer + */ + public synchronized void reset() throws IOException { + try { + close(); + } finally { + if (memory == null) { + memory = new MemoryOutput(); + } else { + memory.reset(); + } + out = memory; + if (file != null) { + File deleteMe = file; + file = null; + if (!deleteMe.delete()) { + throw new IOException("Could not delete: " + deleteMe); + } + } + } + } + + @Override + public synchronized void write(int b) throws IOException { + update(1); + out.write(b); + } + + @Override + public synchronized void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public synchronized void write(byte[] b, int off, int len) throws IOException { + update(len); + out.write(b, off, len); + } + + @Override + public synchronized void close() throws IOException { + out.close(); + } + + @Override + public synchronized void flush() throws IOException { + out.flush(); + } + + /** + * Checks if writing {@code len} bytes would go over threshold, and switches to file buffering if + * so. + */ + private void update(int len) throws IOException { + if (memory != null && (memory.getCount() + len > fileThreshold)) { + File temp = TempFileCreator.INSTANCE.createTempFile("FileBackedOutputStream"); + if (resetOnFinalize) { + // Finalizers are not guaranteed to be called on system shutdown; + // this is insurance. + temp.deleteOnExit(); + } + try { + FileOutputStream transfer = new FileOutputStream(temp); + transfer.write(memory.getBuffer(), 0, memory.getCount()); + transfer.flush(); + // We've successfully transferred the data; switch to writing to file + out = transfer; + } catch (IOException e) { + temp.delete(); + throw e; + } + + file = temp; + memory = null; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/FileWriteMode.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/FileWriteMode.java new file mode 100644 index 0000000000000..18e40876b52ff --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/FileWriteMode.java @@ -0,0 +1,37 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Modes for opening a file for writing. The default when mode when none is specified is to truncate + * the file before writing. + * + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public enum FileWriteMode { + /** Specifies that writes to the opened file should append to the end of the file. */ + APPEND +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Files.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Files.java new file mode 100644 index 0000000000000..abfa35818d8c0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Files.java @@ -0,0 +1,876 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.base.Splitter; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Lists; +import org.apache.iotdb.google.common.graph.SuccessorsFunction; +import org.apache.iotdb.google.common.graph.Traverser; +import org.apache.iotdb.google.common.hash.HashCode; +import org.apache.iotdb.google.common.hash.HashFunction; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.io.FileWriteMode.APPEND; + +/** + * Provides utility methods for working with {@linkplain File files}. + * + *

{@link java.nio.file.Path} users will find similar utilities in {@link MoreFiles} and the + * JDK's {@link java.nio.file.Files} class. + * + * @author Chris Nokleberg + * @author Colin Decker + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Files { + + private Files() {} + + /** + * Returns a buffered reader that reads from a file using the given character set. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#newBufferedReader(java.nio.file.Path, Charset)}. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants + * @return the buffered reader + */ + public static BufferedReader newReader(File file, Charset charset) throws FileNotFoundException { + checkNotNull(file); + checkNotNull(charset); + return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); + } + + /** + * Returns a buffered writer that writes to a file using the given character set. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#newBufferedWriter(java.nio.file.Path, Charset, + * java.nio.file.OpenOption...)}. + * + * @param file the file to write to + * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for + * helpful predefined constants + * @return the buffered writer + */ + public static BufferedWriter newWriter(File file, Charset charset) throws FileNotFoundException { + checkNotNull(file); + checkNotNull(charset); + return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)); + } + + /** + * Returns a new {@link ByteSource} for reading bytes from the given file. + * + * @since 14.0 + */ + public static ByteSource asByteSource(File file) { + return new FileByteSource(file); + } + + private static final class FileByteSource extends ByteSource { + + private final File file; + + private FileByteSource(File file) { + this.file = checkNotNull(file); + } + + @Override + public FileInputStream openStream() throws IOException { + return new FileInputStream(file); + } + + @Override + public Optional sizeIfKnown() { + if (file.isFile()) { + return Optional.of(file.length()); + } else { + return Optional.absent(); + } + } + + @Override + public long size() throws IOException { + if (!file.isFile()) { + throw new FileNotFoundException(file.toString()); + } + return file.length(); + } + + @Override + public byte[] read() throws IOException { + Closer closer = Closer.create(); + try { + FileInputStream in = closer.register(openStream()); + return ByteStreams.toByteArray(in, in.getChannel().size()); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + @Override + public String toString() { + return "Files.asByteSource(" + file + ")"; + } + } + + /** + * Returns a new {@link ByteSink} for writing bytes to the given file. The given {@code modes} + * control how the file is opened for writing. When no mode is provided, the file will be + * truncated before writing. When the {@link FileWriteMode#APPEND APPEND} mode is provided, writes + * will append to the end of the file without truncating it. + * + * @since 14.0 + */ + public static ByteSink asByteSink(File file, FileWriteMode... modes) { + return new FileByteSink(file, modes); + } + + private static final class FileByteSink extends ByteSink { + + private final File file; + private final ImmutableSet modes; + + private FileByteSink(File file, FileWriteMode... modes) { + this.file = checkNotNull(file); + this.modes = ImmutableSet.copyOf(modes); + } + + @Override + public FileOutputStream openStream() throws IOException { + return new FileOutputStream(file, modes.contains(APPEND)); + } + + @Override + public String toString() { + return "Files.asByteSink(" + file + ", " + modes + ")"; + } + } + + /** + * Returns a new {@link CharSource} for reading character data from the given file using the given + * character set. + * + * @since 14.0 + */ + public static CharSource asCharSource(File file, Charset charset) { + return asByteSource(file).asCharSource(charset); + } + + /** + * Returns a new {@link CharSink} for writing character data to the given file using the given + * character set. The given {@code modes} control how the file is opened for writing. When no mode + * is provided, the file will be truncated before writing. When the {@link FileWriteMode#APPEND + * APPEND} mode is provided, writes will append to the end of the file without truncating it. + * + * @since 14.0 + */ + public static CharSink asCharSink(File file, Charset charset, FileWriteMode... modes) { + return asByteSink(file, modes).asCharSink(charset); + } + + /** + * Reads all bytes from a file into a byte array. + * + *

{@link java.nio.file.Path} equivalent: {@link java.nio.file.Files#readAllBytes}. + * + * @param file the file to read from + * @return a byte array containing all the bytes from file + * @throws IllegalArgumentException if the file is bigger than the largest possible byte array + * (2^31 - 1) + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(File file) throws IOException { + return asByteSource(file).read(); + } + + /** + * Reads all characters from a file into a {@link String}, using the given character set. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants + * @return a string containing all the characters from the file + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asCharSource(file, charset).read()}. + */ + @Deprecated + public static String toString(File file, Charset charset) throws IOException { + return asCharSource(file, charset).read(); + } + + /** + * Overwrites a file with the contents of a byte array. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#write(java.nio.file.Path, byte[], java.nio.file.OpenOption...)}. + * + * @param from the bytes to write + * @param to the destination file + * @throws IOException if an I/O error occurs + */ + public static void write(byte[] from, File to) throws IOException { + asByteSink(to).write(from); + } + + /** + * Writes a character sequence (such as a string) to a file using the given character set. + * + * @param from the character sequence to write + * @param to the destination file + * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for + * helpful predefined constants + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asCharSink(to, charset).write(from)}. + */ + @Deprecated + public static void write(CharSequence from, File to, Charset charset) throws IOException { + asCharSink(to, charset).write(from); + } + + /** + * Copies all bytes from a file to an output stream. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#copy(java.nio.file.Path, OutputStream)}. + * + * @param from the source file + * @param to the output stream + * @throws IOException if an I/O error occurs + */ + public static void copy(File from, OutputStream to) throws IOException { + asByteSource(from).copyTo(to); + } + + /** + * Copies all the bytes from one file to another. + * + *

Copying is not an atomic operation - in the case of an I/O error, power loss, process + * termination, or other problems, {@code to} may not be a complete copy of {@code from}. If you + * need to guard against those conditions, you should employ other file-level synchronization. + * + *

Warning: If {@code to} represents an existing file, that file will be overwritten + * with the contents of {@code from}. If {@code to} and {@code from} refer to the same + * file, the contents of that file will be deleted. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...)}. + * + * @param from the source file + * @param to the destination file + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if {@code from.equals(to)} + */ + public static void copy(File from, File to) throws IOException { + checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); + asByteSource(from).copyTo(asByteSink(to)); + } + + /** + * Copies all characters from a file to an appendable object, using the given character set. + * + * @param from the source file + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants + * @param to the appendable object + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asCharSource(from, charset).copyTo(to)}. + */ + @Deprecated + public static void copy(File from, Charset charset, Appendable to) throws IOException { + asCharSource(from, charset).copyTo(to); + } + + /** + * Appends a character sequence (such as a string) to a file using the given character set. + * + * @param from the character sequence to append + * @param to the destination file + * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for + * helpful predefined constants + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asCharSink(to, charset, FileWriteMode.APPEND).write(from)}. This + * method is scheduled to be removed in October 2019. + */ + @Deprecated + public static void append(CharSequence from, File to, Charset charset) throws IOException { + asCharSink(to, charset, FileWriteMode.APPEND).write(from); + } + + /** + * Returns true if the given files exist, are not directories, and contain the same bytes. + * + * @throws IOException if an I/O error occurs + */ + public static boolean equal(File file1, File file2) throws IOException { + checkNotNull(file1); + checkNotNull(file2); + if (file1 == file2 || file1.equals(file2)) { + return true; + } + + /* + * Some operating systems may return zero as the length for files denoting system-dependent + * entities such as devices or pipes, in which case we must fall back on comparing the bytes + * directly. + */ + long len1 = file1.length(); + long len2 = file2.length(); + if (len1 != 0 && len2 != 0 && len1 != len2) { + return false; + } + return asByteSource(file1).contentEquals(asByteSource(file2)); + } + + /** + * Atomically creates a new directory somewhere beneath the system's temporary directory (as + * defined by the {@code java.io.tmpdir} system property), and returns its name. + * + *

The temporary directory is created with permissions restricted to the current user or, in + * the case of Android, the current app. If that is not possible (as is the case under the very + * old Android Ice Cream Sandwich release), then this method throws an exception instead of + * creating a directory that would be more accessible. (This behavior is new in Guava 32.0.0. + * Previous versions would create a directory that is more accessible, as discussed in CVE-2020-8908.) + * + *

Use this method instead of {@link File#createTempFile(String, String)} when you wish to + * create a directory, not a regular file. A common pitfall is to call {@code createTempFile}, + * delete the file and create a directory in its place, but this leads a race condition which can + * be exploited to create security vulnerabilities, especially when executable files are to be + * written into the directory. + * + *

This method assumes that the temporary volume is writable, has free inodes and free blocks, + * and that it will not be called thousands of times per second. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#createTempDirectory}. + * + * @return the newly-created directory + * @throws IllegalStateException if the directory could not be created, such as if the system does + * not support creating temporary directories securely + * @deprecated For Android users, see the Data and File + * Storage overview to select an appropriate temporary directory (perhaps {@code + * context.getCacheDir()}), and create your own directory under that. (For example, you might + * use {@code new File(context.getCacheDir(), "directoryname").mkdir()}, or, if you need an + * arbitrary number of temporary directories, you might have to generate multiple directory + * names in a loop until {@code mkdir()} returns {@code true}.) For developers on Java 7 or + * later, use {@link java.nio.file.Files#createTempDirectory}, transforming it to a {@link + * File} using {@link java.nio.file.Path#toFile() toFile()} if needed. To restrict permissions + * as this method does, pass {@code + * PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"))} to your + * call to {@code createTempDirectory}. + */ + @Beta + @Deprecated + public static File createTempDir() { + return TempFileCreator.INSTANCE.createTempDir(); + } + + /** + * Creates an empty file or updates the last updated timestamp on the same as the unix command of + * the same name. + * + * @param file the file to create or update + * @throws IOException if an I/O error occurs + */ + @SuppressWarnings("GoodTime") // reading system time without TimeSource + public static void touch(File file) throws IOException { + checkNotNull(file); + if (!file.createNewFile() && !file.setLastModified(System.currentTimeMillis())) { + throw new IOException("Unable to update modification time of " + file); + } + } + + /** + * Creates any necessary but nonexistent parent directories of the specified file. Note that if + * this operation fails it may have succeeded in creating some (but not all) of the necessary + * parent directories. + * + * @throws IOException if an I/O error occurs, or if any necessary but nonexistent parent + * directories of the specified file could not be created. + * @since 4.0 + */ + public static void createParentDirs(File file) throws IOException { + checkNotNull(file); + File parent = file.getCanonicalFile().getParentFile(); + if (parent == null) { + /* + * The given directory is a filesystem root. All zero of its ancestors exist. This doesn't + * mean that the root itself exists -- consider x:\ on a Windows machine without such a drive + * -- or even that the caller can create it, but this method makes no such guarantees even for + * non-root files. + */ + return; + } + parent.mkdirs(); + if (!parent.isDirectory()) { + throw new IOException("Unable to create parent directories of " + file); + } + } + + /** + * Moves a file from one path to another. This method can rename a file and/or move it to a + * different directory. In either case {@code to} must be the target path for the file itself; not + * just the new name for the file or the path to the new parent directory. + * + *

{@link java.nio.file.Path} equivalent: {@link java.nio.file.Files#move}. + * + * @param from the source file + * @param to the destination file + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if {@code from.equals(to)} + */ + public static void move(File from, File to) throws IOException { + checkNotNull(from); + checkNotNull(to); + checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); + + if (!from.renameTo(to)) { + copy(from, to); + if (!from.delete()) { + if (!to.delete()) { + throw new IOException("Unable to delete " + to); + } + throw new IOException("Unable to delete " + from); + } + } + } + + /** + * Reads the first line from a file. The line does not include line-termination characters, but + * does include other leading and trailing whitespace. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants + * @return the first line, or null if the file is empty + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asCharSource(file, charset).readFirstLine()}. + */ + @Deprecated + public static String readFirstLine(File file, Charset charset) throws IOException { + return asCharSource(file, charset).readFirstLine(); + } + + /** + * Reads all of the lines from a file. The lines do not include line-termination characters, but + * do include other leading and trailing whitespace. + * + *

This method returns a mutable {@code List}. For an {@code ImmutableList}, use {@code + * Files.asCharSource(file, charset).readLines()}. + * + *

{@link java.nio.file.Path} equivalent: {@link + * java.nio.file.Files#readAllLines(java.nio.file.Path, Charset)}. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants + * @return a mutable {@link List} containing all the lines + * @throws IOException if an I/O error occurs + */ + public static List readLines(File file, Charset charset) throws IOException { + // don't use asCharSource(file, charset).readLines() because that returns + // an immutable list, which would change the behavior of this method + return asCharSource(file, charset) + .readLines( + new LineProcessor>() { + final List result = Lists.newArrayList(); + + @Override + public boolean processLine(String line) { + result.add(line); + return true; + } + + @Override + public List getResult() { + return result; + } + }); + } + + /** + * Streams lines from a {@link File}, stopping when our callback returns false, or we have read + * all of the lines. + * + * @param file the file to read from + * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for + * helpful predefined constants + * @param callback the {@link LineProcessor} to use to handle the lines + * @return the output of processing the lines + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asCharSource(file, charset).readLines(callback)}. + */ + @Deprecated + + // some processors won't return a useful result + @ParametricNullness + public static T readLines( + File file, Charset charset, LineProcessor callback) throws IOException { + return asCharSource(file, charset).readLines(callback); + } + + /** + * Process the bytes of a file. + * + *

(If this seems too complicated, maybe you're looking for {@link #toByteArray}.) + * + * @param file the file to read + * @param processor the object to which the bytes of the file are passed. + * @return the result of the byte processor + * @throws IOException if an I/O error occurs + * @deprecated Prefer {@code asByteSource(file).read(processor)}. + */ + @Deprecated + + // some processors won't return a useful result + @ParametricNullness + public static T readBytes(File file, ByteProcessor processor) + throws IOException { + return asByteSource(file).read(processor); + } + + /** + * Computes the hash code of the {@code file} using {@code hashFunction}. + * + * @param file the file to read + * @param hashFunction the hash function to use to hash the data + * @return the {@link HashCode} of all of the bytes in the file + * @throws IOException if an I/O error occurs + * @since 12.0 + * @deprecated Prefer {@code asByteSource(file).hash(hashFunction)}. + */ + @Deprecated + public static HashCode hash(File file, HashFunction hashFunction) throws IOException { + return asByteSource(file).hash(hashFunction); + } + + /** + * Fully maps a file read-only in to memory as per {@link + * FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}. + * + *

Files are mapped from offset 0 to its length. + * + *

This only works for files ≤ {@link Integer#MAX_VALUE} bytes. + * + * @param file the file to map + * @return a read-only buffer reflecting {@code file} + * @throws FileNotFoundException if the {@code file} does not exist + * @throws IOException if an I/O error occurs + * @see FileChannel#map(MapMode, long, long) + * @since 2.0 + */ + public static MappedByteBuffer map(File file) throws IOException { + checkNotNull(file); + return map(file, MapMode.READ_ONLY); + } + + /** + * Fully maps a file in to memory as per {@link + * FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} using the requested {@link + * MapMode}. + * + *

Files are mapped from offset 0 to its length. + * + *

This only works for files ≤ {@link Integer#MAX_VALUE} bytes. + * + * @param file the file to map + * @param mode the mode to use when mapping {@code file} + * @return a buffer reflecting {@code file} + * @throws FileNotFoundException if the {@code file} does not exist + * @throws IOException if an I/O error occurs + * @see FileChannel#map(MapMode, long, long) + * @since 2.0 + */ + public static MappedByteBuffer map(File file, MapMode mode) throws IOException { + return mapInternal(file, mode, -1); + } + + /** + * Maps a file in to memory as per {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, + * long, long)} using the requested {@link MapMode}. + * + *

Files are mapped from offset 0 to {@code size}. + * + *

If the mode is {@link MapMode#READ_WRITE} and the file does not exist, it will be created + * with the requested {@code size}. Thus this method is useful for creating memory mapped files + * which do not yet exist. + * + *

This only works for files ≤ {@link Integer#MAX_VALUE} bytes. + * + * @param file the file to map + * @param mode the mode to use when mapping {@code file} + * @return a buffer reflecting {@code file} + * @throws IOException if an I/O error occurs + * @see FileChannel#map(MapMode, long, long) + * @since 2.0 + */ + public static MappedByteBuffer map(File file, MapMode mode, long size) throws IOException { + checkArgument(size >= 0, "size (%s) may not be negative", size); + return mapInternal(file, mode, size); + } + + private static MappedByteBuffer mapInternal(File file, MapMode mode, long size) + throws IOException { + checkNotNull(file); + checkNotNull(mode); + + Closer closer = Closer.create(); + try { + RandomAccessFile raf = + closer.register(new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw")); + FileChannel channel = closer.register(raf.getChannel()); + return channel.map(mode, 0, size == -1 ? channel.size() : size); + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + /** + * Returns the lexically cleaned form of the path name, usually (but not always) equivalent + * to the original. The following heuristics are used: + * + *

    + *
  • empty string becomes . + *
  • . stays as . + *
  • fold out ./ + *
  • fold out ../ when possible + *
  • collapse multiple slashes + *
  • delete trailing slashes (unless the path is just "/") + *
+ * + *

These heuristics do not always match the behavior of the filesystem. In particular, consider + * the path {@code a/../b}, which {@code simplifyPath} will change to {@code b}. If {@code a} is a + * symlink to {@code x}, {@code a/../b} may refer to a sibling of {@code x}, rather than the + * sibling of {@code a} referred to by {@code b}. + * + * @since 11.0 + */ + public static String simplifyPath(String pathname) { + checkNotNull(pathname); + if (pathname.length() == 0) { + return "."; + } + + // split the path apart + Iterable components = Splitter.on('/').omitEmptyStrings().split(pathname); + List path = new ArrayList<>(); + + // resolve ., .., and // + for (String component : components) { + switch (component) { + case ".": + continue; + case "..": + if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) { + path.remove(path.size() - 1); + } else { + path.add(".."); + } + break; + default: + path.add(component); + break; + } + } + + // put it back together + String result = Joiner.on('/').join(path); + if (pathname.charAt(0) == '/') { + result = "/" + result; + } + + while (result.startsWith("/../")) { + result = result.substring(3); + } + if (result.equals("/..")) { + result = "/"; + } else if ("".equals(result)) { + result = "."; + } + + return result; + } + + /** + * Returns the file extension for + * the given file name, or the empty string if the file has no extension. The result does not + * include the '{@code .}'. + * + *

Note: This method simply returns everything after the last '{@code .}' in the file's + * name as determined by {@link File#getName}. It does not account for any filesystem-specific + * behavior that the {@link File} API does not already account for. For example, on NTFS it will + * report {@code "txt"} as the extension for the filename {@code "foo.exe:.txt"} even though NTFS + * will drop the {@code ":.txt"} part of the name when the file is actually created on the + * filesystem due to NTFS's Alternate Data Streams. + * + * @since 11.0 + */ + public static String getFileExtension(String fullName) { + checkNotNull(fullName); + String fileName = new File(fullName).getName(); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); + } + + /** + * Returns the file name without its file extension or path. This is + * similar to the {@code basename} unix command. The result does not include the '{@code .}'. + * + * @param file The name of the file to trim the extension from. This can be either a fully + * qualified file name (including a path) or just a file name. + * @return The file name without its path or extension. + * @since 14.0 + */ + public static String getNameWithoutExtension(String file) { + checkNotNull(file); + String fileName = new File(file).getName(); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); + } + + /** + * Returns a {@link Traverser} instance for the file and directory tree. The returned traverser + * starts from a {@link File} and will return all files and directories it encounters. + * + *

Warning: {@code File} provides no support for symbolic links, and as such there is no + * way to ensure that a symbolic link to a directory is not followed when traversing the tree. In + * this case, iterables created by this traverser could contain files that are outside of the + * given directory or even be infinite if there is a symbolic link loop. + * + *

If available, consider using {@link MoreFiles#fileTraverser()} instead. It behaves the same + * except that it doesn't follow symbolic links and returns {@code Path} instances. + * + *

If the {@link File} passed to one of the {@link Traverser} methods does not exist or is not + * a directory, no exception will be thrown and the returned {@link Iterable} will contain a + * single element: that file. + * + *

Example: {@code Files.fileTraverser().depthFirstPreOrder(new File("/"))} may return files + * with the following paths: {@code ["/", "/etc", "/etc/config.txt", "/etc/fonts", "/home", + * "/home/alice", ...]} + * + * @since 23.5 + */ + public static Traverser fileTraverser() { + return Traverser.forTree(FILE_TREE); + } + + private static final SuccessorsFunction FILE_TREE = + new SuccessorsFunction() { + @Override + public Iterable successors(File file) { + // check isDirectory() just because it may be faster than listFiles() on a non-directory + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + return Collections.unmodifiableList(Arrays.asList(files)); + } + } + + return ImmutableList.of(); + } + }; + + /** + * Returns a predicate that returns the result of {@link File#isDirectory} on input files. + * + * @since 15.0 + */ + public static Predicate isDirectory() { + return FilePredicate.IS_DIRECTORY; + } + + /** + * Returns a predicate that returns the result of {@link File#isFile} on input files. + * + * @since 15.0 + */ + public static Predicate isFile() { + return FilePredicate.IS_FILE; + } + + private enum FilePredicate implements Predicate { + IS_DIRECTORY { + @Override + public boolean apply(File file) { + return file.isDirectory(); + } + + @Override + public String toString() { + return "Files.isDirectory()"; + } + }, + + IS_FILE { + @Override + public boolean apply(File file) { + return file.isFile(); + } + + @Override + public String toString() { + return "Files.isFile()"; + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Flushables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Flushables.java new file mode 100644 index 0000000000000..2e7bd4a776cae --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Flushables.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Flushable; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility methods for working with {@link Flushable} objects. + * + * @author Michael Lancaster + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Flushables { + private static final Logger logger = Logger.getLogger(Flushables.class.getName()); + + private Flushables() {} + + /** + * Flush a {@link Flushable}, with control over whether an {@code IOException} may be thrown. + * + *

If {@code swallowIOException} is true, then we don't rethrow {@code IOException}, but merely + * log it. + * + * @param flushable the {@code Flushable} object to be flushed. + * @param swallowIOException if true, don't propagate IO exceptions thrown by the {@code flush} + * method + * @throws IOException if {@code swallowIOException} is false and {@link Flushable#flush} throws + * an {@code IOException}. + * @see Closeables#close + */ + public static void flush(Flushable flushable, boolean swallowIOException) throws IOException { + try { + flushable.flush(); + } catch (IOException e) { + if (swallowIOException) { + logger.log(Level.WARNING, "IOException thrown while flushing Flushable.", e); + } else { + throw e; + } + } + } + + /** + * Equivalent to calling {@code flush(flushable, true)}, but with no {@code IOException} in the + * signature. + * + * @param flushable the {@code Flushable} object to be flushed. + */ + @Beta + public static void flushQuietly(Flushable flushable) { + try { + flush(flushable, true); + } catch (IOException e) { + logger.log(Level.SEVERE, "IOException should not have been thrown.", e); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/IgnoreJRERequirement.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/IgnoreJRERequirement.java new file mode 100644 index 0000000000000..8253ed97e35d0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/IgnoreJRERequirement.java @@ -0,0 +1,35 @@ +/* + * 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.iotdb.google.common.io; + +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@ElementTypesAreNonnullByDefault +@interface IgnoreJRERequirement {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/InsecureRecursiveDeleteException.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/InsecureRecursiveDeleteException.java new file mode 100644 index 0000000000000..7377a5098a9ca --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/InsecureRecursiveDeleteException.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.nio.file.FileSystemException; +import java.nio.file.SecureDirectoryStream; + +/** + * Exception indicating that a recursive delete can't be performed because the file system does not + * have the support necessary to guarantee that it is not vulnerable to race conditions that would + * allow it to delete files and directories outside of the directory being deleted (i.e., {@link + * SecureDirectoryStream} is not supported). + * + *

{@link RecursiveDeleteOption#ALLOW_INSECURE} can be used to force the recursive delete method + * to proceed anyway. + * + * @since 21.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +// java.nio.file +@ElementTypesAreNonnullByDefault +public final class InsecureRecursiveDeleteException extends FileSystemException { + + public InsecureRecursiveDeleteException(String file) { + super(file, null, "unable to guarantee security of recursive delete"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Java8Compatibility.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Java8Compatibility.java new file mode 100644 index 0000000000000..1fd5cedb2af58 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Java8Compatibility.java @@ -0,0 +1,60 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.nio.Buffer; + +/** + * Wrappers around {@link Buffer} methods that are covariantly overridden in Java 9+. See + * https://github.com/google/guava/issues/3990 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class Java8Compatibility { + static void clear(Buffer b) { + b.clear(); + } + + static void flip(Buffer b) { + b.flip(); + } + + static void limit(Buffer b, int limit) { + b.limit(limit); + } + + static void mark(Buffer b) { + b.mark(); + } + + static void position(Buffer b, int position) { + b.position(position); + } + + static void reset(Buffer b) { + b.reset(); + } + + private Java8Compatibility() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineBuffer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineBuffer.java new file mode 100644 index 0000000000000..40d6efa79df0a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineBuffer.java @@ -0,0 +1,123 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; + +/** + * Package-protected abstract class that implements the line reading algorithm used by {@link + * LineReader}. Line separators are per {@link java.io.BufferedReader}: line feed, carriage return, + * or carriage return followed immediately by a linefeed. + * + *

Subclasses must implement {@link #handleLine}, call {@link #add} to pass character data, and + * call {@link #finish} at the end of stream. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class LineBuffer { + /** Holds partial line contents. */ + private StringBuilder line = new StringBuilder(); + + /** Whether a line ending with a CR is pending processing. */ + private boolean sawReturn; + + /** + * Process additional characters from the stream. When a line separator is found the contents of + * the line and the line separator itself are passed to the abstract {@link #handleLine} method. + * + * @param cbuf the character buffer to process + * @param off the offset into the buffer + * @param len the number of characters to process + * @throws IOException if an I/O error occurs + * @see #finish + */ + protected void add(char[] cbuf, int off, int len) throws IOException { + int pos = off; + if (sawReturn && len > 0) { + // Last call to add ended with a CR; we can handle the line now. + if (finishLine(cbuf[pos] == '\n')) { + pos++; + } + } + + int start = pos; + for (int end = off + len; pos < end; pos++) { + switch (cbuf[pos]) { + case '\r': + line.append(cbuf, start, pos - start); + sawReturn = true; + if (pos + 1 < end) { + if (finishLine(cbuf[pos + 1] == '\n')) { + pos++; + } + } + start = pos + 1; + break; + + case '\n': + line.append(cbuf, start, pos - start); + finishLine(true); + start = pos + 1; + break; + + default: + // do nothing + } + } + line.append(cbuf, start, off + len - start); + } + + /** Called when a line is complete. */ + private boolean finishLine(boolean sawNewline) throws IOException { + String separator = sawReturn ? (sawNewline ? "\r\n" : "\r") : (sawNewline ? "\n" : ""); + handleLine(line.toString(), separator); + line = new StringBuilder(); + sawReturn = false; + return sawNewline; + } + + /** + * Subclasses must call this method after finishing character processing, in order to ensure that + * any unterminated line in the buffer is passed to {@link #handleLine}. + * + * @throws IOException if an I/O error occurs + */ + protected void finish() throws IOException { + if (sawReturn || line.length() > 0) { + finishLine(false); + } + } + + /** + * Called for each line found in the character data passed to {@link #add}. + * + * @param line a line of text (possibly empty), without any line separators + * @param end the line separator; one of {@code "\r"}, {@code "\n"}, {@code "\r\n"}, or {@code ""} + * @throws IOException if an I/O error occurs + */ + protected abstract void handleLine(String line, String end) throws IOException; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineProcessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineProcessor.java new file mode 100644 index 0000000000000..758b07d0151e5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineProcessor.java @@ -0,0 +1,53 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; + +/** + * A callback to be used with the streaming {@code readLines} methods. + * + *

{@link #processLine} will be called for each line that is read, and should return {@code + * false} when you want to stop processing. + * + * @author Miles Barr + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface LineProcessor { + + /** + * This method will be called once for each line. + * + * @param line the line read from the input, without delimiter + * @return true to continue processing, false to stop + */ + // some uses know that their processor never returns false + boolean processLine(String line) throws IOException; + + /** Return the result of processing all the lines. */ + @ParametricNullness + T getResult(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineReader.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineReader.java new file mode 100644 index 0000000000000..533a6c7ec6868 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LineReader.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; +import java.util.ArrayDeque; +import java.util.Queue; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.io.CharStreams.createBuffer; + +/** + * A class for reading lines of text. Provides the same functionality as {@link + * java.io.BufferedReader#readLine()} but for all {@link Readable} objects, not just instances of + * {@link Reader}. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class LineReader { + private final Readable readable; + private final Reader reader; + private final CharBuffer cbuf = createBuffer(); + private final char[] buf = cbuf.array(); + + private final Queue lines = new ArrayDeque<>(); + private final LineBuffer lineBuf = + new LineBuffer() { + @Override + protected void handleLine(String line, String end) { + lines.add(line); + } + }; + + /** Creates a new instance that will read lines from the given {@code Readable} object. */ + public LineReader(Readable readable) { + this.readable = checkNotNull(readable); + this.reader = (readable instanceof Reader) ? (Reader) readable : null; + } + + /** + * Reads a line of text. A line is considered to be terminated by any one of a line feed ({@code + * '\n'}), a carriage return ({@code '\r'}), or a carriage return followed immediately by a + * linefeed ({@code "\r\n"}). + * + * @return a {@code String} containing the contents of the line, not including any + * line-termination characters, or {@code null} if the end of the stream has been reached. + * @throws IOException if an I/O error occurs + */ + // to skip a line + public String readLine() throws IOException { + while (lines.peek() == null) { + Java8Compatibility.clear(cbuf); + // The default implementation of Reader#read(CharBuffer) allocates a + // temporary char[], so we call Reader#read(char[], int, int) instead. + int read = (reader != null) ? reader.read(buf, 0, buf.length) : readable.read(cbuf); + if (read == -1) { + lineBuf.finish(); + break; + } + lineBuf.add(buf, 0, read); + } + return lines.poll(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LittleEndianDataInputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LittleEndianDataInputStream.java new file mode 100644 index 0000000000000..272a0dc6f645e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LittleEndianDataInputStream.java @@ -0,0 +1,243 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.primitives.Ints; +import org.apache.iotdb.google.common.primitives.Longs; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * An implementation of {@link DataInput} that uses little-endian byte ordering for reading {@code + * short}, {@code int}, {@code float}, {@code double}, and {@code long} values. + * + *

Note: This class intentionally violates the specification of its supertype {@code + * DataInput}, which explicitly requires big-endian byte order. + * + * @author Chris Nokleberg + * @author Keith Bottner + * @since 8.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class LittleEndianDataInputStream extends FilterInputStream implements DataInput { + + /** + * Creates a {@code LittleEndianDataInputStream} that wraps the given stream. + * + * @param in the stream to delegate to + */ + public LittleEndianDataInputStream(InputStream in) { + super(Preconditions.checkNotNull(in)); + } + + /** This method will throw an {@link UnsupportedOperationException}. */ + // to skip a line + @Override + public String readLine() { + throw new UnsupportedOperationException("readLine is not supported"); + } + + @Override + public void readFully(byte[] b) throws IOException { + ByteStreams.readFully(this, b); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + ByteStreams.readFully(this, b, off, len); + } + + @Override + public int skipBytes(int n) throws IOException { + return (int) in.skip(n); + } + + // to skip a byte + @Override + public int readUnsignedByte() throws IOException { + int b1 = in.read(); + if (0 > b1) { + throw new EOFException(); + } + + return b1; + } + + /** + * Reads an unsigned {@code short} as specified by {@link DataInputStream#readUnsignedShort()}, + * except using little-endian byte order. + * + * @return the next two bytes of the input stream, interpreted as an unsigned 16-bit integer in + * little-endian byte order + * @throws IOException if an I/O error occurs + */ + // to skip some bytes + @Override + public int readUnsignedShort() throws IOException { + byte b1 = readAndCheckByte(); + byte b2 = readAndCheckByte(); + + return Ints.fromBytes((byte) 0, (byte) 0, b2, b1); + } + + /** + * Reads an integer as specified by {@link DataInputStream#readInt()}, except using little-endian + * byte order. + * + * @return the next four bytes of the input stream, interpreted as an {@code int} in little-endian + * byte order + * @throws IOException if an I/O error occurs + */ + // to skip some bytes + @Override + public int readInt() throws IOException { + byte b1 = readAndCheckByte(); + byte b2 = readAndCheckByte(); + byte b3 = readAndCheckByte(); + byte b4 = readAndCheckByte(); + + return Ints.fromBytes(b4, b3, b2, b1); + } + + /** + * Reads a {@code long} as specified by {@link DataInputStream#readLong()}, except using + * little-endian byte order. + * + * @return the next eight bytes of the input stream, interpreted as a {@code long} in + * little-endian byte order + * @throws IOException if an I/O error occurs + */ + // to skip some bytes + @Override + public long readLong() throws IOException { + byte b1 = readAndCheckByte(); + byte b2 = readAndCheckByte(); + byte b3 = readAndCheckByte(); + byte b4 = readAndCheckByte(); + byte b5 = readAndCheckByte(); + byte b6 = readAndCheckByte(); + byte b7 = readAndCheckByte(); + byte b8 = readAndCheckByte(); + + return Longs.fromBytes(b8, b7, b6, b5, b4, b3, b2, b1); + } + + /** + * Reads a {@code float} as specified by {@link DataInputStream#readFloat()}, except using + * little-endian byte order. + * + * @return the next four bytes of the input stream, interpreted as a {@code float} in + * little-endian byte order + * @throws IOException if an I/O error occurs + */ + // to skip some bytes + @Override + public float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } + + /** + * Reads a {@code double} as specified by {@link DataInputStream#readDouble()}, except using + * little-endian byte order. + * + * @return the next eight bytes of the input stream, interpreted as a {@code double} in + * little-endian byte order + * @throws IOException if an I/O error occurs + */ + // to skip some bytes + @Override + public double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } + + // to skip a field + @Override + public String readUTF() throws IOException { + return new DataInputStream(in).readUTF(); + } + + /** + * Reads a {@code short} as specified by {@link DataInputStream#readShort()}, except using + * little-endian byte order. + * + * @return the next two bytes of the input stream, interpreted as a {@code short} in little-endian + * byte order. + * @throws IOException if an I/O error occurs. + */ + // to skip some bytes + @Override + public short readShort() throws IOException { + return (short) readUnsignedShort(); + } + + /** + * Reads a char as specified by {@link DataInputStream#readChar()}, except using little-endian + * byte order. + * + * @return the next two bytes of the input stream, interpreted as a {@code char} in little-endian + * byte order + * @throws IOException if an I/O error occurs + */ + // to skip some bytes + @Override + public char readChar() throws IOException { + return (char) readUnsignedShort(); + } + + // to skip a byte + @Override + public byte readByte() throws IOException { + return (byte) readUnsignedByte(); + } + + // to skip a byte + @Override + public boolean readBoolean() throws IOException { + return readUnsignedByte() != 0; + } + + /** + * Reads a byte from the input stream checking that the end of file (EOF) has not been + * encountered. + * + * @return byte read from input + * @throws IOException if an error is encountered while reading + * @throws EOFException if the end of file (EOF) is encountered. + */ + private byte readAndCheckByte() throws IOException, EOFException { + int b1 = in.read(); + + if (-1 == b1) { + throw new EOFException(); + } + + return (byte) b1; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LittleEndianDataOutputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LittleEndianDataOutputStream.java new file mode 100644 index 0000000000000..2ad043d0033bd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/LittleEndianDataOutputStream.java @@ -0,0 +1,180 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.primitives.Longs; + +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * An implementation of {@link DataOutput} that uses little-endian byte ordering for writing {@code + * char}, {@code short}, {@code int}, {@code float}, {@code double}, and {@code long} values. + * + *

Note: This class intentionally violates the specification of its supertype {@code + * DataOutput}, which explicitly requires big-endian byte order. + * + * @author Chris Nokleberg + * @author Keith Bottner + * @since 8.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput { + + /** + * Creates a {@code LittleEndianDataOutputStream} that wraps the given stream. + * + * @param out the stream to delegate to + */ + public LittleEndianDataOutputStream(OutputStream out) { + super(new DataOutputStream(Preconditions.checkNotNull(out))); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + // Override slow FilterOutputStream impl + out.write(b, off, len); + } + + @Override + public void writeBoolean(boolean v) throws IOException { + ((DataOutputStream) out).writeBoolean(v); + } + + @Override + public void writeByte(int v) throws IOException { + ((DataOutputStream) out).writeByte(v); + } + + /** + * @deprecated The semantics of {@code writeBytes(String s)} are considered dangerous. Please use + * {@link #writeUTF(String s)}, {@link #writeChars(String s)} or another write method instead. + */ + @Deprecated + @Override + public void writeBytes(String s) throws IOException { + ((DataOutputStream) out).writeBytes(s); + } + + /** + * Writes a char as specified by {@link DataOutputStream#writeChar(int)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeChar(int v) throws IOException { + writeShort(v); + } + + /** + * Writes a {@code String} as specified by {@link DataOutputStream#writeChars(String)}, except + * each character is written using little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeChars(String s) throws IOException { + for (int i = 0; i < s.length(); i++) { + writeChar(s.charAt(i)); + } + } + + /** + * Writes a {@code double} as specified by {@link DataOutputStream#writeDouble(double)}, except + * using little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + + /** + * Writes a {@code float} as specified by {@link DataOutputStream#writeFloat(float)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + /** + * Writes an {@code int} as specified by {@link DataOutputStream#writeInt(int)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeInt(int v) throws IOException { + out.write(0xFF & v); + out.write(0xFF & (v >> 8)); + out.write(0xFF & (v >> 16)); + out.write(0xFF & (v >> 24)); + } + + /** + * Writes a {@code long} as specified by {@link DataOutputStream#writeLong(long)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeLong(long v) throws IOException { + byte[] bytes = Longs.toByteArray(Long.reverseBytes(v)); + write(bytes, 0, bytes.length); + } + + /** + * Writes a {@code short} as specified by {@link DataOutputStream#writeShort(int)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeShort(int v) throws IOException { + out.write(0xFF & v); + out.write(0xFF & (v >> 8)); + } + + @Override + public void writeUTF(String str) throws IOException { + ((DataOutputStream) out).writeUTF(str); + } + + // Overriding close() because FilterOutputStream's close() method pre-JDK8 has bad behavior: + // it silently ignores any exception thrown by flush(). Instead, just close the delegate stream. + // It should flush itself if necessary. + @Override + public void close() throws IOException { + out.close(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MoreFiles.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MoreFiles.java new file mode 100644 index 0000000000000..dd08f08aec113 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MoreFiles.java @@ -0,0 +1,852 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.graph.Traverser; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.Charset; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystemException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NoSuchFileException; +import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.SecureDirectoryStream; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Stream; + +import static java.nio.file.LinkOption.NOFOLLOW_LINKS; +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.Iterables.getOnlyElement; + +/** + * Static utilities for use with {@link Path} instances, intended to complement {@link Files}. + * + *

Many methods provided by Guava's {@code Files} class for {@link java.io.File} instances are + * now available via the JDK's {@link java.nio.file.Files} class for {@code Path} - check the JDK's + * class if a sibling method from {@code Files} appears to be missing from this class. + * + * @since 21.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +// java.nio.file +@ElementTypesAreNonnullByDefault +public final class MoreFiles { + + private MoreFiles() {} + + /** + * Returns a view of the given {@code path} as a {@link ByteSource}. + * + *

Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned source and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#READ READ} option. + */ + public static ByteSource asByteSource(Path path, OpenOption... options) { + return new PathByteSource(path, options); + } + + private static final class PathByteSource extends ByteSource { + + private static final LinkOption[] FOLLOW_LINKS = {}; + + private final Path path; + private final OpenOption[] options; + private final boolean followLinks; + + private PathByteSource(Path path, OpenOption... options) { + this.path = checkNotNull(path); + this.options = options.clone(); + this.followLinks = followLinks(this.options); + // TODO(cgdecker): validate the provided options... for example, just WRITE seems wrong + } + + private static boolean followLinks(OpenOption[] options) { + for (OpenOption option : options) { + if (option == NOFOLLOW_LINKS) { + return false; + } + } + return true; + } + + @Override + public InputStream openStream() throws IOException { + return Files.newInputStream(path, options); + } + + private BasicFileAttributes readAttributes() throws IOException { + return Files.readAttributes( + path, + BasicFileAttributes.class, + followLinks ? FOLLOW_LINKS : new LinkOption[] {NOFOLLOW_LINKS}); + } + + @Override + public Optional sizeIfKnown() { + BasicFileAttributes attrs; + try { + attrs = readAttributes(); + } catch (IOException e) { + // Failed to get attributes; we don't know the size. + return Optional.absent(); + } + + // Don't return a size for directories or symbolic links; their sizes are implementation + // specific and they can't be read as bytes using the read methods anyway. + if (attrs.isDirectory() || attrs.isSymbolicLink()) { + return Optional.absent(); + } + + return Optional.of(attrs.size()); + } + + @Override + public long size() throws IOException { + BasicFileAttributes attrs = readAttributes(); + + // Don't return a size for directories or symbolic links; their sizes are implementation + // specific and they can't be read as bytes using the read methods anyway. + if (attrs.isDirectory()) { + throw new IOException("can't read: is a directory"); + } else if (attrs.isSymbolicLink()) { + throw new IOException("can't read: is a symbolic link"); + } + + return attrs.size(); + } + + @Override + public byte[] read() throws IOException { + try (SeekableByteChannel channel = Files.newByteChannel(path, options)) { + return ByteStreams.toByteArray(Channels.newInputStream(channel), channel.size()); + } + } + + @Override + public CharSource asCharSource(Charset charset) { + if (options.length == 0) { + // If no OpenOptions were passed, delegate to Files.lines, which could have performance + // advantages. (If OpenOptions were passed we can't, because Files.lines doesn't have an + // overload taking OpenOptions, meaning we can't guarantee the same behavior w.r.t. things + // like following/not following symlinks.) + return new AsCharSource(charset) { + @SuppressWarnings("FilesLinesLeak") // the user needs to close it in this case + @Override + public Stream lines() throws IOException { + return Files.lines(path, charset); + } + }; + } + + return super.asCharSource(charset); + } + + @Override + public String toString() { + return "MoreFiles.asByteSource(" + path + ", " + Arrays.toString(options) + ")"; + } + } + + /** + * Returns a view of the given {@code path} as a {@link ByteSink}. + * + *

Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned sink and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#CREATE CREATE}, {@link + * StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} and {@link StandardOpenOption#WRITE + * WRITE} options. + */ + public static ByteSink asByteSink(Path path, OpenOption... options) { + return new PathByteSink(path, options); + } + + private static final class PathByteSink extends ByteSink { + + private final Path path; + private final OpenOption[] options; + + private PathByteSink(Path path, OpenOption... options) { + this.path = checkNotNull(path); + this.options = options.clone(); + // TODO(cgdecker): validate the provided options... for example, just READ seems wrong + } + + @Override + public OutputStream openStream() throws IOException { + return Files.newOutputStream(path, options); + } + + @Override + public String toString() { + return "MoreFiles.asByteSink(" + path + ", " + Arrays.toString(options) + ")"; + } + } + + /** + * Returns a view of the given {@code path} as a {@link CharSource} using the given {@code + * charset}. + * + *

Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned source and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#READ READ} option. + */ + public static CharSource asCharSource(Path path, Charset charset, OpenOption... options) { + return asByteSource(path, options).asCharSource(charset); + } + + /** + * Returns a view of the given {@code path} as a {@link CharSink} using the given {@code charset}. + * + *

Any {@linkplain OpenOption open options} provided are used when opening streams to the file + * and may affect the behavior of the returned sink and the streams it provides. See {@link + * StandardOpenOption} for the standard options that may be provided. Providing no options is + * equivalent to providing the {@link StandardOpenOption#CREATE CREATE}, {@link + * StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} and {@link StandardOpenOption#WRITE + * WRITE} options. + */ + public static CharSink asCharSink(Path path, Charset charset, OpenOption... options) { + return asByteSink(path, options).asCharSink(charset); + } + + /** + * Returns an immutable list of paths to the files contained in the given directory. + * + * @throws NoSuchFileException if the file does not exist (optional specific exception) + * @throws NotDirectoryException if the file could not be opened because it is not a directory + * (optional specific exception) + * @throws IOException if an I/O error occurs + */ + public static ImmutableList listFiles(Path dir) throws IOException { + try (DirectoryStream stream = Files.newDirectoryStream(dir)) { + return ImmutableList.copyOf(stream); + } catch (DirectoryIteratorException e) { + throw e.getCause(); + } + } + + /** + * Returns a {@link Traverser} instance for the file and directory tree. The returned traverser + * starts from a {@link Path} and will return all files and directories it encounters. + * + *

The returned traverser attempts to avoid following symbolic links to directories. However, + * the traverser cannot guarantee that it will not follow symbolic links to directories as it is + * possible for a directory to be replaced with a symbolic link between checking if the file is a + * directory and actually reading the contents of that directory. + * + *

If the {@link Path} passed to one of the traversal methods does not exist or is not a + * directory, no exception will be thrown and the returned {@link Iterable} will contain a single + * element: that path. + * + *

{@link DirectoryIteratorException} may be thrown when iterating {@link Iterable} instances + * created by this traverser if an {@link IOException} is thrown by a call to {@link + * #listFiles(Path)}. + * + *

Example: {@code MoreFiles.fileTraverser().depthFirstPreOrder(Paths.get("/"))} may return the + * following paths: {@code ["/", "/etc", "/etc/config.txt", "/etc/fonts", "/home", "/home/alice", + * ...]} + * + * @since 23.5 + */ + public static Traverser fileTraverser() { + return Traverser.forTree(MoreFiles::fileTreeChildren); + } + + private static Iterable fileTreeChildren(Path dir) { + if (Files.isDirectory(dir, NOFOLLOW_LINKS)) { + try { + return listFiles(dir); + } catch (IOException e) { + // the exception thrown when iterating a DirectoryStream if an I/O exception occurs + throw new DirectoryIteratorException(e); + } + } + return ImmutableList.of(); + } + + /** + * Returns a predicate that returns the result of {@link java.nio.file.Files#isDirectory(Path, + * LinkOption...)} on input paths with the given link options. + */ + public static Predicate isDirectory(LinkOption... options) { + final LinkOption[] optionsCopy = options.clone(); + return new Predicate() { + @Override + public boolean apply(Path input) { + return Files.isDirectory(input, optionsCopy); + } + + @Override + public String toString() { + return "MoreFiles.isDirectory(" + Arrays.toString(optionsCopy) + ")"; + } + }; + } + + /** Returns whether or not the file with the given name in the given dir is a directory. */ + private static boolean isDirectory( + SecureDirectoryStream dir, Path name, LinkOption... options) throws IOException { + return dir.getFileAttributeView(name, BasicFileAttributeView.class, options) + .readAttributes() + .isDirectory(); + } + + /** + * Returns a predicate that returns the result of {@link java.nio.file.Files#isRegularFile(Path, + * LinkOption...)} on input paths with the given link options. + */ + public static Predicate isRegularFile(LinkOption... options) { + final LinkOption[] optionsCopy = options.clone(); + return new Predicate() { + @Override + public boolean apply(Path input) { + return Files.isRegularFile(input, optionsCopy); + } + + @Override + public String toString() { + return "MoreFiles.isRegularFile(" + Arrays.toString(optionsCopy) + ")"; + } + }; + } + + /** + * Returns true if the files located by the given paths exist, are not directories, and contain + * the same bytes. + * + * @throws IOException if an I/O error occurs + * @since 22.0 + */ + public static boolean equal(Path path1, Path path2) throws IOException { + checkNotNull(path1); + checkNotNull(path2); + if (Files.isSameFile(path1, path2)) { + return true; + } + + /* + * Some operating systems may return zero as the length for files denoting system-dependent + * entities such as devices or pipes, in which case we must fall back on comparing the bytes + * directly. + */ + ByteSource source1 = asByteSource(path1); + ByteSource source2 = asByteSource(path2); + long len1 = source1.sizeIfKnown().or(0L); + long len2 = source2.sizeIfKnown().or(0L); + if (len1 != 0 && len2 != 0 && len1 != len2) { + return false; + } + return source1.contentEquals(source2); + } + + /** + * Like the unix command of the same name, creates an empty file or updates the last modified + * timestamp of the existing file at the given path to the current system time. + */ + @SuppressWarnings("GoodTime") // reading system time without TimeSource + public static void touch(Path path) throws IOException { + checkNotNull(path); + + try { + Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis())); + } catch (NoSuchFileException e) { + try { + Files.createFile(path); + } catch (FileAlreadyExistsException ignore) { + // The file didn't exist when we called setLastModifiedTime, but it did when we called + // createFile, so something else created the file in between. The end result is + // what we wanted: a new file that probably has its last modified time set to approximately + // now. Or it could have an arbitrary last modified time set by the creator, but that's no + // different than if another process set its last modified time to something else after we + // created it here. + } + } + } + + /** + * Creates any necessary but nonexistent parent directories of the specified path. Note that if + * this operation fails, it may have succeeded in creating some (but not all) of the necessary + * parent directories. The parent directory is created with the given {@code attrs}. + * + * @throws IOException if an I/O error occurs, or if any necessary but nonexistent parent + * directories of the specified file could not be created. + */ + public static void createParentDirectories(Path path, FileAttribute... attrs) + throws IOException { + // Interestingly, unlike File.getCanonicalFile(), Path/Files provides no way of getting the + // canonical (absolute, normalized, symlinks resolved, etc.) form of a path to a nonexistent + // file. getCanonicalFile() can at least get the canonical form of the part of the path which + // actually exists and then append the normalized remainder of the path to that. + Path normalizedAbsolutePath = path.toAbsolutePath().normalize(); + Path parent = normalizedAbsolutePath.getParent(); + if (parent == null) { + // The given directory is a filesystem root. All zero of its ancestors exist. This doesn't + // mean that the root itself exists -- consider x:\ on a Windows machine without such a + // drive -- or even that the caller can create it, but this method makes no such guarantees + // even for non-root files. + return; + } + + // Check if the parent is a directory first because createDirectories will fail if the parent + // exists and is a symlink to a directory... we'd like for this to succeed in that case. + // (I'm kind of surprised that createDirectories would fail in that case; doesn't seem like + // what you'd want to happen.) + if (!Files.isDirectory(parent)) { + Files.createDirectories(parent, attrs); + if (!Files.isDirectory(parent)) { + throw new IOException("Unable to create parent directories of " + path); + } + } + } + + /** + * Returns the file extension for + * the file at the given path, or the empty string if the file has no extension. The result does + * not include the '{@code .}'. + * + *

Note: This method simply returns everything after the last '{@code .}' in the file's + * name as determined by {@link Path#getFileName}. It does not account for any filesystem-specific + * behavior that the {@link Path} API does not already account for. For example, on NTFS it will + * report {@code "txt"} as the extension for the filename {@code "foo.exe:.txt"} even though NTFS + * will drop the {@code ":.txt"} part of the name when the file is actually created on the + * filesystem due to NTFS's Alternate Data Streams. + */ + public static String getFileExtension(Path path) { + Path name = path.getFileName(); + + // null for empty paths and root-only paths + if (name == null) { + return ""; + } + + String fileName = name.toString(); + int dotIndex = fileName.lastIndexOf('.'); + return dotIndex == -1 ? "" : fileName.substring(dotIndex + 1); + } + + /** + * Returns the file name without its file extension or path. This is + * similar to the {@code basename} unix command. The result does not include the '{@code .}'. + */ + public static String getNameWithoutExtension(Path path) { + Path name = path.getFileName(); + + // null for empty paths and root-only paths + if (name == null) { + return ""; + } + + String fileName = name.toString(); + int dotIndex = fileName.lastIndexOf('.'); + return dotIndex == -1 ? fileName : fileName.substring(0, dotIndex); + } + + /** + * Deletes the file or directory at the given {@code path} recursively. Deletes symbolic links, + * not their targets (subject to the caveat below). + * + *

If an I/O exception occurs attempting to read, open or delete any file under the given + * directory, this method skips that file and continues. All such exceptions are collected and, + * after attempting to delete all files, an {@code IOException} is thrown containing those + * exceptions as {@linkplain Throwable#getSuppressed() suppressed exceptions}. + * + *

Warning: Security of recursive deletes

+ * + *

On a file system that supports symbolic links and does not support {@link + * SecureDirectoryStream}, it is possible for a recursive delete to delete files and directories + * that are outside the directory being deleted. This can happen if, after checking that a + * file is a directory (and not a symbolic link), that directory is replaced by a symbolic link to + * an outside directory before the call that opens the directory to read its entries. + * + *

By default, this method throws {@link InsecureRecursiveDeleteException} if it can't + * guarantee the security of recursive deletes. If you wish to allow the recursive deletes anyway, + * pass {@link RecursiveDeleteOption#ALLOW_INSECURE} to this method to override that behavior. + * + * @throws NoSuchFileException if {@code path} does not exist (optional specific exception) + * @throws InsecureRecursiveDeleteException if the security of recursive deletes can't be + * guaranteed for the file system and {@link RecursiveDeleteOption#ALLOW_INSECURE} was not + * specified + * @throws IOException if {@code path} or any file in the subtree rooted at it can't be deleted + * for any reason + */ + public static void deleteRecursively(Path path, RecursiveDeleteOption... options) + throws IOException { + Path parentPath = getParentPath(path); + if (parentPath == null) { + throw new FileSystemException(path.toString(), null, "can't delete recursively"); + } + + Collection exceptions = null; // created lazily if needed + try { + boolean sdsSupported = false; + try (DirectoryStream parent = Files.newDirectoryStream(parentPath)) { + if (parent instanceof SecureDirectoryStream) { + sdsSupported = true; + exceptions = + deleteRecursivelySecure( + (SecureDirectoryStream) parent, + /* + * requireNonNull is safe because paths have file names when they have parents, + * and we checked for a parent at the beginning of the method. + */ + requireNonNull(path.getFileName())); + } + } + + if (!sdsSupported) { + checkAllowsInsecure(path, options); + exceptions = deleteRecursivelyInsecure(path); + } + } catch (IOException e) { + if (exceptions == null) { + throw e; + } else { + exceptions.add(e); + } + } + + if (exceptions != null) { + throwDeleteFailed(path, exceptions); + } + } + + /** + * Deletes all files within the directory at the given {@code path} {@linkplain #deleteRecursively + * recursively}. Does not delete the directory itself. Deletes symbolic links, not their targets + * (subject to the caveat below). If {@code path} itself is a symbolic link to a directory, that + * link is followed and the contents of the directory it targets are deleted. + * + *

If an I/O exception occurs attempting to read, open or delete any file under the given + * directory, this method skips that file and continues. All such exceptions are collected and, + * after attempting to delete all files, an {@code IOException} is thrown containing those + * exceptions as {@linkplain Throwable#getSuppressed() suppressed exceptions}. + * + *

Warning: Security of recursive deletes

+ * + *

On a file system that supports symbolic links and does not support {@link + * SecureDirectoryStream}, it is possible for a recursive delete to delete files and directories + * that are outside the directory being deleted. This can happen if, after checking that a + * file is a directory (and not a symbolic link), that directory is replaced by a symbolic link to + * an outside directory before the call that opens the directory to read its entries. + * + *

By default, this method throws {@link InsecureRecursiveDeleteException} if it can't + * guarantee the security of recursive deletes. If you wish to allow the recursive deletes anyway, + * pass {@link RecursiveDeleteOption#ALLOW_INSECURE} to this method to override that behavior. + * + * @throws NoSuchFileException if {@code path} does not exist (optional specific exception) + * @throws NotDirectoryException if the file at {@code path} is not a directory (optional + * specific exception) + * @throws InsecureRecursiveDeleteException if the security of recursive deletes can't be + * guaranteed for the file system and {@link RecursiveDeleteOption#ALLOW_INSECURE} was not + * specified + * @throws IOException if one or more files can't be deleted for any reason + */ + public static void deleteDirectoryContents(Path path, RecursiveDeleteOption... options) + throws IOException { + Collection exceptions = null; // created lazily if needed + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + if (stream instanceof SecureDirectoryStream) { + SecureDirectoryStream sds = (SecureDirectoryStream) stream; + exceptions = deleteDirectoryContentsSecure(sds); + } else { + checkAllowsInsecure(path, options); + exceptions = deleteDirectoryContentsInsecure(stream); + } + } catch (IOException e) { + if (exceptions == null) { + throw e; + } else { + exceptions.add(e); + } + } + + if (exceptions != null) { + throwDeleteFailed(path, exceptions); + } + } + + /** + * Secure recursive delete using {@code SecureDirectoryStream}. Returns a collection of exceptions + * that occurred or null if no exceptions were thrown. + */ + private static Collection deleteRecursivelySecure( + SecureDirectoryStream dir, Path path) { + Collection exceptions = null; + try { + if (isDirectory(dir, path, NOFOLLOW_LINKS)) { + try (SecureDirectoryStream childDir = dir.newDirectoryStream(path, NOFOLLOW_LINKS)) { + exceptions = deleteDirectoryContentsSecure(childDir); + } + + // If exceptions is not null, something went wrong trying to delete the contents of the + // directory, so we shouldn't try to delete the directory as it will probably fail. + if (exceptions == null) { + dir.deleteDirectory(path); + } + } else { + dir.deleteFile(path); + } + + return exceptions; + } catch (IOException e) { + return addException(exceptions, e); + } + } + + /** + * Secure method for deleting the contents of a directory using {@code SecureDirectoryStream}. + * Returns a collection of exceptions that occurred or null if no exceptions were thrown. + */ + private static Collection deleteDirectoryContentsSecure( + SecureDirectoryStream dir) { + Collection exceptions = null; + try { + for (Path path : dir) { + exceptions = concat(exceptions, deleteRecursivelySecure(dir, path.getFileName())); + } + + return exceptions; + } catch (DirectoryIteratorException e) { + return addException(exceptions, e.getCause()); + } + } + + /** + * Insecure recursive delete for file systems that don't support {@code SecureDirectoryStream}. + * Returns a collection of exceptions that occurred or null if no exceptions were thrown. + */ + private static Collection deleteRecursivelyInsecure(Path path) { + Collection exceptions = null; + try { + if (Files.isDirectory(path, NOFOLLOW_LINKS)) { + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + exceptions = deleteDirectoryContentsInsecure(stream); + } + } + + // If exceptions is not null, something went wrong trying to delete the contents of the + // directory, so we shouldn't try to delete the directory as it will probably fail. + if (exceptions == null) { + Files.delete(path); + } + + return exceptions; + } catch (IOException e) { + return addException(exceptions, e); + } + } + + /** + * Simple, insecure method for deleting the contents of a directory for file systems that don't + * support {@code SecureDirectoryStream}. Returns a collection of exceptions that occurred or null + * if no exceptions were thrown. + */ + private static Collection deleteDirectoryContentsInsecure( + DirectoryStream dir) { + Collection exceptions = null; + try { + for (Path entry : dir) { + exceptions = concat(exceptions, deleteRecursivelyInsecure(entry)); + } + + return exceptions; + } catch (DirectoryIteratorException e) { + return addException(exceptions, e.getCause()); + } + } + + /** + * Returns a path to the parent directory of the given path. If the path actually has a parent + * path, this is simple. Otherwise, we need to do some trickier things. Returns null if the path + * is a root or is the empty path. + */ + private static Path getParentPath(Path path) { + Path parent = path.getParent(); + + // Paths that have a parent: + if (parent != null) { + // "/foo" ("/") + // "foo/bar" ("foo") + // "C:\foo" ("C:\") + // "\foo" ("\" - current drive for process on Windows) + // "C:foo" ("C:" - working dir of drive C on Windows) + return parent; + } + + // Paths that don't have a parent: + if (path.getNameCount() == 0) { + // "/", "C:\", "\" (no parent) + // "" (undefined, though typically parent of working dir) + // "C:" (parent of working dir of drive C on Windows) + // + // For working dir paths ("" and "C:"), return null because: + // A) it's not specified that "" is the path to the working directory. + // B) if we're getting this path for recursive delete, it's typically not possible to + // delete the working dir with a relative path anyway, so it's ok to fail. + // C) if we're getting it for opening a new SecureDirectoryStream, there's no need to get + // the parent path anyway since we can safely open a DirectoryStream to the path without + // worrying about a symlink. + return null; + } else { + // "foo" (working dir) + return path.getFileSystem().getPath("."); + } + } + + /** Checks that the given options allow an insecure delete, throwing an exception if not. */ + private static void checkAllowsInsecure(Path path, RecursiveDeleteOption[] options) + throws InsecureRecursiveDeleteException { + if (!Arrays.asList(options).contains(RecursiveDeleteOption.ALLOW_INSECURE)) { + throw new InsecureRecursiveDeleteException(path.toString()); + } + } + + /** + * Adds the given exception to the given collection, creating the collection if it's null. Returns + * the collection. + */ + private static Collection addException( + Collection exceptions, IOException e) { + if (exceptions == null) { + exceptions = new ArrayList<>(); // don't need Set semantics + } + exceptions.add(e); + return exceptions; + } + + /** + * Concatenates the contents of the two given collections of exceptions. If either collection is + * null, the other collection is returned. Otherwise, the elements of {@code other} are added to + * {@code exceptions} and {@code exceptions} is returned. + */ + private static Collection concat( + Collection exceptions, Collection other) { + if (exceptions == null) { + return other; + } else if (other != null) { + exceptions.addAll(other); + } + return exceptions; + } + + /** + * Throws an exception indicating that one or more files couldn't be deleted when deleting {@code + * path} or its contents. + * + *

If there is only one exception in the collection, and it is a {@link NoSuchFileException} + * thrown because {@code path} itself didn't exist, then throws that exception. Otherwise, the + * thrown exception contains all the exceptions in the given collection as suppressed exceptions. + */ + private static void throwDeleteFailed(Path path, Collection exceptions) + throws FileSystemException { + NoSuchFileException pathNotFound = pathNotFound(path, exceptions); + if (pathNotFound != null) { + throw pathNotFound; + } + // TODO(cgdecker): Should there be a custom exception type for this? + // Also, should we try to include the Path of each file we may have failed to delete rather + // than just the exceptions that occurred? + FileSystemException deleteFailed = + new FileSystemException( + path.toString(), + null, + "failed to delete one or more files; see suppressed exceptions for details"); + for (IOException e : exceptions) { + deleteFailed.addSuppressed(e); + } + throw deleteFailed; + } + + private static NoSuchFileException pathNotFound(Path path, Collection exceptions) { + if (exceptions.size() != 1) { + return null; + } + IOException exception = getOnlyElement(exceptions); + if (!(exception instanceof NoSuchFileException)) { + return null; + } + NoSuchFileException noSuchFileException = (NoSuchFileException) exception; + String exceptionFile = noSuchFileException.getFile(); + if (exceptionFile == null) { + /* + * It's not clear whether this happens in practice, especially with the filesystem + * implementations that are built into java.nio. + */ + return null; + } + Path parentPath = getParentPath(path); + if (parentPath == null) { + /* + * This is probably impossible: + * + * - In deleteRecursively, we require the path argument to have a parent. + * + * - In deleteDirectoryContents, the path argument may have no parent. Fortunately, all the + * *other* paths we process will be descendants of that. That leaves only the original path + * argument for us to consider. And the only place we call pathNotFound is from + * throwDeleteFailed, and the other place that we call throwDeleteFailed inside + * deleteDirectoryContents is when an exception is thrown during the recursive steps. Any + * failure during the initial lookup of the path argument itself is rethrown directly. So + * any exception that we're seeing here is from a descendant, which naturally has a parent. + * I think. + * + * Still, if this can happen somehow (a weird filesystem implementation that lets callers + * change its working directly concurrently with a call to deleteDirectoryContents?), it makes + * more sense for us to fall back to a generic FileSystemException (by returning null here) + * than to dereference parentPath and end up producing NullPointerException. + */ + return null; + } + // requireNonNull is safe because paths have file names when they have parents. + Path pathResolvedFromParent = parentPath.resolve(requireNonNull(path.getFileName())); + if (exceptionFile.equals(pathResolvedFromParent.toString())) { + return noSuchFileException; + } + return null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MultiInputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MultiInputStream.java new file mode 100644 index 0000000000000..dbeb55be89e24 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MultiInputStream.java @@ -0,0 +1,127 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An {@link InputStream} that concatenates multiple substreams. At most one stream will be open at + * a time. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class MultiInputStream extends InputStream { + + private Iterator it; + private InputStream in; + + /** + * Creates a new instance. + * + * @param it an iterator of I/O suppliers that will provide each substream + */ + public MultiInputStream(Iterator it) throws IOException { + this.it = checkNotNull(it); + advance(); + } + + @Override + public void close() throws IOException { + if (in != null) { + try { + in.close(); + } finally { + in = null; + } + } + } + + /** Closes the current input stream and opens the next one, if any. */ + private void advance() throws IOException { + close(); + if (it.hasNext()) { + in = it.next().openStream(); + } + } + + @Override + public int available() throws IOException { + if (in == null) { + return 0; + } + return in.available(); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public int read() throws IOException { + while (in != null) { + int result = in.read(); + if (result != -1) { + return result; + } + advance(); + } + return -1; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + checkNotNull(b); + while (in != null) { + int result = in.read(b, off, len); + if (result != -1) { + return result; + } + advance(); + } + return -1; + } + + @Override + public long skip(long n) throws IOException { + if (in == null || n <= 0) { + return 0; + } + long result = in.skip(n); + if (result != 0) { + return result; + } + if (read() == -1) { + return 0; + } + return 1 + in.skip(n - 1); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MultiReader.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MultiReader.java new file mode 100644 index 0000000000000..a1def480c6c29 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/MultiReader.java @@ -0,0 +1,102 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.IOException; +import java.io.Reader; +import java.util.Iterator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link Reader} that concatenates multiple readers. + * + * @author Bin Zhu + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +class MultiReader extends Reader { + private final Iterator it; + private Reader current; + + MultiReader(Iterator readers) throws IOException { + this.it = readers; + advance(); + } + + /** Closes the current reader and opens the next one, if any. */ + private void advance() throws IOException { + close(); + if (it.hasNext()) { + current = it.next().openStream(); + } + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + checkNotNull(cbuf); + if (current == null) { + return -1; + } + int result = current.read(cbuf, off, len); + if (result == -1) { + advance(); + return read(cbuf, off, len); + } + return result; + } + + @Override + public long skip(long n) throws IOException { + Preconditions.checkArgument(n >= 0, "n is negative"); + if (n > 0) { + while (current != null) { + long result = current.skip(n); + if (result > 0) { + return result; + } + advance(); + } + } + return 0; + } + + @Override + public boolean ready() throws IOException { + return (current != null) && current.ready(); + } + + @Override + public void close() throws IOException { + if (current != null) { + try { + current.close(); + } finally { + current = null; + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ParametricNullness.java new file mode 100644 index 0000000000000..198d5b3bc81fd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/PatternFilenameFilter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/PatternFilenameFilter.java new file mode 100644 index 0000000000000..3907db70944b5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/PatternFilenameFilter.java @@ -0,0 +1,81 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * File name filter that only accepts files matching a regular expression. This class is thread-safe + * and immutable. + * + * @author Apple Chow + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class PatternFilenameFilter implements FilenameFilter { + + private final Pattern pattern; + + /** + * Constructs a pattern file name filter object. + * + * @param patternStr the pattern string on which to filter file names + * @throws PatternSyntaxException if pattern compilation fails (runtime) + */ + public PatternFilenameFilter(String patternStr) { + this(Pattern.compile(patternStr)); + } + + /** + * Constructs a pattern file name filter object. + * + * @param pattern the pattern on which to filter file names + */ + public PatternFilenameFilter(Pattern pattern) { + this.pattern = Preconditions.checkNotNull(pattern); + } + + /* + * Our implementation works fine with a null `dir`. However, there's nothing in the documentation + * of the supertype that suggests that implementations are expected to tolerate null. That said, I + * see calls in Google code that pass a null `dir` to a FilenameFilter.... So let's declare the + * parameter as non-nullable (since passing null to a FilenameFilter is unsafe in general), but if + * someone still manages to pass null, let's continue to have the method work. + * + * (PatternFilenameFilter is of course one of those classes that shouldn't be a publicly visible + * class to begin with but rather something returned from a static factory method whose declared + * return type is plain FilenameFilter. If we made such a change, then the annotation we choose + * here would have no significance to end users, who would be forced to conform to the signature + * used in FilenameFilter.) + */ + @Override + public boolean accept(File dir, String fileName) { + return pattern.matcher(fileName).matches(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ReaderInputStream.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ReaderInputStream.java new file mode 100644 index 0000000000000..7eb236d1d684d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/ReaderInputStream.java @@ -0,0 +1,266 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.UnsignedBytes; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.util.Arrays; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * An {@link InputStream} that converts characters from a {@link Reader} into bytes using an + * arbitrary Charset. + * + *

This is an alternative to copying the data to an {@code OutputStream} via a {@code Writer}, + * which is necessarily blocking. By implementing an {@code InputStream} it allows consumers to + * "pull" as much data as they can handle, which is more convenient when dealing with flow + * controlled, async APIs. + * + * @author Chris Nokleberg + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class ReaderInputStream extends InputStream { + private final Reader reader; + private final CharsetEncoder encoder; + private final byte[] singleByte = new byte[1]; + + /** + * charBuffer holds characters that have been read from the Reader but not encoded yet. The buffer + * is perpetually "flipped" (unencoded characters between position and limit). + */ + private CharBuffer charBuffer; + + /** + * byteBuffer holds encoded characters that have not yet been sent to the caller of the input + * stream. When encoding it is "unflipped" (encoded bytes between 0 and position) and when + * draining it is flipped (undrained bytes between position and limit). + */ + private ByteBuffer byteBuffer; + + /** Whether we've finished reading the reader. */ + private boolean endOfInput; + + /** Whether we're copying encoded bytes to the caller's buffer. */ + private boolean draining; + + /** Whether we've successfully flushed the encoder. */ + private boolean doneFlushing; + + /** + * Creates a new input stream that will encode the characters from {@code reader} into bytes using + * the given character set. Malformed input and unmappable characters will be replaced. + * + * @param reader input source + * @param charset character set used for encoding chars to bytes + * @param bufferSize size of internal input and output buffers + * @throws IllegalArgumentException if bufferSize is non-positive + */ + ReaderInputStream(Reader reader, Charset charset, int bufferSize) { + this( + reader, + charset + .newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE), + bufferSize); + } + + /** + * Creates a new input stream that will encode the characters from {@code reader} into bytes using + * the given character set encoder. + * + * @param reader input source + * @param encoder character set encoder used for encoding chars to bytes + * @param bufferSize size of internal input and output buffers + * @throws IllegalArgumentException if bufferSize is non-positive + */ + ReaderInputStream(Reader reader, CharsetEncoder encoder, int bufferSize) { + this.reader = checkNotNull(reader); + this.encoder = checkNotNull(encoder); + checkArgument(bufferSize > 0, "bufferSize must be positive: %s", bufferSize); + encoder.reset(); + + charBuffer = CharBuffer.allocate(bufferSize); + Java8Compatibility.flip(charBuffer); + + byteBuffer = ByteBuffer.allocate(bufferSize); + } + + @Override + public void close() throws IOException { + reader.close(); + } + + @Override + public int read() throws IOException { + return (read(singleByte) == 1) ? UnsignedBytes.toInt(singleByte[0]) : -1; + } + + // TODO(chrisn): Consider trying to encode/flush directly to the argument byte + // buffer when possible. + @Override + public int read(byte[] b, int off, int len) throws IOException { + // Obey InputStream contract. + checkPositionIndexes(off, off + len, b.length); + if (len == 0) { + return 0; + } + + // The rest of this method implements the process described by the CharsetEncoder javadoc. + int totalBytesRead = 0; + boolean doneEncoding = endOfInput; + + DRAINING: + while (true) { + // We stay in draining mode until there are no bytes left in the output buffer. Then we go + // back to encoding/flushing. + if (draining) { + totalBytesRead += drain(b, off + totalBytesRead, len - totalBytesRead); + if (totalBytesRead == len || doneFlushing) { + return (totalBytesRead > 0) ? totalBytesRead : -1; + } + draining = false; + Java8Compatibility.clear(byteBuffer); + } + + while (true) { + // We call encode until there is no more input. The last call to encode will have endOfInput + // == true. Then there is a final call to flush. + CoderResult result; + if (doneFlushing) { + result = CoderResult.UNDERFLOW; + } else if (doneEncoding) { + result = encoder.flush(byteBuffer); + } else { + result = encoder.encode(charBuffer, byteBuffer, endOfInput); + } + + if (result.isOverflow()) { + // Not enough room in output buffer--drain it, creating a bigger buffer if necessary. + startDraining(true); + continue DRAINING; + } else if (result.isUnderflow()) { + // If encoder underflows, it means either: + // a) the final flush() succeeded; next drain (then done) + // b) we encoded all of the input; next flush + // c) we ran of out input to encode; next read more input + if (doneEncoding) { // (a) + doneFlushing = true; + startDraining(false); + continue DRAINING; + } else if (endOfInput) { // (b) + doneEncoding = true; + } else { // (c) + readMoreChars(); + } + } else if (result.isError()) { + // Only reach here if a CharsetEncoder with non-REPLACE settings is used. + result.throwException(); + return 0; // Not called. + } + } + } + } + + /** Returns a new CharBuffer identical to buf, except twice the capacity. */ + private static CharBuffer grow(CharBuffer buf) { + char[] copy = Arrays.copyOf(buf.array(), buf.capacity() * 2); + CharBuffer bigger = CharBuffer.wrap(copy); + Java8Compatibility.position(bigger, buf.position()); + Java8Compatibility.limit(bigger, buf.limit()); + return bigger; + } + + /** Handle the case of underflow caused by needing more input characters. */ + private void readMoreChars() throws IOException { + // Possibilities: + // 1) array has space available on right-hand side (between limit and capacity) + // 2) array has space available on left-hand side (before position) + // 3) array has no space available + // + // In case 2 we shift the existing chars to the left, and in case 3 we create a bigger + // array, then they both become case 1. + + if (availableCapacity(charBuffer) == 0) { + if (charBuffer.position() > 0) { + // (2) There is room in the buffer. Move existing bytes to the beginning. + Java8Compatibility.flip(charBuffer.compact()); + } else { + // (3) Entire buffer is full, need bigger buffer. + charBuffer = grow(charBuffer); + } + } + + // (1) Read more characters into free space at end of array. + int limit = charBuffer.limit(); + int numChars = reader.read(charBuffer.array(), limit, availableCapacity(charBuffer)); + if (numChars == -1) { + endOfInput = true; + } else { + Java8Compatibility.limit(charBuffer, limit + numChars); + } + } + + /** Returns the number of elements between the limit and capacity. */ + private static int availableCapacity(Buffer buffer) { + return buffer.capacity() - buffer.limit(); + } + + /** + * Flips the buffer output buffer so we can start reading bytes from it. If we are starting to + * drain because there was overflow, and there aren't actually any characters to drain, then the + * overflow must be due to a small output buffer. + */ + private void startDraining(boolean overflow) { + Java8Compatibility.flip(byteBuffer); + if (overflow && byteBuffer.remaining() == 0) { + byteBuffer = ByteBuffer.allocate(byteBuffer.capacity() * 2); + } else { + draining = true; + } + } + + /** + * Copy as much of the byte buffer into the output array as possible, returning the (positive) + * number of characters copied. + */ + private int drain(byte[] b, int off, int len) { + int remaining = Math.min(len, byteBuffer.remaining()); + byteBuffer.get(b, off, remaining); + return remaining; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/RecursiveDeleteOption.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/RecursiveDeleteOption.java new file mode 100644 index 0000000000000..5cc99df95bbdc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/RecursiveDeleteOption.java @@ -0,0 +1,52 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.nio.file.SecureDirectoryStream; + +/** + * Options for use with recursive delete methods ({@link MoreFiles#deleteRecursively} and {@link + * MoreFiles#deleteDirectoryContents}). + * + * @since 21.0 + * @author Colin Decker + */ +@J2ktIncompatible +@GwtIncompatible +// java.nio.file +@ElementTypesAreNonnullByDefault +public enum RecursiveDeleteOption { + /** + * Specifies that the recursive delete should not throw an exception when it can't be guaranteed + * that it can be done securely, without vulnerability to race conditions (i.e. when the file + * system does not support {@link SecureDirectoryStream}). + * + *

Warning: On a file system that supports symbolic links, it is possible for an + * insecure recursive delete to delete files and directories that are outside the directory + * being deleted. This can happen if, after checking that a file is a directory (and not a + * symbolic link), that directory is deleted and replaced by a symbolic link to an outside + * directory before the call that opens the directory to read its entries. File systems that + * support {@code SecureDirectoryStream} do not have this vulnerability. + */ + ALLOW_INSECURE +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Resources.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Resources.java new file mode 100644 index 0000000000000..08e5a59738c18 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/Resources.java @@ -0,0 +1,217 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Charsets; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.collect.Lists; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.List; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Provides utility methods for working with resources in the classpath. Note that even though these + * methods use {@link URL} parameters, they are usually not appropriate for HTTP or other + * non-classpath resources. + * + * @author Chris Nokleberg + * @author Ben Yu + * @author Colin Decker + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Resources { + private Resources() {} + + /** + * Returns a {@link ByteSource} that reads from the given URL. + * + * @since 14.0 + */ + public static ByteSource asByteSource(URL url) { + return new UrlByteSource(url); + } + + /** A byte source that reads from a URL using {@link URL#openStream()}. */ + private static final class UrlByteSource extends ByteSource { + + private final URL url; + + private UrlByteSource(URL url) { + this.url = checkNotNull(url); + } + + @Override + public InputStream openStream() throws IOException { + return url.openStream(); + } + + @Override + public String toString() { + return "Resources.asByteSource(" + url + ")"; + } + } + + /** + * Returns a {@link CharSource} that reads from the given URL using the given character set. + * + * @since 14.0 + */ + public static CharSource asCharSource(URL url, Charset charset) { + return asByteSource(url).asCharSource(charset); + } + + /** + * Reads all bytes from a URL into a byte array. + * + * @param url the URL to read from + * @return a byte array containing all the bytes from the URL + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(URL url) throws IOException { + return asByteSource(url).read(); + } + + /** + * Reads all characters from a URL into a {@link String}, using the given character set. + * + * @param url the URL to read from + * @param charset the charset used to decode the input stream; see {@link Charsets} for helpful + * predefined constants + * @return a string containing all the characters from the URL + * @throws IOException if an I/O error occurs. + */ + public static String toString(URL url, Charset charset) throws IOException { + return asCharSource(url, charset).read(); + } + + /** + * Streams lines from a URL, stopping when our callback returns false, or we have read all of the + * lines. + * + * @param url the URL to read from + * @param charset the charset used to decode the input stream; see {@link Charsets} for helpful + * predefined constants + * @param callback the LineProcessor to use to handle the lines + * @return the output of processing the lines + * @throws IOException if an I/O error occurs + */ + // some processors won't return a useful result + @ParametricNullness + public static T readLines(URL url, Charset charset, LineProcessor callback) + throws IOException { + return asCharSource(url, charset).readLines(callback); + } + + /** + * Reads all of the lines from a URL. The lines do not include line-termination characters, but do + * include other leading and trailing whitespace. + * + *

This method returns a mutable {@code List}. For an {@code ImmutableList}, use {@code + * Resources.asCharSource(url, charset).readLines()}. + * + * @param url the URL to read from + * @param charset the charset used to decode the input stream; see {@link Charsets} for helpful + * predefined constants + * @return a mutable {@link List} containing all the lines + * @throws IOException if an I/O error occurs + */ + public static List readLines(URL url, Charset charset) throws IOException { + // don't use asCharSource(url, charset).readLines() because that returns + // an immutable list, which would change the behavior of this method + return readLines( + url, + charset, + new LineProcessor>() { + final List result = Lists.newArrayList(); + + @Override + public boolean processLine(String line) { + result.add(line); + return true; + } + + @Override + public List getResult() { + return result; + } + }); + } + + /** + * Copies all bytes from a URL to an output stream. + * + * @param from the URL to read from + * @param to the output stream + * @throws IOException if an I/O error occurs + */ + public static void copy(URL from, OutputStream to) throws IOException { + asByteSource(from).copyTo(to); + } + + /** + * Returns a {@code URL} pointing to {@code resourceName} if the resource is found using the + * {@linkplain Thread#getContextClassLoader() context class loader}. In simple environments, the + * context class loader will find resources from the class path. In environments where different + * threads can have different class loaders, for example app servers, the context class loader + * will typically have been set to an appropriate loader for the current thread. + * + *

In the unusual case where the context class loader is null, the class loader that loaded + * this class ({@code Resources}) will be used instead. + * + * @throws IllegalArgumentException if the resource is not found + */ + // being used to check if a resource exists + // TODO(cgdecker): maybe add a better way to check if a resource exists + // e.g. Optional tryGetResource or boolean resourceExists + public static URL getResource(String resourceName) { + ClassLoader loader = + MoreObjects.firstNonNull( + Thread.currentThread().getContextClassLoader(), Resources.class.getClassLoader()); + URL url = loader.getResource(resourceName); + checkArgument(url != null, "resource %s not found.", resourceName); + return url; + } + + /** + * Given a {@code resourceName} that is relative to {@code contextClass}, returns a {@code URL} + * pointing to the named resource. + * + * @throws IllegalArgumentException if the resource is not found + */ + // being used to check if a resource exists + public static URL getResource(Class contextClass, String resourceName) { + URL url = contextClass.getResource(resourceName); + checkArgument( + url != null, "resource %s relative to %s not found.", resourceName, contextClass.getName()); + return url; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/TempFileCreator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/TempFileCreator.java new file mode 100644 index 0000000000000..96767b22de4f1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/TempFileCreator.java @@ -0,0 +1,243 @@ +/* + * 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.iotdb.google.common.io; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ImmutableList; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Paths; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryPermission; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermissions; +import java.nio.file.attribute.UserPrincipal; +import java.util.EnumSet; +import java.util.Set; + +import static java.nio.file.attribute.AclEntryFlag.DIRECTORY_INHERIT; +import static java.nio.file.attribute.AclEntryFlag.FILE_INHERIT; +import static java.nio.file.attribute.AclEntryType.ALLOW; +import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute; +import static org.apache.iotdb.google.common.base.StandardSystemProperty.JAVA_IO_TMPDIR; +import static org.apache.iotdb.google.common.base.StandardSystemProperty.USER_NAME; + +/** + * Creates temporary files and directories whose permissions are restricted to the current user or, + * in the case of Android, the current app. If that is not possible (as is the case under the very + * old Android Ice Cream Sandwich release), then this class throws an exception instead of creating + * a file or directory that would be more accessible. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class TempFileCreator { + static final TempFileCreator INSTANCE = pickSecureCreator(); + + /** + * @throws IllegalStateException if the directory could not be created (to implement the contract + * of {@link Files#createTempDir()}, such as if the system does not support creating temporary + * directories securely + */ + abstract File createTempDir(); + + abstract File createTempFile(String prefix) throws IOException; + + private static TempFileCreator pickSecureCreator() { + try { + Class.forName("java.nio.file.Path"); + return new JavaNioCreator(); + } catch (ClassNotFoundException runningUnderAndroid) { + // Try another way. + } + + try { + int version = (int) Class.forName("android.os.Build$VERSION").getField("SDK_INT").get(null); + int jellyBean = + (int) Class.forName("android.os.Build$VERSION_CODES").getField("JELLY_BEAN").get(null); + /* + * I assume that this check can't fail because JELLY_BEAN will be present only if we're + * running under Jelly Bean or higher. But it seems safest to check. + */ + if (version < jellyBean) { + return new ThrowingCreator(); + } + + // Don't merge these catch() blocks, let alone use ReflectiveOperationException directly: + // b/65343391 + } catch (NoSuchFieldException e) { + // The JELLY_BEAN field doesn't exist because we're running on a version before Jelly Bean :) + return new ThrowingCreator(); + } catch (ClassNotFoundException e) { + // Should be impossible, but we want to return *something* so that class init succeeds. + return new ThrowingCreator(); + } catch (IllegalAccessException e) { + // ditto + return new ThrowingCreator(); + } + + // Android isolates apps' temporary directories since Jelly Bean: + // https://github.com/google/guava/issues/4011#issuecomment-770020802 + // So we can create files there with any permissions and still get security from the isolation. + return new JavaIoCreator(); + } + + @IgnoreJRERequirement // used only when Path is available + private static final class JavaNioCreator extends TempFileCreator { + @Override + File createTempDir() { + try { + return java.nio.file.Files.createTempDirectory( + Paths.get(JAVA_IO_TMPDIR.value()), /* prefix= */ null, directoryPermissions.get()) + .toFile(); + } catch (IOException e) { + throw new IllegalStateException("Failed to create directory", e); + } + } + + @Override + File createTempFile(String prefix) throws IOException { + return java.nio.file.Files.createTempFile( + Paths.get(JAVA_IO_TMPDIR.value()), + /* prefix= */ prefix, + /* suffix= */ null, + filePermissions.get()) + .toFile(); + } + + @IgnoreJRERequirement // see enclosing class (whose annotation Animal Sniffer ignores here...) + private interface PermissionSupplier { + FileAttribute get() throws IOException; + } + + private static final PermissionSupplier filePermissions; + private static final PermissionSupplier directoryPermissions; + + static { + Set views = FileSystems.getDefault().supportedFileAttributeViews(); + if (views.contains("posix")) { + filePermissions = () -> asFileAttribute(PosixFilePermissions.fromString("rw-------")); + directoryPermissions = () -> asFileAttribute(PosixFilePermissions.fromString("rwx------")); + } else if (views.contains("acl")) { + filePermissions = directoryPermissions = userPermissions(); + } else { + filePermissions = + directoryPermissions = + () -> { + throw new IOException("unrecognized FileSystem type " + FileSystems.getDefault()); + }; + } + } + + private static PermissionSupplier userPermissions() { + try { + UserPrincipal user = + FileSystems.getDefault() + .getUserPrincipalLookupService() + .lookupPrincipalByName(USER_NAME.value()); + ImmutableList acl = + ImmutableList.of( + AclEntry.newBuilder() + .setType(ALLOW) + .setPrincipal(user) + .setPermissions(EnumSet.allOf(AclEntryPermission.class)) + .setFlags(DIRECTORY_INHERIT, FILE_INHERIT) + .build()); + FileAttribute> attribute = + new FileAttribute>() { + @Override + public String name() { + return "acl:acl"; + } + + @Override + public ImmutableList value() { + return acl; + } + }; + return () -> attribute; + } catch (IOException e) { + // We throw a new exception each time so that the stack trace is right. + return () -> { + throw new IOException("Could not find user", e); + }; + } + } + } + + private static final class JavaIoCreator extends TempFileCreator { + @Override + File createTempDir() { + File baseDir = new File(JAVA_IO_TMPDIR.value()); + @SuppressWarnings("GoodTime") // reading system time without TimeSource + String baseName = System.currentTimeMillis() + "-"; + + for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { + File tempDir = new File(baseDir, baseName + counter); + if (tempDir.mkdir()) { + return tempDir; + } + } + throw new IllegalStateException( + "Failed to create directory within " + + TEMP_DIR_ATTEMPTS + + " attempts (tried " + + baseName + + "0 to " + + baseName + + (TEMP_DIR_ATTEMPTS - 1) + + ')'); + } + + @Override + File createTempFile(String prefix) throws IOException { + return File.createTempFile( + /* prefix= */ prefix, + /* suffix= */ null, + /* directory= */ null /* defaults to java.io.tmpdir */); + } + + /** Maximum loop count when creating temp directories. */ + private static final int TEMP_DIR_ATTEMPTS = 10000; + } + + private static final class ThrowingCreator extends TempFileCreator { + private static final String MESSAGE = + "Guava cannot securely create temporary files or directories under SDK versions before" + + " Jelly Bean. You can create one yourself, either in the insecure default directory" + + " or in a more secure directory, such as context.getCacheDir(). For more information," + + " see the Javadoc for Files.createTempDir()."; + + @Override + File createTempDir() { + throw new IllegalStateException(MESSAGE); + } + + @Override + File createTempFile(String prefix) throws IOException { + throw new IOException(MESSAGE); + } + } + + private TempFileCreator() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/package-info.java new file mode 100644 index 0000000000000..c73f1bdf02f1c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/io/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.io; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/BigDecimalMath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/BigDecimalMath.java new file mode 100644 index 0000000000000..b8805ec9dba8a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/BigDecimalMath.java @@ -0,0 +1,90 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * A class for arithmetic on {@link BigDecimal} that is not covered by its built-in methods. + * + * @author Louis Wasserman + * @since 30.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public class BigDecimalMath { + private BigDecimalMath() {} + + /** + * Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x} + * is precisely representable as a {@code double}, its {@code double} value will be returned; + * otherwise, the rounding will choose between the two nearest representable values with {@code + * mode}. + * + *

For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN}, + * infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not + * representable as a double, but {@code roundToDouble(BigDecimal.valueOf(2).pow(2000), HALF_UP)} + * will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}. + * + *

For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754 + * default rounding mode: if the two nearest representable values are equally near, the one with + * the least significant bit zero is chosen. (In such cases, both of the nearest representable + * values are even integers; this method returns the one that is a multiple of a greater power of + * two.) + * + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not precisely representable as a {@code double} + * @since 30.0 + */ + public static double roundToDouble(BigDecimal x, RoundingMode mode) { + return BigDecimalToDoubleRounder.INSTANCE.roundToDouble(x, mode); + } + + private static class BigDecimalToDoubleRounder extends ToDoubleRounder { + static final BigDecimalToDoubleRounder INSTANCE = new BigDecimalToDoubleRounder(); + + private BigDecimalToDoubleRounder() {} + + @Override + double roundToDoubleArbitrarily(BigDecimal bigDecimal) { + return bigDecimal.doubleValue(); + } + + @Override + int sign(BigDecimal bigDecimal) { + return bigDecimal.signum(); + } + + @Override + BigDecimal toX(double d, RoundingMode mode) { + return new BigDecimal(d); + } + + @Override + BigDecimal minus(BigDecimal a, BigDecimal b) { + return a.subtract(b); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/BigIntegerMath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/BigIntegerMath.java new file mode 100644 index 0000000000000..f7bbe7b2c1a05 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/BigIntegerMath.java @@ -0,0 +1,542 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; + +import static java.math.RoundingMode.CEILING; +import static java.math.RoundingMode.FLOOR; +import static java.math.RoundingMode.HALF_DOWN; +import static java.math.RoundingMode.HALF_EVEN; +import static java.math.RoundingMode.UNNECESSARY; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkNonNegative; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkPositive; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkRoundingUnnecessary; + +/** + * A class for arithmetic on values of type {@code BigInteger}. + * + *

The implementations of many methods in this class are based on material from Henry S. Warren, + * Jr.'s Hacker's Delight, (Addison Wesley, 2002). + * + *

Similar functionality for {@code int} and for {@code long} can be found in {@link IntMath} and + * {@link LongMath} respectively. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class BigIntegerMath { + /** + * Returns the smallest power of two greater than or equal to {@code x}. This is equivalent to + * {@code BigInteger.valueOf(2).pow(log2(x, CEILING))}. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @since 20.0 + */ + public static BigInteger ceilingPowerOfTwo(BigInteger x) { + return BigInteger.ZERO.setBit(log2(x, CEILING)); + } + + /** + * Returns the largest power of two less than or equal to {@code x}. This is equivalent to {@code + * BigInteger.valueOf(2).pow(log2(x, FLOOR))}. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @since 20.0 + */ + public static BigInteger floorPowerOfTwo(BigInteger x) { + return BigInteger.ZERO.setBit(log2(x, FLOOR)); + } + + /** Returns {@code true} if {@code x} represents a power of two. */ + public static boolean isPowerOfTwo(BigInteger x) { + checkNotNull(x); + return x.signum() > 0 && x.getLowestSetBit() == x.bitLength() - 1; + } + + /** + * Returns the base-2 logarithm of {@code x}, rounded according to the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not a power of two + */ + @SuppressWarnings("fallthrough") + // TODO(kevinb): remove after this warning is disabled globally + public static int log2(BigInteger x, RoundingMode mode) { + checkPositive("x", checkNotNull(x)); + int logFloor = x.bitLength() - 1; + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(isPowerOfTwo(x)); // fall through + case DOWN: + case FLOOR: + return logFloor; + + case UP: + case CEILING: + return isPowerOfTwo(x) ? logFloor : logFloor + 1; + + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + if (logFloor < SQRT2_PRECOMPUTE_THRESHOLD) { + BigInteger halfPower = + SQRT2_PRECOMPUTED_BITS.shiftRight(SQRT2_PRECOMPUTE_THRESHOLD - logFloor); + if (x.compareTo(halfPower) <= 0) { + return logFloor; + } else { + return logFloor + 1; + } + } + // Since sqrt(2) is irrational, log2(x) - logFloor cannot be exactly 0.5 + // + // To determine which side of logFloor.5 the logarithm is, + // we compare x^2 to 2^(2 * logFloor + 1). + BigInteger x2 = x.pow(2); + int logX2Floor = x2.bitLength() - 1; + return (logX2Floor < 2 * logFloor + 1) ? logFloor : logFloor + 1; + + default: + throw new AssertionError(); + } + } + + /* + * The maximum number of bits in a square root for which we'll precompute an explicit half power + * of two. This can be any value, but higher values incur more class load time and linearly + * increasing memory consumption. + */ + @VisibleForTesting static final int SQRT2_PRECOMPUTE_THRESHOLD = 256; + + @VisibleForTesting + static final BigInteger SQRT2_PRECOMPUTED_BITS = + new BigInteger("16a09e667f3bcc908b2fb1366ea957d3e3adec17512775099da2f590b0667322a", 16); + + /** + * Returns the base-10 logarithm of {@code x}, rounded according to the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not a power of ten + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("fallthrough") + public static int log10(BigInteger x, RoundingMode mode) { + checkPositive("x", x); + if (fitsInLong(x)) { + return LongMath.log10(x.longValue(), mode); + } + + int approxLog10 = (int) (log2(x, FLOOR) * LN_2 / LN_10); + BigInteger approxPow = BigInteger.TEN.pow(approxLog10); + int approxCmp = approxPow.compareTo(x); + + /* + * We adjust approxLog10 and approxPow until they're equal to floor(log10(x)) and + * 10^floor(log10(x)). + */ + + if (approxCmp > 0) { + /* + * The code is written so that even completely incorrect approximations will still yield the + * correct answer eventually, but in practice this branch should almost never be entered, and + * even then the loop should not run more than once. + */ + do { + approxLog10--; + approxPow = approxPow.divide(BigInteger.TEN); + approxCmp = approxPow.compareTo(x); + } while (approxCmp > 0); + } else { + BigInteger nextPow = BigInteger.TEN.multiply(approxPow); + int nextCmp = nextPow.compareTo(x); + while (nextCmp <= 0) { + approxLog10++; + approxPow = nextPow; + approxCmp = nextCmp; + nextPow = BigInteger.TEN.multiply(approxPow); + nextCmp = nextPow.compareTo(x); + } + } + + int floorLog = approxLog10; + BigInteger floorPow = approxPow; + int floorCmp = approxCmp; + + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(floorCmp == 0); + // fall through + case FLOOR: + case DOWN: + return floorLog; + + case CEILING: + case UP: + return floorPow.equals(x) ? floorLog : floorLog + 1; + + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + // Since sqrt(10) is irrational, log10(x) - floorLog can never be exactly 0.5 + BigInteger x2 = x.pow(2); + BigInteger halfPowerSquared = floorPow.pow(2).multiply(BigInteger.TEN); + return (x2.compareTo(halfPowerSquared) <= 0) ? floorLog : floorLog + 1; + default: + throw new AssertionError(); + } + } + + private static final double LN_10 = Math.log(10); + private static final double LN_2 = Math.log(2); + + /** + * Returns the square root of {@code x}, rounded with the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x < 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code + * sqrt(x)} is not an integer + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("fallthrough") + public static BigInteger sqrt(BigInteger x, RoundingMode mode) { + checkNonNegative("x", x); + if (fitsInLong(x)) { + return BigInteger.valueOf(LongMath.sqrt(x.longValue(), mode)); + } + BigInteger sqrtFloor = sqrtFloor(x); + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(sqrtFloor.pow(2).equals(x)); // fall through + case FLOOR: + case DOWN: + return sqrtFloor; + case CEILING: + case UP: + int sqrtFloorInt = sqrtFloor.intValue(); + boolean sqrtFloorIsExact = + (sqrtFloorInt * sqrtFloorInt == x.intValue()) // fast check mod 2^32 + && sqrtFloor.pow(2).equals(x); // slow exact check + return sqrtFloorIsExact ? sqrtFloor : sqrtFloor.add(BigInteger.ONE); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + BigInteger halfSquare = sqrtFloor.pow(2).add(sqrtFloor); + /* + * We wish to test whether or not x <= (sqrtFloor + 0.5)^2 = halfSquare + 0.25. Since both x + * and halfSquare are integers, this is equivalent to testing whether or not x <= + * halfSquare. + */ + return (halfSquare.compareTo(x) >= 0) ? sqrtFloor : sqrtFloor.add(BigInteger.ONE); + default: + throw new AssertionError(); + } + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static BigInteger sqrtFloor(BigInteger x) { + /* + * Adapted from Hacker's Delight, Figure 11-1. + * + * Using DoubleUtils.bigToDouble, getting a double approximation of x is extremely fast, and + * then we can get a double approximation of the square root. Then, we iteratively improve this + * guess with an application of Newton's method, which sets guess := (guess + (x / guess)) / 2. + * This iteration has the following two properties: + * + * a) every iteration (except potentially the first) has guess >= floor(sqrt(x)). This is + * because guess' is the arithmetic mean of guess and x / guess, sqrt(x) is the geometric mean, + * and the arithmetic mean is always higher than the geometric mean. + * + * b) this iteration converges to floor(sqrt(x)). In fact, the number of correct digits doubles + * with each iteration, so this algorithm takes O(log(digits)) iterations. + * + * We start out with a double-precision approximation, which may be higher or lower than the + * true value. Therefore, we perform at least one Newton iteration to get a guess that's + * definitely >= floor(sqrt(x)), and then continue the iteration until we reach a fixed point. + */ + BigInteger sqrt0; + int log2 = log2(x, FLOOR); + if (log2 < Double.MAX_EXPONENT) { + sqrt0 = sqrtApproxWithDoubles(x); + } else { + int shift = (log2 - DoubleUtils.SIGNIFICAND_BITS) & ~1; // even! + /* + * We have that x / 2^shift < 2^54. Our initial approximation to sqrtFloor(x) will be + * 2^(shift/2) * sqrtApproxWithDoubles(x / 2^shift). + */ + sqrt0 = sqrtApproxWithDoubles(x.shiftRight(shift)).shiftLeft(shift >> 1); + } + BigInteger sqrt1 = sqrt0.add(x.divide(sqrt0)).shiftRight(1); + if (sqrt0.equals(sqrt1)) { + return sqrt0; + } + do { + sqrt0 = sqrt1; + sqrt1 = sqrt0.add(x.divide(sqrt0)).shiftRight(1); + } while (sqrt1.compareTo(sqrt0) < 0); + return sqrt0; + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static BigInteger sqrtApproxWithDoubles(BigInteger x) { + return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), HALF_EVEN); + } + + /** + * Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x} + * is precisely representable as a {@code double}, its {@code double} value will be returned; + * otherwise, the rounding will choose between the two nearest representable values with {@code + * mode}. + * + *

For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN}, + * infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not + * representable as a double, but {@code roundToDouble(BigInteger.valueOf(2).pow(2000), HALF_UP)} + * will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}. + * + *

For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754 + * default rounding mode: if the two nearest representable values are equally near, the one with + * the least significant bit zero is chosen. (In such cases, both of the nearest representable + * values are even integers; this method returns the one that is a multiple of a greater power of + * two.) + * + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not precisely representable as a {@code double} + * @since 30.0 + */ + @J2ktIncompatible + @GwtIncompatible + public static double roundToDouble(BigInteger x, RoundingMode mode) { + return BigIntegerToDoubleRounder.INSTANCE.roundToDouble(x, mode); + } + + @J2ktIncompatible + @GwtIncompatible + private static class BigIntegerToDoubleRounder extends ToDoubleRounder { + static final BigIntegerToDoubleRounder INSTANCE = new BigIntegerToDoubleRounder(); + + private BigIntegerToDoubleRounder() {} + + @Override + double roundToDoubleArbitrarily(BigInteger bigInteger) { + return DoubleUtils.bigToDouble(bigInteger); + } + + @Override + int sign(BigInteger bigInteger) { + return bigInteger.signum(); + } + + @Override + BigInteger toX(double d, RoundingMode mode) { + return DoubleMath.roundToBigInteger(d, mode); + } + + @Override + BigInteger minus(BigInteger a, BigInteger b) { + return a.subtract(b); + } + } + + /** + * Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code + * RoundingMode}. + * + * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} + * is not an integer multiple of {@code b} + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static BigInteger divide(BigInteger p, BigInteger q, RoundingMode mode) { + BigDecimal pDec = new BigDecimal(p); + BigDecimal qDec = new BigDecimal(q); + return pDec.divide(qDec, 0, mode).toBigIntegerExact(); + } + + /** + * Returns {@code n!}, that is, the product of the first {@code n} positive integers, or {@code 1} + * if {@code n == 0}. + * + *

Warning: the result takes O(n log n) space, so use cautiously. + * + *

This uses an efficient binary recursive algorithm to compute the factorial with balanced + * multiplies. It also removes all the 2s from the intermediate products (shifting them back in at + * the end). + * + * @throws IllegalArgumentException if {@code n < 0} + */ + public static BigInteger factorial(int n) { + checkNonNegative("n", n); + + // If the factorial is small enough, just use LongMath to do it. + if (n < LongMath.factorials.length) { + return BigInteger.valueOf(LongMath.factorials[n]); + } + + // Pre-allocate space for our list of intermediate BigIntegers. + int approxSize = IntMath.divide(n * IntMath.log2(n, CEILING), Long.SIZE, CEILING); + ArrayList bignums = new ArrayList<>(approxSize); + + // Start from the pre-computed maximum long factorial. + int startingNumber = LongMath.factorials.length; + long product = LongMath.factorials[startingNumber - 1]; + // Strip off 2s from this value. + int shift = Long.numberOfTrailingZeros(product); + product >>= shift; + + // Use floor(log2(num)) + 1 to prevent overflow of multiplication. + int productBits = LongMath.log2(product, FLOOR) + 1; + int bits = LongMath.log2(startingNumber, FLOOR) + 1; + // Check for the next power of two boundary, to save us a CLZ operation. + int nextPowerOfTwo = 1 << (bits - 1); + + // Iteratively multiply the longs as big as they can go. + for (long num = startingNumber; num <= n; num++) { + // Check to see if the floor(log2(num)) + 1 has changed. + if ((num & nextPowerOfTwo) != 0) { + nextPowerOfTwo <<= 1; + bits++; + } + // Get rid of the 2s in num. + int tz = Long.numberOfTrailingZeros(num); + long normalizedNum = num >> tz; + shift += tz; + // Adjust floor(log2(num)) + 1. + int normalizedBits = bits - tz; + // If it won't fit in a long, then we store off the intermediate product. + if (normalizedBits + productBits >= Long.SIZE) { + bignums.add(BigInteger.valueOf(product)); + product = 1; + productBits = 0; + } + product *= normalizedNum; + productBits = LongMath.log2(product, FLOOR) + 1; + } + // Check for leftovers. + if (product > 1) { + bignums.add(BigInteger.valueOf(product)); + } + // Efficiently multiply all the intermediate products together. + return listProduct(bignums).shiftLeft(shift); + } + + static BigInteger listProduct(List nums) { + return listProduct(nums, 0, nums.size()); + } + + static BigInteger listProduct(List nums, int start, int end) { + switch (end - start) { + case 0: + return BigInteger.ONE; + case 1: + return nums.get(start); + case 2: + return nums.get(start).multiply(nums.get(start + 1)); + case 3: + return nums.get(start).multiply(nums.get(start + 1)).multiply(nums.get(start + 2)); + default: + // Otherwise, split the list in half and recursively do this. + int m = (end + start) >>> 1; + return listProduct(nums, start, m).multiply(listProduct(nums, m, end)); + } + } + + /** + * Returns {@code n} choose {@code k}, also known as the binomial coefficient of {@code n} and + * {@code k}, that is, {@code n! / (k! (n - k)!)}. + * + *

Warning: the result can take as much as O(k log n) space. + * + * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0}, or {@code k > n} + */ + public static BigInteger binomial(int n, int k) { + checkNonNegative("n", n); + checkNonNegative("k", k); + checkArgument(k <= n, "k (%s) > n (%s)", k, n); + if (k > (n >> 1)) { + k = n - k; + } + if (k < LongMath.biggestBinomials.length && n <= LongMath.biggestBinomials[k]) { + return BigInteger.valueOf(LongMath.binomial(n, k)); + } + + BigInteger accum = BigInteger.ONE; + + long numeratorAccum = n; + long denominatorAccum = 1; + + int bits = LongMath.log2(n, CEILING); + + int numeratorBits = bits; + + for (int i = 1; i < k; i++) { + int p = n - i; + int q = i + 1; + + // log2(p) >= bits - 1, because p >= n/2 + + if (numeratorBits + bits >= Long.SIZE - 1) { + // The numerator is as big as it can get without risking overflow. + // Multiply numeratorAccum / denominatorAccum into accum. + accum = + accum + .multiply(BigInteger.valueOf(numeratorAccum)) + .divide(BigInteger.valueOf(denominatorAccum)); + numeratorAccum = p; + denominatorAccum = q; + numeratorBits = bits; + } else { + // We can definitely multiply into the long accumulators without overflowing them. + numeratorAccum *= p; + denominatorAccum *= q; + numeratorBits += bits; + } + } + return accum + .multiply(BigInteger.valueOf(numeratorAccum)) + .divide(BigInteger.valueOf(denominatorAccum)); + } + + // Returns true if BigInteger.valueOf(x.longValue()).equals(x). + @J2ktIncompatible + @GwtIncompatible // TODO + static boolean fitsInLong(BigInteger x) { + return x.bitLength() <= Long.SIZE - 1; + } + + private BigIntegerMath() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/DoubleMath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/DoubleMath.java new file mode 100644 index 0000000000000..6ee8eff1535e8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/DoubleMath.java @@ -0,0 +1,544 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.primitives.Booleans; + +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Iterator; + +import static java.lang.Math.abs; +import static java.lang.Math.copySign; +import static java.lang.Math.getExponent; +import static java.lang.Math.log; +import static java.lang.Math.rint; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.math.DoubleUtils.IMPLICIT_BIT; +import static org.apache.iotdb.google.common.math.DoubleUtils.SIGNIFICAND_BITS; +import static org.apache.iotdb.google.common.math.DoubleUtils.getSignificand; +import static org.apache.iotdb.google.common.math.DoubleUtils.isFinite; +import static org.apache.iotdb.google.common.math.DoubleUtils.isNormal; +import static org.apache.iotdb.google.common.math.DoubleUtils.scaleNormalize; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkInRangeForRoundingInputs; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkNonNegative; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkRoundingUnnecessary; + +/** + * A class for arithmetic on doubles that is not covered by {@link java.lang.Math}. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class DoubleMath { + /* + * This method returns a value y such that rounding y DOWN (towards zero) gives the same result as + * rounding x according to the specified mode. + */ + @J2ktIncompatible + @GwtIncompatible // #isMathematicalInteger, org.apache.iotdb.google.common.math.DoubleUtils + static double roundIntermediate(double x, RoundingMode mode) { + if (!isFinite(x)) { + throw new ArithmeticException("input is infinite or NaN"); + } + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(isMathematicalInteger(x)); + return x; + + case FLOOR: + if (x >= 0.0 || isMathematicalInteger(x)) { + return x; + } else { + return (long) x - 1; + } + + case CEILING: + if (x <= 0.0 || isMathematicalInteger(x)) { + return x; + } else { + return (long) x + 1; + } + + case DOWN: + return x; + + case UP: + if (isMathematicalInteger(x)) { + return x; + } else { + return (long) x + (x > 0 ? 1 : -1); + } + + case HALF_EVEN: + return rint(x); + + case HALF_UP: + { + double z = rint(x); + if (abs(x - z) == 0.5) { + return x + copySign(0.5, x); + } else { + return z; + } + } + + case HALF_DOWN: + { + double z = rint(x); + if (abs(x - z) == 0.5) { + return x; + } else { + return z; + } + } + + default: + throw new AssertionError(); + } + } + + /** + * Returns the {@code int} value that is equal to {@code x} rounded with the specified rounding + * mode, if possible. + * + * @throws ArithmeticException if + *

    + *
  • {@code x} is infinite or NaN + *
  • {@code x}, after being rounded to a mathematical integer using the specified rounding + * mode, is either less than {@code Integer.MIN_VALUE} or greater than {@code + * Integer.MAX_VALUE} + *
  • {@code x} is not a mathematical integer and {@code mode} is {@link + * RoundingMode#UNNECESSARY} + *
+ */ + @J2ktIncompatible + @GwtIncompatible // #roundIntermediate + public static int roundToInt(double x, RoundingMode mode) { + double z = roundIntermediate(x, mode); + checkInRangeForRoundingInputs( + z > MIN_INT_AS_DOUBLE - 1.0 & z < MAX_INT_AS_DOUBLE + 1.0, x, mode); + return (int) z; + } + + private static final double MIN_INT_AS_DOUBLE = -0x1p31; + private static final double MAX_INT_AS_DOUBLE = 0x1p31 - 1.0; + + /** + * Returns the {@code long} value that is equal to {@code x} rounded with the specified rounding + * mode, if possible. + * + * @throws ArithmeticException if + *
    + *
  • {@code x} is infinite or NaN + *
  • {@code x}, after being rounded to a mathematical integer using the specified rounding + * mode, is either less than {@code Long.MIN_VALUE} or greater than {@code + * Long.MAX_VALUE} + *
  • {@code x} is not a mathematical integer and {@code mode} is {@link + * RoundingMode#UNNECESSARY} + *
+ */ + @J2ktIncompatible + @GwtIncompatible // #roundIntermediate + public static long roundToLong(double x, RoundingMode mode) { + double z = roundIntermediate(x, mode); + checkInRangeForRoundingInputs( + MIN_LONG_AS_DOUBLE - z < 1.0 & z < MAX_LONG_AS_DOUBLE_PLUS_ONE, x, mode); + return (long) z; + } + + private static final double MIN_LONG_AS_DOUBLE = -0x1p63; + /* + * We cannot store Long.MAX_VALUE as a double without losing precision. Instead, we store + * Long.MAX_VALUE + 1 == -Long.MIN_VALUE, and then offset all comparisons by 1. + */ + private static final double MAX_LONG_AS_DOUBLE_PLUS_ONE = 0x1p63; + + /** + * Returns the {@code BigInteger} value that is equal to {@code x} rounded with the specified + * rounding mode, if possible. + * + * @throws ArithmeticException if + *
    + *
  • {@code x} is infinite or NaN + *
  • {@code x} is not a mathematical integer and {@code mode} is {@link + * RoundingMode#UNNECESSARY} + *
+ */ + // #roundIntermediate, java.lang.Math.getExponent, org.apache.iotdb.google.common.math.DoubleUtils + @J2ktIncompatible + @GwtIncompatible + public static BigInteger roundToBigInteger(double x, RoundingMode mode) { + x = roundIntermediate(x, mode); + if (MIN_LONG_AS_DOUBLE - x < 1.0 & x < MAX_LONG_AS_DOUBLE_PLUS_ONE) { + return BigInteger.valueOf((long) x); + } + int exponent = getExponent(x); + long significand = getSignificand(x); + BigInteger result = BigInteger.valueOf(significand).shiftLeft(exponent - SIGNIFICAND_BITS); + return (x < 0) ? result.negate() : result; + } + + /** + * Returns {@code true} if {@code x} is exactly equal to {@code 2^k} for some finite integer + * {@code k}. + */ + @J2ktIncompatible + @GwtIncompatible // org.apache.iotdb.google.common.math.DoubleUtils + public static boolean isPowerOfTwo(double x) { + if (x > 0.0 && isFinite(x)) { + long significand = getSignificand(x); + return (significand & (significand - 1)) == 0; + } + return false; + } + + /** + * Returns the base 2 logarithm of a double value. + * + *

Special cases: + * + *

    + *
  • If {@code x} is NaN or less than zero, the result is NaN. + *
  • If {@code x} is positive infinity, the result is positive infinity. + *
  • If {@code x} is positive or negative zero, the result is negative infinity. + *
+ * + *

The computed result is within 1 ulp of the exact result. + * + *

If the result of this method will be immediately rounded to an {@code int}, {@link + * #log2(double, RoundingMode)} is faster. + */ + public static double log2(double x) { + return log(x) / LN_2; // surprisingly within 1 ulp according to tests + } + + /** + * Returns the base 2 logarithm of a double value, rounded with the specified rounding mode to an + * {@code int}. + * + *

Regardless of the rounding mode, this is faster than {@code (int) log2(x)}. + * + * @throws IllegalArgumentException if {@code x <= 0.0}, {@code x} is NaN, or {@code x} is + * infinite + */ + @J2ktIncompatible + @GwtIncompatible // java.lang.Math.getExponent, org.apache.iotdb.google.common.math.DoubleUtils + @SuppressWarnings("fallthrough") + public static int log2(double x, RoundingMode mode) { + checkArgument(x > 0.0 && isFinite(x), "x must be positive and finite"); + int exponent = getExponent(x); + if (!isNormal(x)) { + return log2(x * IMPLICIT_BIT, mode) - SIGNIFICAND_BITS; + // Do the calculation on a normal value. + } + // x is positive, finite, and normal + boolean increment; + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(isPowerOfTwo(x)); + // fall through + case FLOOR: + increment = false; + break; + case CEILING: + increment = !isPowerOfTwo(x); + break; + case DOWN: + increment = exponent < 0 & !isPowerOfTwo(x); + break; + case UP: + increment = exponent >= 0 & !isPowerOfTwo(x); + break; + case HALF_DOWN: + case HALF_EVEN: + case HALF_UP: + double xScaled = scaleNormalize(x); + // sqrt(2) is irrational, and the spec is relative to the "exact numerical result," + // so log2(x) is never exactly exponent + 0.5. + increment = (xScaled * xScaled) > 2.0; + break; + default: + throw new AssertionError(); + } + return increment ? exponent + 1 : exponent; + } + + private static final double LN_2 = log(2); + + /** + * Returns {@code true} if {@code x} represents a mathematical integer. + * + *

This is equivalent to, but not necessarily implemented as, the expression {@code + * !Double.isNaN(x) && !Double.isInfinite(x) && x == Math.rint(x)}. + */ + @GwtIncompatible // java.lang.Math.getExponent, org.apache.iotdb.google.common.math.DoubleUtils + public static boolean isMathematicalInteger(double x) { + return isFinite(x) + && (x == 0.0 + || SIGNIFICAND_BITS - Long.numberOfTrailingZeros(getSignificand(x)) <= getExponent(x)); + } + + /** + * Returns {@code n!}, that is, the product of the first {@code n} positive integers, {@code 1} if + * {@code n == 0}, or {@code n!}, or {@link Double#POSITIVE_INFINITY} if {@code n! > + * Double.MAX_VALUE}. + * + *

The result is within 1 ulp of the true value. + * + * @throws IllegalArgumentException if {@code n < 0} + */ + public static double factorial(int n) { + checkNonNegative("n", n); + if (n > MAX_FACTORIAL) { + return Double.POSITIVE_INFINITY; + } else { + // Multiplying the last (n & 0xf) values into their own accumulator gives a more accurate + // result than multiplying by everySixteenthFactorial[n >> 4] directly. + double accum = 1.0; + for (int i = 1 + (n & ~0xf); i <= n; i++) { + accum *= i; + } + return accum * everySixteenthFactorial[n >> 4]; + } + } + + @VisibleForTesting static final int MAX_FACTORIAL = 170; + + @VisibleForTesting + static final double[] everySixteenthFactorial = { + 0x1.0p0, + 0x1.30777758p44, + 0x1.956ad0aae33a4p117, + 0x1.ee69a78d72cb6p202, + 0x1.fe478ee34844ap295, + 0x1.c619094edabffp394, + 0x1.3638dd7bd6347p498, + 0x1.7cac197cfe503p605, + 0x1.1e5dfc140e1e5p716, + 0x1.8ce85fadb707ep829, + 0x1.95d5f3d928edep945 + }; + + /** + * Returns {@code true} if {@code a} and {@code b} are within {@code tolerance} of each other. + * + *

Technically speaking, this is equivalent to {@code Math.abs(a - b) <= tolerance || + * Double.valueOf(a).equals(Double.valueOf(b))}. + * + *

Notable special cases include: + * + *

    + *
  • All NaNs are fuzzily equal. + *
  • If {@code a == b}, then {@code a} and {@code b} are always fuzzily equal. + *
  • Positive and negative zero are always fuzzily equal. + *
  • If {@code tolerance} is zero, and neither {@code a} nor {@code b} is NaN, then {@code a} + * and {@code b} are fuzzily equal if and only if {@code a == b}. + *
  • With {@link Double#POSITIVE_INFINITY} tolerance, all non-NaN values are fuzzily equal. + *
  • With finite tolerance, {@code Double.POSITIVE_INFINITY} and {@code + * Double.NEGATIVE_INFINITY} are fuzzily equal only to themselves. + *
+ * + *

This is reflexive and symmetric, but not transitive, so it is not an + * equivalence relation and not suitable for use in {@link Object#equals} + * implementations. + * + * @throws IllegalArgumentException if {@code tolerance} is {@code < 0} or NaN + * @since 13.0 + */ + public static boolean fuzzyEquals(double a, double b, double tolerance) { + MathPreconditions.checkNonNegative("tolerance", tolerance); + return Math.copySign(a - b, 1.0) <= tolerance + // copySign(x, 1.0) is a branch-free version of abs(x), but with different NaN semantics + || (a == b) // needed to ensure that infinities equal themselves + || (Double.isNaN(a) && Double.isNaN(b)); + } + + /** + * Compares {@code a} and {@code b} "fuzzily," with a tolerance for nearly-equal values. + * + *

This method is equivalent to {@code fuzzyEquals(a, b, tolerance) ? 0 : Double.compare(a, + * b)}. In particular, like {@link Double#compare(double, double)}, it treats all NaN values as + * equal and greater than all other values (including {@link Double#POSITIVE_INFINITY}). + * + *

This is not a total ordering and is not suitable for use in {@link + * Comparable#compareTo} implementations. In particular, it is not transitive. + * + * @throws IllegalArgumentException if {@code tolerance} is {@code < 0} or NaN + * @since 13.0 + */ + public static int fuzzyCompare(double a, double b, double tolerance) { + if (fuzzyEquals(a, b, tolerance)) { + return 0; + } else if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return Booleans.compare(Double.isNaN(a), Double.isNaN(b)); + } + } + + /** + * Returns the arithmetic mean of + * {@code values}. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + * @param values a nonempty series of values + * @throws IllegalArgumentException if {@code values} is empty or contains any non-finite value + * @deprecated Use {@link Stats#meanOf} instead, noting the less strict handling of non-finite + * values. + */ + @Deprecated + // org.apache.iotdb.google.common.math.DoubleUtils + @J2ktIncompatible + @GwtIncompatible + public static double mean(double... values) { + checkArgument(values.length > 0, "Cannot take mean of 0 values"); + long count = 1; + double mean = checkFinite(values[0]); + for (int index = 1; index < values.length; ++index) { + checkFinite(values[index]); + count++; + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (values[index] - mean) / count; + } + return mean; + } + + /** + * Returns the arithmetic mean of + * {@code values}. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + * @param values a nonempty series of values + * @throws IllegalArgumentException if {@code values} is empty + * @deprecated Use {@link Stats#meanOf} instead, noting the less strict handling of non-finite + * values. + */ + @Deprecated + public static double mean(int... values) { + checkArgument(values.length > 0, "Cannot take mean of 0 values"); + // The upper bound on the length of an array and the bounds on the int values mean that, in + // this case only, we can compute the sum as a long without risking overflow or loss of + // precision. So we do that, as it's slightly quicker than the Knuth algorithm. + long sum = 0; + for (int index = 0; index < values.length; ++index) { + sum += values[index]; + } + return (double) sum / values.length; + } + + /** + * Returns the arithmetic mean of + * {@code values}. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + * @param values a nonempty series of values, which will be converted to {@code double} values + * (this may cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + * @throws IllegalArgumentException if {@code values} is empty + * @deprecated Use {@link Stats#meanOf} instead, noting the less strict handling of non-finite + * values. + */ + @Deprecated + public static double mean(long... values) { + checkArgument(values.length > 0, "Cannot take mean of 0 values"); + long count = 1; + double mean = values[0]; + for (int index = 1; index < values.length; ++index) { + count++; + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (values[index] - mean) / count; + } + return mean; + } + + /** + * Returns the arithmetic mean of + * {@code values}. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + * @param values a nonempty series of values, which will be converted to {@code double} values + * (this may cause loss of precision) + * @throws IllegalArgumentException if {@code values} is empty or contains any non-finite value + * @deprecated Use {@link Stats#meanOf} instead, noting the less strict handling of non-finite + * values. + */ + @Deprecated + // org.apache.iotdb.google.common.math.DoubleUtils + @J2ktIncompatible + @GwtIncompatible + public static double mean(Iterable values) { + return mean(values.iterator()); + } + + /** + * Returns the arithmetic mean of + * {@code values}. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + * @param values a nonempty series of values, which will be converted to {@code double} values + * (this may cause loss of precision) + * @throws IllegalArgumentException if {@code values} is empty or contains any non-finite value + * @deprecated Use {@link Stats#meanOf} instead, noting the less strict handling of non-finite + * values. + */ + @Deprecated + // org.apache.iotdb.google.common.math.DoubleUtils + @J2ktIncompatible + @GwtIncompatible + public static double mean(Iterator values) { + checkArgument(values.hasNext(), "Cannot take mean of 0 values"); + long count = 1; + double mean = checkFinite(values.next().doubleValue()); + while (values.hasNext()) { + double value = checkFinite(values.next().doubleValue()); + count++; + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (value - mean) / count; + } + return mean; + } + + @J2ktIncompatible + @GwtIncompatible // org.apache.iotdb.google.common.math.DoubleUtils + private static double checkFinite(double argument) { + checkArgument(isFinite(argument)); + return argument; + } + + private DoubleMath() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/DoubleUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/DoubleUtils.java new file mode 100644 index 0000000000000..7fab431ee2629 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/DoubleUtils.java @@ -0,0 +1,145 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.math.BigInteger; + +import static java.lang.Double.MAX_EXPONENT; +import static java.lang.Double.MIN_EXPONENT; +import static java.lang.Double.POSITIVE_INFINITY; +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.isNaN; +import static java.lang.Double.longBitsToDouble; +import static java.lang.Math.getExponent; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * Utilities for {@code double} primitives. + * + * @author Louis Wasserman + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class DoubleUtils { + private DoubleUtils() {} + + static double nextDown(double d) { + return -Math.nextUp(-d); + } + + // The mask for the significand, according to the {@link + // Double#doubleToRawLongBits(double)} spec. + static final long SIGNIFICAND_MASK = 0x000fffffffffffffL; + + // The mask for the exponent, according to the {@link + // Double#doubleToRawLongBits(double)} spec. + static final long EXPONENT_MASK = 0x7ff0000000000000L; + + // The mask for the sign, according to the {@link + // Double#doubleToRawLongBits(double)} spec. + static final long SIGN_MASK = 0x8000000000000000L; + + static final int SIGNIFICAND_BITS = 52; + + static final int EXPONENT_BIAS = 1023; + + /** The implicit 1 bit that is omitted in significands of normal doubles. */ + static final long IMPLICIT_BIT = SIGNIFICAND_MASK + 1; + + static long getSignificand(double d) { + checkArgument(isFinite(d), "not a normal value"); + int exponent = getExponent(d); + long bits = doubleToRawLongBits(d); + bits &= SIGNIFICAND_MASK; + return (exponent == MIN_EXPONENT - 1) ? bits << 1 : bits | IMPLICIT_BIT; + } + + static boolean isFinite(double d) { + return getExponent(d) <= MAX_EXPONENT; + } + + static boolean isNormal(double d) { + return getExponent(d) >= MIN_EXPONENT; + } + + /* + * Returns x scaled by a power of 2 such that it is in the range [1, 2). Assumes x is positive, + * normal, and finite. + */ + static double scaleNormalize(double x) { + long significand = doubleToRawLongBits(x) & SIGNIFICAND_MASK; + return longBitsToDouble(significand | ONE_BITS); + } + + static double bigToDouble(BigInteger x) { + // This is an extremely fast implementation of BigInteger.doubleValue(). JDK patch pending. + BigInteger absX = x.abs(); + int exponent = absX.bitLength() - 1; + // exponent == floor(log2(abs(x))) + if (exponent < Long.SIZE - 1) { + return x.longValue(); + } else if (exponent > MAX_EXPONENT) { + return x.signum() * POSITIVE_INFINITY; + } + + /* + * We need the top SIGNIFICAND_BITS + 1 bits, including the "implicit" one bit. To make rounding + * easier, we pick out the top SIGNIFICAND_BITS + 2 bits, so we have one to help us round up or + * down. twiceSignifFloor will contain the top SIGNIFICAND_BITS + 2 bits, and signifFloor the + * top SIGNIFICAND_BITS + 1. + * + * It helps to consider the real number signif = absX * 2^(SIGNIFICAND_BITS - exponent). + */ + int shift = exponent - SIGNIFICAND_BITS - 1; + long twiceSignifFloor = absX.shiftRight(shift).longValue(); + long signifFloor = twiceSignifFloor >> 1; + signifFloor &= SIGNIFICAND_MASK; // remove the implied bit + + /* + * We round up if either the fractional part of signif is strictly greater than 0.5 (which is + * true if the 0.5 bit is set and any lower bit is set), or if the fractional part of signif is + * >= 0.5 and signifFloor is odd (which is true if both the 0.5 bit and the 1 bit are set). + */ + boolean increment = + (twiceSignifFloor & 1) != 0 && ((signifFloor & 1) != 0 || absX.getLowestSetBit() < shift); + long signifRounded = increment ? signifFloor + 1 : signifFloor; + long bits = (long) (exponent + EXPONENT_BIAS) << SIGNIFICAND_BITS; + bits += signifRounded; + /* + * If signifRounded == 2^53, we'd need to set all of the significand bits to zero and add 1 to + * the exponent. This is exactly the behavior we get from just adding signifRounded to bits + * directly. If the exponent is MAX_DOUBLE_EXPONENT, we round up (correctly) to + * Double.POSITIVE_INFINITY. + */ + bits |= x.signum() & SIGN_MASK; + return longBitsToDouble(bits); + } + + /** Returns its argument if it is non-negative, zero if it is negative. */ + static double ensureNonNegative(double value) { + checkArgument(!isNaN(value)); + return Math.max(value, 0.0); + } + + @VisibleForTesting static final long ONE_BITS = 0x3ff0000000000000L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..7dabc1cc93cdb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/IntMath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/IntMath.java new file mode 100644 index 0000000000000..89112dbca275c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/IntMath.java @@ -0,0 +1,731 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.math.BigInteger; +import java.math.RoundingMode; + +import static java.lang.Math.abs; +import static java.lang.Math.min; +import static java.math.RoundingMode.HALF_EVEN; +import static java.math.RoundingMode.HALF_UP; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkNoOverflow; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkNonNegative; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkPositive; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkRoundingUnnecessary; + +/** + * A class for arithmetic on values of type {@code int}. Where possible, methods are defined and + * named analogously to their {@code BigInteger} counterparts. + * + *

The implementations of many methods in this class are based on material from Henry S. Warren, + * Jr.'s Hacker's Delight, (Addison Wesley, 2002). + * + *

Similar functionality for {@code long} and for {@link BigInteger} can be found in {@link + * LongMath} and {@link BigIntegerMath} respectively. For other common operations on {@code int} + * values, see {@link org.apache.iotdb.google.common.primitives.Ints}. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class IntMath { + // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + + @VisibleForTesting static final int MAX_SIGNED_POWER_OF_TWO = 1 << (Integer.SIZE - 2); + + /** + * Returns the smallest power of two greater than or equal to {@code x}. This is equivalent to + * {@code checkedPow(2, log2(x, CEILING))}. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException of the next-higher power of two is not representable as an {@code + * int}, i.e. when {@code x > 2^30} + * @since 20.0 + */ + public static int ceilingPowerOfTwo(int x) { + checkPositive("x", x); + if (x > MAX_SIGNED_POWER_OF_TWO) { + throw new ArithmeticException("ceilingPowerOfTwo(" + x + ") not representable as an int"); + } + return 1 << -Integer.numberOfLeadingZeros(x - 1); + } + + /** + * Returns the largest power of two less than or equal to {@code x}. This is equivalent to {@code + * checkedPow(2, log2(x, FLOOR))}. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @since 20.0 + */ + public static int floorPowerOfTwo(int x) { + checkPositive("x", x); + return Integer.highestOneBit(x); + } + + /** + * Returns {@code true} if {@code x} represents a power of two. + * + *

This differs from {@code Integer.bitCount(x) == 1}, because {@code + * Integer.bitCount(Integer.MIN_VALUE) == 1}, but {@link Integer#MIN_VALUE} is not a power of two. + */ + public static boolean isPowerOfTwo(int x) { + return x > 0 & (x & (x - 1)) == 0; + } + + /** + * Returns 1 if {@code x < y} as unsigned integers, and 0 otherwise. Assumes that x - y fits into + * a signed int. The implementation is branch-free, and benchmarks suggest it is measurably (if + * narrowly) faster than the straightforward ternary expression. + */ + @VisibleForTesting + static int lessThanBranchFree(int x, int y) { + // The double negation is optimized away by normal Java, but is necessary for GWT + // to make sure bit twiddling works as expected. + return ~~(x - y) >>> (Integer.SIZE - 1); + } + + /** + * Returns the base-2 logarithm of {@code x}, rounded according to the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not a power of two + */ + @SuppressWarnings("fallthrough") + // TODO(kevinb): remove after this warning is disabled globally + public static int log2(int x, RoundingMode mode) { + checkPositive("x", x); + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(isPowerOfTwo(x)); + // fall through + case DOWN: + case FLOOR: + return (Integer.SIZE - 1) - Integer.numberOfLeadingZeros(x); + + case UP: + case CEILING: + return Integer.SIZE - Integer.numberOfLeadingZeros(x - 1); + + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + // Since sqrt(2) is irrational, log2(x) - logFloor cannot be exactly 0.5 + int leadingZeros = Integer.numberOfLeadingZeros(x); + int cmp = MAX_POWER_OF_SQRT2_UNSIGNED >>> leadingZeros; + // floor(2^(logFloor + 0.5)) + int logFloor = (Integer.SIZE - 1) - leadingZeros; + return logFloor + lessThanBranchFree(cmp, x); + + default: + throw new AssertionError(); + } + } + + /** The biggest half power of two that can fit in an unsigned int. */ + @VisibleForTesting static final int MAX_POWER_OF_SQRT2_UNSIGNED = 0xB504F333; + + /** + * Returns the base-10 logarithm of {@code x}, rounded according to the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not a power of ten + */ + @J2ktIncompatible + @GwtIncompatible // need BigIntegerMath to adequately test + @SuppressWarnings("fallthrough") + public static int log10(int x, RoundingMode mode) { + checkPositive("x", x); + int logFloor = log10Floor(x); + int floorPow = powersOf10[logFloor]; + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(x == floorPow); + // fall through + case FLOOR: + case DOWN: + return logFloor; + case CEILING: + case UP: + return logFloor + lessThanBranchFree(floorPow, x); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + // sqrt(10) is irrational, so log10(x) - logFloor is never exactly 0.5 + return logFloor + lessThanBranchFree(halfPowersOf10[logFloor], x); + default: + throw new AssertionError(); + } + } + + private static int log10Floor(int x) { + /* + * Based on Hacker's Delight Fig. 11-5, the two-table-lookup, branch-free implementation. + * + * The key idea is that based on the number of leading zeros (equivalently, floor(log2(x))), we + * can narrow the possible floor(log10(x)) values to two. For example, if floor(log2(x)) is 6, + * then 64 <= x < 128, so floor(log10(x)) is either 1 or 2. + */ + int y = maxLog10ForLeadingZeros[Integer.numberOfLeadingZeros(x)]; + /* + * y is the higher of the two possible values of floor(log10(x)). If x < 10^y, then we want the + * lower of the two possible values, or y - 1, otherwise, we want y. + */ + return y - lessThanBranchFree(x, powersOf10[y]); + } + + // maxLog10ForLeadingZeros[i] == floor(log10(2^(Long.SIZE - i))) + @VisibleForTesting + static final byte[] maxLog10ForLeadingZeros = { + 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, + 0 + }; + + @VisibleForTesting + static final int[] powersOf10 = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 + }; + + // halfPowersOf10[i] = largest int less than 10^(i + 0.5) + @VisibleForTesting + static final int[] halfPowersOf10 = { + 3, 31, 316, 3162, 31622, 316227, 3162277, 31622776, 316227766, Integer.MAX_VALUE + }; + + /** + * Returns {@code b} to the {@code k}th power. Even if the result overflows, it will be equal to + * {@code BigInteger.valueOf(b).pow(k).intValue()}. This implementation runs in {@code O(log k)} + * time. + * + *

Compare {@link #checkedPow}, which throws an {@link ArithmeticException} upon overflow. + * + * @throws IllegalArgumentException if {@code k < 0} + */ + @J2ktIncompatible + @GwtIncompatible // failing tests + public static int pow(int b, int k) { + checkNonNegative("exponent", k); + switch (b) { + case 0: + return (k == 0) ? 1 : 0; + case 1: + return 1; + case (-1): + return ((k & 1) == 0) ? 1 : -1; + case 2: + return (k < Integer.SIZE) ? (1 << k) : 0; + case (-2): + if (k < Integer.SIZE) { + return ((k & 1) == 0) ? (1 << k) : -(1 << k); + } else { + return 0; + } + default: + // continue below to handle the general case + } + for (int accum = 1; ; k >>= 1) { + switch (k) { + case 0: + return accum; + case 1: + return b * accum; + default: + accum *= ((k & 1) == 0) ? 1 : b; + b *= b; + } + } + } + + /** + * Returns the square root of {@code x}, rounded with the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x < 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code + * sqrt(x)} is not an integer + */ + @GwtIncompatible // need BigIntegerMath to adequately test + @SuppressWarnings("fallthrough") + public static int sqrt(int x, RoundingMode mode) { + checkNonNegative("x", x); + int sqrtFloor = sqrtFloor(x); + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(sqrtFloor * sqrtFloor == x); // fall through + case FLOOR: + case DOWN: + return sqrtFloor; + case CEILING: + case UP: + return sqrtFloor + lessThanBranchFree(sqrtFloor * sqrtFloor, x); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + int halfSquare = sqrtFloor * sqrtFloor + sqrtFloor; + /* + * We wish to test whether or not x <= (sqrtFloor + 0.5)^2 = halfSquare + 0.25. Since both x + * and halfSquare are integers, this is equivalent to testing whether or not x <= + * halfSquare. (We have to deal with overflow, though.) + * + * If we treat halfSquare as an unsigned int, we know that + * sqrtFloor^2 <= x < (sqrtFloor + 1)^2 + * halfSquare - sqrtFloor <= x < halfSquare + sqrtFloor + 1 + * so |x - halfSquare| <= sqrtFloor. Therefore, it's safe to treat x - halfSquare as a + * signed int, so lessThanBranchFree is safe for use. + */ + return sqrtFloor + lessThanBranchFree(halfSquare, x); + default: + throw new AssertionError(); + } + } + + private static int sqrtFloor(int x) { + // There is no loss of precision in converting an int to a double, according to + // http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.2 + return (int) Math.sqrt(x); + } + + /** + * Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code + * RoundingMode}. + * + * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} + * is not an integer multiple of {@code b} + */ + @SuppressWarnings("fallthrough") + public static int divide(int p, int q, RoundingMode mode) { + checkNotNull(mode); + if (q == 0) { + throw new ArithmeticException("/ by zero"); // for GWT + } + int div = p / q; + int rem = p - q * div; // equal to p % q + + if (rem == 0) { + return div; + } + + /* + * Normal Java division rounds towards 0, consistently with RoundingMode.DOWN. We just have to + * deal with the cases where rounding towards 0 is wrong, which typically depends on the sign of + * p / q. + * + * signum is 1 if p and q are both nonnegative or both negative, and -1 otherwise. + */ + int signum = 1 | ((p ^ q) >> (Integer.SIZE - 1)); + boolean increment; + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(rem == 0); + // fall through + case DOWN: + increment = false; + break; + case UP: + increment = true; + break; + case CEILING: + increment = signum > 0; + break; + case FLOOR: + increment = signum < 0; + break; + case HALF_EVEN: + case HALF_DOWN: + case HALF_UP: + int absRem = abs(rem); + int cmpRemToHalfDivisor = absRem - (abs(q) - absRem); + // subtracting two nonnegative ints can't overflow + // cmpRemToHalfDivisor has the same sign as compare(abs(rem), abs(q) / 2). + if (cmpRemToHalfDivisor == 0) { // exactly on the half mark + increment = (mode == HALF_UP || (mode == HALF_EVEN & (div & 1) != 0)); + } else { + increment = cmpRemToHalfDivisor > 0; // closer to the UP value + } + break; + default: + throw new AssertionError(); + } + return increment ? div + signum : div; + } + + /** + * Returns {@code x mod m}, a non-negative value less than {@code m}. This differs from {@code x % + * m}, which might be negative. + * + *

For example: + * + *

{@code
+   * mod(7, 4) == 3
+   * mod(-7, 4) == 1
+   * mod(-1, 4) == 3
+   * mod(-8, 4) == 0
+   * mod(8, 4) == 0
+   * }
+ * + * @throws ArithmeticException if {@code m <= 0} + * @see + * Remainder Operator + */ + public static int mod(int x, int m) { + if (m <= 0) { + throw new ArithmeticException("Modulus " + m + " must be > 0"); + } + int result = x % m; + return (result >= 0) ? result : result + m; + } + + /** + * Returns the greatest common divisor of {@code a, b}. Returns {@code 0} if {@code a == 0 && b == + * 0}. + * + * @throws IllegalArgumentException if {@code a < 0} or {@code b < 0} + */ + public static int gcd(int a, int b) { + /* + * The reason we require both arguments to be >= 0 is because otherwise, what do you return on + * gcd(0, Integer.MIN_VALUE)? BigInteger.gcd would return positive 2^31, but positive 2^31 isn't + * an int. + */ + checkNonNegative("a", a); + checkNonNegative("b", b); + if (a == 0) { + // 0 % b == 0, so b divides a, but the converse doesn't hold. + // BigInteger.gcd is consistent with this decision. + return b; + } else if (b == 0) { + return a; // similar logic + } + /* + * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm. This is + * >40% faster than the Euclidean algorithm in benchmarks. + */ + int aTwos = Integer.numberOfTrailingZeros(a); + a >>= aTwos; // divide out all 2s + int bTwos = Integer.numberOfTrailingZeros(b); + b >>= bTwos; // divide out all 2s + while (a != b) { // both a, b are odd + // The key to the binary GCD algorithm is as follows: + // Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b). + // But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two. + + // We bend over backwards to avoid branching, adapting a technique from + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax + + int delta = a - b; // can't overflow, since a and b are nonnegative + + int minDeltaOrZero = delta & (delta >> (Integer.SIZE - 1)); + // equivalent to Math.min(delta, 0) + + a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b) + // a is now nonnegative and even + + b += minDeltaOrZero; // sets b to min(old a, b) + a >>= Integer.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b + } + return a << min(aTwos, bTwos); + } + + /** + * Returns the sum of {@code a} and {@code b}, provided it does not overflow. + * + * @throws ArithmeticException if {@code a + b} overflows in signed {@code int} arithmetic + */ + public static int checkedAdd(int a, int b) { + long result = (long) a + b; + checkNoOverflow(result == (int) result, "checkedAdd", a, b); + return (int) result; + } + + /** + * Returns the difference of {@code a} and {@code b}, provided it does not overflow. + * + * @throws ArithmeticException if {@code a - b} overflows in signed {@code int} arithmetic + */ + public static int checkedSubtract(int a, int b) { + long result = (long) a - b; + checkNoOverflow(result == (int) result, "checkedSubtract", a, b); + return (int) result; + } + + /** + * Returns the product of {@code a} and {@code b}, provided it does not overflow. + * + * @throws ArithmeticException if {@code a * b} overflows in signed {@code int} arithmetic + */ + public static int checkedMultiply(int a, int b) { + long result = (long) a * b; + checkNoOverflow(result == (int) result, "checkedMultiply", a, b); + return (int) result; + } + + /** + * Returns the {@code b} to the {@code k}th power, provided it does not overflow. + * + *

{@link #pow} may be faster, but does not check for overflow. + * + * @throws ArithmeticException if {@code b} to the {@code k}th power overflows in signed {@code + * int} arithmetic + */ + public static int checkedPow(int b, int k) { + checkNonNegative("exponent", k); + switch (b) { + case 0: + return (k == 0) ? 1 : 0; + case 1: + return 1; + case (-1): + return ((k & 1) == 0) ? 1 : -1; + case 2: + checkNoOverflow(k < Integer.SIZE - 1, "checkedPow", b, k); + return 1 << k; + case (-2): + checkNoOverflow(k < Integer.SIZE, "checkedPow", b, k); + return ((k & 1) == 0) ? 1 << k : -1 << k; + default: + // continue below to handle the general case + } + int accum = 1; + while (true) { + switch (k) { + case 0: + return accum; + case 1: + return checkedMultiply(accum, b); + default: + if ((k & 1) != 0) { + accum = checkedMultiply(accum, b); + } + k >>= 1; + if (k > 0) { + checkNoOverflow(-FLOOR_SQRT_MAX_INT <= b & b <= FLOOR_SQRT_MAX_INT, "checkedPow", b, k); + b *= b; + } + } + } + } + + /** + * Returns the sum of {@code a} and {@code b} unless it would overflow or underflow in which case + * {@code Integer.MAX_VALUE} or {@code Integer.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + public static int saturatedAdd(int a, int b) { + return Ints.saturatedCast((long) a + b); + } + + /** + * Returns the difference of {@code a} and {@code b} unless it would overflow or underflow in + * which case {@code Integer.MAX_VALUE} or {@code Integer.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + public static int saturatedSubtract(int a, int b) { + return Ints.saturatedCast((long) a - b); + } + + /** + * Returns the product of {@code a} and {@code b} unless it would overflow or underflow in which + * case {@code Integer.MAX_VALUE} or {@code Integer.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + public static int saturatedMultiply(int a, int b) { + return Ints.saturatedCast((long) a * b); + } + + /** + * Returns the {@code b} to the {@code k}th power, unless it would overflow or underflow in which + * case {@code Integer.MAX_VALUE} or {@code Integer.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + public static int saturatedPow(int b, int k) { + checkNonNegative("exponent", k); + switch (b) { + case 0: + return (k == 0) ? 1 : 0; + case 1: + return 1; + case (-1): + return ((k & 1) == 0) ? 1 : -1; + case 2: + if (k >= Integer.SIZE - 1) { + return Integer.MAX_VALUE; + } + return 1 << k; + case (-2): + if (k >= Integer.SIZE) { + return Integer.MAX_VALUE + (k & 1); + } + return ((k & 1) == 0) ? 1 << k : -1 << k; + default: + // continue below to handle the general case + } + int accum = 1; + // if b is negative and k is odd then the limit is MIN otherwise the limit is MAX + int limit = Integer.MAX_VALUE + ((b >>> Integer.SIZE - 1) & (k & 1)); + while (true) { + switch (k) { + case 0: + return accum; + case 1: + return saturatedMultiply(accum, b); + default: + if ((k & 1) != 0) { + accum = saturatedMultiply(accum, b); + } + k >>= 1; + if (k > 0) { + if (-FLOOR_SQRT_MAX_INT > b | b > FLOOR_SQRT_MAX_INT) { + return limit; + } + b *= b; + } + } + } + } + + @VisibleForTesting static final int FLOOR_SQRT_MAX_INT = 46340; + + /** + * Returns {@code n!}, that is, the product of the first {@code n} positive integers, {@code 1} if + * {@code n == 0}, or {@link Integer#MAX_VALUE} if the result does not fit in a {@code int}. + * + * @throws IllegalArgumentException if {@code n < 0} + */ + public static int factorial(int n) { + checkNonNegative("n", n); + return (n < factorials.length) ? factorials[n] : Integer.MAX_VALUE; + } + + private static final int[] factorials = { + 1, + 1, + 1 * 2, + 1 * 2 * 3, + 1 * 2 * 3 * 4, + 1 * 2 * 3 * 4 * 5, + 1 * 2 * 3 * 4 * 5 * 6, + 1 * 2 * 3 * 4 * 5 * 6 * 7, + 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8, + 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9, + 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10, + 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11, + 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 + }; + + /** + * Returns {@code n} choose {@code k}, also known as the binomial coefficient of {@code n} and + * {@code k}, or {@link Integer#MAX_VALUE} if the result does not fit in an {@code int}. + * + * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0} or {@code k > n} + */ + public static int binomial(int n, int k) { + checkNonNegative("n", n); + checkNonNegative("k", k); + checkArgument(k <= n, "k (%s) > n (%s)", k, n); + if (k > (n >> 1)) { + k = n - k; + } + if (k >= biggestBinomials.length || n > biggestBinomials[k]) { + return Integer.MAX_VALUE; + } + switch (k) { + case 0: + return 1; + case 1: + return n; + default: + long result = 1; + for (int i = 0; i < k; i++) { + result *= n - i; + result /= i + 1; + } + return (int) result; + } + } + + // binomial(biggestBinomials[k], k) fits in an int, but not binomial(biggestBinomials[k]+1,k). + @VisibleForTesting + static int[] biggestBinomials = { + Integer.MAX_VALUE, + Integer.MAX_VALUE, + 65536, + 2345, + 477, + 193, + 110, + 75, + 58, + 49, + 43, + 39, + 37, + 35, + 34, + 34, + 33 + }; + + /** + * Returns the arithmetic mean of {@code x} and {@code y}, rounded towards negative infinity. This + * method is overflow resilient. + * + * @since 14.0 + */ + public static int mean(int x, int y) { + // Efficient method for computing the arithmetic mean. + // The alternative (x + y) / 2 fails for large values. + // The alternative (x + y) >>> 1 fails for negative values. + return (x & y) + ((x ^ y) >> 1); + } + + /** + * Returns {@code true} if {@code n} is a prime number: an integer greater + * than one that cannot be factored into a product of smaller positive integers. + * Returns {@code false} if {@code n} is zero, one, or a composite number (one which can be + * factored into smaller positive integers). + * + *

To test larger numbers, use {@link LongMath#isPrime} or {@link BigInteger#isProbablePrime}. + * + * @throws IllegalArgumentException if {@code n} is negative + * @since 20.0 + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static boolean isPrime(int n) { + return LongMath.isPrime(n); + } + + private IntMath() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/LinearTransformation.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/LinearTransformation.java new file mode 100644 index 0000000000000..b528acb376135 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/LinearTransformation.java @@ -0,0 +1,309 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import static java.lang.Double.NaN; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.math.DoubleUtils.isFinite; + +/** + * The representation of a linear transformation between real numbers {@code x} and {@code y}. + * Graphically, this is the specification of a straight line on a plane. The transformation can be + * expressed as {@code y = m * x + c} for finite {@code m} and {@code c}, unless it is a vertical + * transformation in which case {@code x} has a constant value for all {@code y}. In the + * non-vertical case, {@code m} is the slope of the transformation (and a horizontal transformation + * has zero slope). + * + * @author Pete Gillin + * @since 20.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class LinearTransformation { + + /** + * Start building an instance which maps {@code x = x1} to {@code y = y1}. Both arguments must be + * finite. Call either {@link LinearTransformationBuilder#and} or {@link + * LinearTransformationBuilder#withSlope} on the returned object to finish building the instance. + */ + public static LinearTransformationBuilder mapping(double x1, double y1) { + checkArgument(isFinite(x1) && isFinite(y1)); + return new LinearTransformationBuilder(x1, y1); + } + + /** + * This is an intermediate stage in the construction process. It is returned by {@link + * LinearTransformation#mapping}. You almost certainly don't want to keep instances around, but + * instead use method chaining. This represents a single point mapping, i.e. a mapping between one + * {@code x} and {@code y} value pair. + * + * @since 20.0 + */ + public static final class LinearTransformationBuilder { + + private final double x1; + private final double y1; + + private LinearTransformationBuilder(double x1, double y1) { + this.x1 = x1; + this.y1 = y1; + } + + /** + * Finish building an instance which also maps {@code x = x2} to {@code y = y2}. These values + * must not both be identical to the values given in the first mapping. If only the {@code x} + * values are identical, the transformation is vertical. If only the {@code y} values are + * identical, the transformation is horizontal (i.e. the slope is zero). + */ + public LinearTransformation and(double x2, double y2) { + checkArgument(isFinite(x2) && isFinite(y2)); + if (x2 == x1) { + checkArgument(y2 != y1); + return new VerticalLinearTransformation(x1); + } else { + return withSlope((y2 - y1) / (x2 - x1)); + } + } + + /** + * Finish building an instance with the given slope, i.e. the rate of change of {@code y} with + * respect to {@code x}. The slope must not be {@code NaN}. It may be infinite, in which case + * the transformation is vertical. (If it is zero, the transformation is horizontal.) + */ + public LinearTransformation withSlope(double slope) { + checkArgument(!Double.isNaN(slope)); + if (isFinite(slope)) { + double yIntercept = y1 - x1 * slope; + return new RegularLinearTransformation(slope, yIntercept); + } else { + return new VerticalLinearTransformation(x1); + } + } + } + + /** + * Builds an instance representing a vertical transformation with a constant value of {@code x}. + * (The inverse of this will be a horizontal transformation.) + */ + public static LinearTransformation vertical(double x) { + checkArgument(isFinite(x)); + return new VerticalLinearTransformation(x); + } + + /** + * Builds an instance representing a horizontal transformation with a constant value of {@code y}. + * (The inverse of this will be a vertical transformation.) + */ + public static LinearTransformation horizontal(double y) { + checkArgument(isFinite(y)); + double slope = 0.0; + return new RegularLinearTransformation(slope, y); + } + + /** + * Builds an instance for datasets which contains {@link Double#NaN}. The {@link #isHorizontal} + * and {@link #isVertical} methods return {@code false} and the {@link #slope}, and {@link + * #transform} methods all return {@link Double#NaN}. The {@link #inverse} method returns the same + * instance. + */ + public static LinearTransformation forNaN() { + return NaNLinearTransformation.INSTANCE; + } + + /** Returns whether this is a vertical transformation. */ + public abstract boolean isVertical(); + + /** Returns whether this is a horizontal transformation. */ + public abstract boolean isHorizontal(); + + /** + * Returns the slope of the transformation, i.e. the rate of change of {@code y} with respect to + * {@code x}. This must not be called on a vertical transformation (i.e. when {@link + * #isVertical()} is true). + */ + public abstract double slope(); + + /** + * Returns the {@code y} corresponding to the given {@code x}. This must not be called on a + * vertical transformation (i.e. when {@link #isVertical()} is true). + */ + public abstract double transform(double x); + + /** + * Returns the inverse linear transformation. The inverse of a horizontal transformation is a + * vertical transformation, and vice versa. The inverse of the {@link #forNaN} transformation is + * itself. In all other cases, the inverse is a transformation such that applying both the + * original transformation and its inverse to a value gives you the original value give-or-take + * numerical errors. Calling this method multiple times on the same instance will always return + * the same instance. Calling this method on the result of calling this method on an instance will + * always return that original instance. + */ + public abstract LinearTransformation inverse(); + + private static final class RegularLinearTransformation extends LinearTransformation { + + final double slope; + final double yIntercept; + + LinearTransformation inverse; + + RegularLinearTransformation(double slope, double yIntercept) { + this.slope = slope; + this.yIntercept = yIntercept; + this.inverse = null; // to be lazily initialized + } + + RegularLinearTransformation(double slope, double yIntercept, LinearTransformation inverse) { + this.slope = slope; + this.yIntercept = yIntercept; + this.inverse = inverse; + } + + @Override + public boolean isVertical() { + return false; + } + + @Override + public boolean isHorizontal() { + return (slope == 0.0); + } + + @Override + public double slope() { + return slope; + } + + @Override + public double transform(double x) { + return x * slope + yIntercept; + } + + @Override + public LinearTransformation inverse() { + LinearTransformation result = inverse; + return (result == null) ? inverse = createInverse() : result; + } + + @Override + public String toString() { + return String.format("y = %g * x + %g", slope, yIntercept); + } + + private LinearTransformation createInverse() { + if (slope != 0.0) { + return new RegularLinearTransformation(1.0 / slope, -1.0 * yIntercept / slope, this); + } else { + return new VerticalLinearTransformation(yIntercept, this); + } + } + } + + private static final class VerticalLinearTransformation extends LinearTransformation { + + final double x; + + LinearTransformation inverse; + + VerticalLinearTransformation(double x) { + this.x = x; + this.inverse = null; // to be lazily initialized + } + + VerticalLinearTransformation(double x, LinearTransformation inverse) { + this.x = x; + this.inverse = inverse; + } + + @Override + public boolean isVertical() { + return true; + } + + @Override + public boolean isHorizontal() { + return false; + } + + @Override + public double slope() { + throw new IllegalStateException(); + } + + @Override + public double transform(double x) { + throw new IllegalStateException(); + } + + @Override + public LinearTransformation inverse() { + LinearTransformation result = inverse; + return (result == null) ? inverse = createInverse() : result; + } + + @Override + public String toString() { + return String.format("x = %g", x); + } + + private LinearTransformation createInverse() { + return new RegularLinearTransformation(0.0, x, this); + } + } + + private static final class NaNLinearTransformation extends LinearTransformation { + + static final NaNLinearTransformation INSTANCE = new NaNLinearTransformation(); + + @Override + public boolean isVertical() { + return false; + } + + @Override + public boolean isHorizontal() { + return false; + } + + @Override + public double slope() { + return NaN; + } + + @Override + public double transform(double x) { + return NaN; + } + + @Override + public LinearTransformation inverse() { + return this; + } + + @Override + public String toString() { + return "NaN"; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/LongMath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/LongMath.java new file mode 100644 index 0000000000000..cb434aa7c951e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/LongMath.java @@ -0,0 +1,1367 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.primitives.Longs; +import org.apache.iotdb.google.common.primitives.UnsignedLongs; + +import java.math.BigInteger; +import java.math.RoundingMode; + +import static java.lang.Math.abs; +import static java.lang.Math.min; +import static java.math.RoundingMode.HALF_EVEN; +import static java.math.RoundingMode.HALF_UP; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkNoOverflow; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkNonNegative; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkPositive; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkRoundingUnnecessary; + +/** + * A class for arithmetic on values of type {@code long}. Where possible, methods are defined and + * named analogously to their {@code BigInteger} counterparts. + * + *

The implementations of many methods in this class are based on material from Henry S. Warren, + * Jr.'s Hacker's Delight, (Addison Wesley, 2002). + * + *

Similar functionality for {@code int} and for {@link BigInteger} can be found in {@link + * IntMath} and {@link BigIntegerMath} respectively. For other common operations on {@code long} + * values, see {@link org.apache.iotdb.google.common.primitives.Longs}. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class LongMath { + // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + + @VisibleForTesting static final long MAX_SIGNED_POWER_OF_TWO = 1L << (Long.SIZE - 2); + + /** + * Returns the smallest power of two greater than or equal to {@code x}. This is equivalent to + * {@code checkedPow(2, log2(x, CEILING))}. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException of the next-higher power of two is not representable as a {@code + * long}, i.e. when {@code x > 2^62} + * @since 20.0 + */ + public static long ceilingPowerOfTwo(long x) { + checkPositive("x", x); + if (x > MAX_SIGNED_POWER_OF_TWO) { + throw new ArithmeticException("ceilingPowerOfTwo(" + x + ") is not representable as a long"); + } + return 1L << -Long.numberOfLeadingZeros(x - 1); + } + + /** + * Returns the largest power of two less than or equal to {@code x}. This is equivalent to {@code + * checkedPow(2, log2(x, FLOOR))}. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @since 20.0 + */ + public static long floorPowerOfTwo(long x) { + checkPositive("x", x); + + // Long.highestOneBit was buggy on GWT. We've fixed it, but I'm not certain when the fix will + // be released. + return 1L << ((Long.SIZE - 1) - Long.numberOfLeadingZeros(x)); + } + + /** + * Returns {@code true} if {@code x} represents a power of two. + * + *

This differs from {@code Long.bitCount(x) == 1}, because {@code + * Long.bitCount(Long.MIN_VALUE) == 1}, but {@link Long#MIN_VALUE} is not a power of two. + */ + @SuppressWarnings("ShortCircuitBoolean") + public static boolean isPowerOfTwo(long x) { + return x > 0 & (x & (x - 1)) == 0; + } + + /** + * Returns 1 if {@code x < y} as unsigned longs, and 0 otherwise. Assumes that x - y fits into a + * signed long. The implementation is branch-free, and benchmarks suggest it is measurably faster + * than the straightforward ternary expression. + */ + @VisibleForTesting + static int lessThanBranchFree(long x, long y) { + // Returns the sign bit of x - y. + return (int) (~~(x - y) >>> (Long.SIZE - 1)); + } + + /** + * Returns the base-2 logarithm of {@code x}, rounded according to the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not a power of two + */ + @SuppressWarnings("fallthrough") + // TODO(kevinb): remove after this warning is disabled globally + public static int log2(long x, RoundingMode mode) { + checkPositive("x", x); + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(isPowerOfTwo(x)); + // fall through + case DOWN: + case FLOOR: + return (Long.SIZE - 1) - Long.numberOfLeadingZeros(x); + + case UP: + case CEILING: + return Long.SIZE - Long.numberOfLeadingZeros(x - 1); + + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + // Since sqrt(2) is irrational, log2(x) - logFloor cannot be exactly 0.5 + int leadingZeros = Long.numberOfLeadingZeros(x); + long cmp = MAX_POWER_OF_SQRT2_UNSIGNED >>> leadingZeros; + // floor(2^(logFloor + 0.5)) + int logFloor = (Long.SIZE - 1) - leadingZeros; + return logFloor + lessThanBranchFree(cmp, x); + } + throw new AssertionError("impossible"); + } + + /** The biggest half power of two that fits into an unsigned long */ + @VisibleForTesting static final long MAX_POWER_OF_SQRT2_UNSIGNED = 0xB504F333F9DE6484L; + + /** + * Returns the base-10 logarithm of {@code x}, rounded according to the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x <= 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not a power of ten + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("fallthrough") + // TODO(kevinb): remove after this warning is disabled globally + public static int log10(long x, RoundingMode mode) { + checkPositive("x", x); + int logFloor = log10Floor(x); + long floorPow = powersOf10[logFloor]; + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(x == floorPow); + // fall through + case FLOOR: + case DOWN: + return logFloor; + case CEILING: + case UP: + return logFloor + lessThanBranchFree(floorPow, x); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + // sqrt(10) is irrational, so log10(x)-logFloor is never exactly 0.5 + return logFloor + lessThanBranchFree(halfPowersOf10[logFloor], x); + } + throw new AssertionError(); + } + + @J2ktIncompatible + @GwtIncompatible // TODO + static int log10Floor(long x) { + /* + * Based on Hacker's Delight Fig. 11-5, the two-table-lookup, branch-free implementation. + * + * The key idea is that based on the number of leading zeros (equivalently, floor(log2(x))), we + * can narrow the possible floor(log10(x)) values to two. For example, if floor(log2(x)) is 6, + * then 64 <= x < 128, so floor(log10(x)) is either 1 or 2. + */ + int y = maxLog10ForLeadingZeros[Long.numberOfLeadingZeros(x)]; + /* + * y is the higher of the two possible values of floor(log10(x)). If x < 10^y, then we want the + * lower of the two possible values, or y - 1, otherwise, we want y. + */ + return y - lessThanBranchFree(x, powersOf10[y]); + } + + // maxLog10ForLeadingZeros[i] == floor(log10(2^(Long.SIZE - i))) + @VisibleForTesting + static final byte[] maxLog10ForLeadingZeros = { + 19, 18, 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, + 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, + 3, 2, 2, 2, 1, 1, 1, 0, 0, 0 + }; + + @J2ktIncompatible + @GwtIncompatible // TODO + @VisibleForTesting + static final long[] powersOf10 = { + 1L, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L, + 10000000000L, + 100000000000L, + 1000000000000L, + 10000000000000L, + 100000000000000L, + 1000000000000000L, + 10000000000000000L, + 100000000000000000L, + 1000000000000000000L + }; + + // halfPowersOf10[i] = largest long less than 10^(i + 0.5) + @J2ktIncompatible + @GwtIncompatible // TODO + @VisibleForTesting + static final long[] halfPowersOf10 = { + 3L, + 31L, + 316L, + 3162L, + 31622L, + 316227L, + 3162277L, + 31622776L, + 316227766L, + 3162277660L, + 31622776601L, + 316227766016L, + 3162277660168L, + 31622776601683L, + 316227766016837L, + 3162277660168379L, + 31622776601683793L, + 316227766016837933L, + 3162277660168379331L + }; + + /** + * Returns {@code b} to the {@code k}th power. Even if the result overflows, it will be equal to + * {@code BigInteger.valueOf(b).pow(k).longValue()}. This implementation runs in {@code O(log k)} + * time. + * + * @throws IllegalArgumentException if {@code k < 0} + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static long pow(long b, int k) { + checkNonNegative("exponent", k); + if (-2 <= b && b <= 2) { + switch ((int) b) { + case 0: + return (k == 0) ? 1 : 0; + case 1: + return 1; + case (-1): + return ((k & 1) == 0) ? 1 : -1; + case 2: + return (k < Long.SIZE) ? 1L << k : 0; + case (-2): + if (k < Long.SIZE) { + return ((k & 1) == 0) ? 1L << k : -(1L << k); + } else { + return 0; + } + default: + throw new AssertionError(); + } + } + for (long accum = 1; ; k >>= 1) { + switch (k) { + case 0: + return accum; + case 1: + return accum * b; + default: + accum *= ((k & 1) == 0) ? 1 : b; + b *= b; + } + } + } + + /** + * Returns the square root of {@code x}, rounded with the specified rounding mode. + * + * @throws IllegalArgumentException if {@code x < 0} + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code + * sqrt(x)} is not an integer + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static long sqrt(long x, RoundingMode mode) { + checkNonNegative("x", x); + if (fitsInInt(x)) { + return IntMath.sqrt((int) x, mode); + } + /* + * Let k be the true value of floor(sqrt(x)), so that + * + * k * k <= x < (k + 1) * (k + 1) + * (double) (k * k) <= (double) x <= (double) ((k + 1) * (k + 1)) + * since casting to double is nondecreasing. + * Note that the right-hand inequality is no longer strict. + * Math.sqrt(k * k) <= Math.sqrt(x) <= Math.sqrt((k + 1) * (k + 1)) + * since Math.sqrt is monotonic. + * (long) Math.sqrt(k * k) <= (long) Math.sqrt(x) <= (long) Math.sqrt((k + 1) * (k + 1)) + * since casting to long is monotonic + * k <= (long) Math.sqrt(x) <= k + 1 + * since (long) Math.sqrt(k * k) == k, as checked exhaustively in + * {@link LongMathTest#testSqrtOfPerfectSquareAsDoubleIsPerfect} + */ + long guess = (long) Math.sqrt((double) x); + // Note: guess is always <= FLOOR_SQRT_MAX_LONG. + long guessSquared = guess * guess; + // Note (2013-2-26): benchmarks indicate that, inscrutably enough, using if statements is + // faster here than using lessThanBranchFree. + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(guessSquared == x); + return guess; + case FLOOR: + case DOWN: + if (x < guessSquared) { + return guess - 1; + } + return guess; + case CEILING: + case UP: + if (x > guessSquared) { + return guess + 1; + } + return guess; + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + long sqrtFloor = guess - ((x < guessSquared) ? 1 : 0); + long halfSquare = sqrtFloor * sqrtFloor + sqrtFloor; + /* + * We wish to test whether or not x <= (sqrtFloor + 0.5)^2 = halfSquare + 0.25. Since both x + * and halfSquare are integers, this is equivalent to testing whether or not x <= + * halfSquare. (We have to deal with overflow, though.) + * + * If we treat halfSquare as an unsigned long, we know that + * sqrtFloor^2 <= x < (sqrtFloor + 1)^2 + * halfSquare - sqrtFloor <= x < halfSquare + sqrtFloor + 1 + * so |x - halfSquare| <= sqrtFloor. Therefore, it's safe to treat x - halfSquare as a + * signed long, so lessThanBranchFree is safe for use. + */ + return sqrtFloor + lessThanBranchFree(halfSquare, x); + } + throw new AssertionError(); + } + + /** + * Returns the result of dividing {@code p} by {@code q}, rounding using the specified {@code + * RoundingMode}. + * + * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} + * is not an integer multiple of {@code b} + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("fallthrough") + public static long divide(long p, long q, RoundingMode mode) { + checkNotNull(mode); + long div = p / q; // throws if q == 0 + long rem = p - q * div; // equals p % q + + if (rem == 0) { + return div; + } + + /* + * Normal Java division rounds towards 0, consistently with RoundingMode.DOWN. We just have to + * deal with the cases where rounding towards 0 is wrong, which typically depends on the sign of + * p / q. + * + * signum is 1 if p and q are both nonnegative or both negative, and -1 otherwise. + */ + int signum = 1 | (int) ((p ^ q) >> (Long.SIZE - 1)); + boolean increment; + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(rem == 0); + // fall through + case DOWN: + increment = false; + break; + case UP: + increment = true; + break; + case CEILING: + increment = signum > 0; + break; + case FLOOR: + increment = signum < 0; + break; + case HALF_EVEN: + case HALF_DOWN: + case HALF_UP: + long absRem = abs(rem); + long cmpRemToHalfDivisor = absRem - (abs(q) - absRem); + // subtracting two nonnegative longs can't overflow + // cmpRemToHalfDivisor has the same sign as compare(abs(rem), abs(q) / 2). + if (cmpRemToHalfDivisor == 0) { // exactly on the half mark + increment = (mode == HALF_UP || (mode == HALF_EVEN && (div & 1) != 0)); + } else { + increment = cmpRemToHalfDivisor > 0; // closer to the UP value + } + break; + default: + throw new AssertionError(); + } + return increment ? div + signum : div; + } + + /** + * Returns {@code x mod m}, a non-negative value less than {@code m}. This differs from {@code x % + * m}, which might be negative. + * + *

For example: + * + *

{@code
+   * mod(7, 4) == 3
+   * mod(-7, 4) == 1
+   * mod(-1, 4) == 3
+   * mod(-8, 4) == 0
+   * mod(8, 4) == 0
+   * }
+ * + * @throws ArithmeticException if {@code m <= 0} + * @see + * Remainder Operator + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static int mod(long x, int m) { + // Cast is safe because the result is guaranteed in the range [0, m) + return (int) mod(x, (long) m); + } + + /** + * Returns {@code x mod m}, a non-negative value less than {@code m}. This differs from {@code x % + * m}, which might be negative. + * + *

For example: + * + *

{@code
+   * mod(7, 4) == 3
+   * mod(-7, 4) == 1
+   * mod(-1, 4) == 3
+   * mod(-8, 4) == 0
+   * mod(8, 4) == 0
+   * }
+ * + * @throws ArithmeticException if {@code m <= 0} + * @see + * Remainder Operator + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static long mod(long x, long m) { + if (m <= 0) { + throw new ArithmeticException("Modulus must be positive"); + } + long result = x % m; + return (result >= 0) ? result : result + m; + } + + /** + * Returns the greatest common divisor of {@code a, b}. Returns {@code 0} if {@code a == 0 && b == + * 0}. + * + * @throws IllegalArgumentException if {@code a < 0} or {@code b < 0} + */ + public static long gcd(long a, long b) { + /* + * The reason we require both arguments to be >= 0 is because otherwise, what do you return on + * gcd(0, Long.MIN_VALUE)? BigInteger.gcd would return positive 2^63, but positive 2^63 isn't an + * int. + */ + checkNonNegative("a", a); + checkNonNegative("b", b); + if (a == 0) { + // 0 % b == 0, so b divides a, but the converse doesn't hold. + // BigInteger.gcd is consistent with this decision. + return b; + } else if (b == 0) { + return a; // similar logic + } + /* + * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm. This is + * >60% faster than the Euclidean algorithm in benchmarks. + */ + int aTwos = Long.numberOfTrailingZeros(a); + a >>= aTwos; // divide out all 2s + int bTwos = Long.numberOfTrailingZeros(b); + b >>= bTwos; // divide out all 2s + while (a != b) { // both a, b are odd + // The key to the binary GCD algorithm is as follows: + // Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b). + // But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two. + + // We bend over backwards to avoid branching, adapting a technique from + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax + + long delta = a - b; // can't overflow, since a and b are nonnegative + + long minDeltaOrZero = delta & (delta >> (Long.SIZE - 1)); + // equivalent to Math.min(delta, 0) + + a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b) + // a is now nonnegative and even + + b += minDeltaOrZero; // sets b to min(old a, b) + a >>= Long.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b + } + return a << min(aTwos, bTwos); + } + + /** + * Returns the sum of {@code a} and {@code b}, provided it does not overflow. + * + * @throws ArithmeticException if {@code a + b} overflows in signed {@code long} arithmetic + */ + @SuppressWarnings("ShortCircuitBoolean") + public static long checkedAdd(long a, long b) { + long result = a + b; + checkNoOverflow((a ^ b) < 0 | (a ^ result) >= 0, "checkedAdd", a, b); + return result; + } + + /** + * Returns the difference of {@code a} and {@code b}, provided it does not overflow. + * + * @throws ArithmeticException if {@code a - b} overflows in signed {@code long} arithmetic + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("ShortCircuitBoolean") + public static long checkedSubtract(long a, long b) { + long result = a - b; + checkNoOverflow((a ^ b) >= 0 | (a ^ result) >= 0, "checkedSubtract", a, b); + return result; + } + + /** + * Returns the product of {@code a} and {@code b}, provided it does not overflow. + * + * @throws ArithmeticException if {@code a * b} overflows in signed {@code long} arithmetic + */ + @SuppressWarnings("ShortCircuitBoolean") + public static long checkedMultiply(long a, long b) { + // Hacker's Delight, Section 2-12 + int leadingZeros = + Long.numberOfLeadingZeros(a) + + Long.numberOfLeadingZeros(~a) + + Long.numberOfLeadingZeros(b) + + Long.numberOfLeadingZeros(~b); + /* + * If leadingZeros > Long.SIZE + 1 it's definitely fine, if it's < Long.SIZE it's definitely + * bad. We do the leadingZeros check to avoid the division below if at all possible. + * + * Otherwise, if b == Long.MIN_VALUE, then the only allowed values of a are 0 and 1. We take + * care of all a < 0 with their own check, because in particular, the case a == -1 will + * incorrectly pass the division check below. + * + * In all other cases, we check that either a is 0 or the result is consistent with division. + */ + if (leadingZeros > Long.SIZE + 1) { + return a * b; + } + checkNoOverflow(leadingZeros >= Long.SIZE, "checkedMultiply", a, b); + checkNoOverflow(a >= 0 | b != Long.MIN_VALUE, "checkedMultiply", a, b); + long result = a * b; + checkNoOverflow(a == 0 || result / a == b, "checkedMultiply", a, b); + return result; + } + + /** + * Returns the {@code b} to the {@code k}th power, provided it does not overflow. + * + * @throws ArithmeticException if {@code b} to the {@code k}th power overflows in signed {@code + * long} arithmetic + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("ShortCircuitBoolean") + public static long checkedPow(long b, int k) { + checkNonNegative("exponent", k); + if (b >= -2 & b <= 2) { + switch ((int) b) { + case 0: + return (k == 0) ? 1 : 0; + case 1: + return 1; + case (-1): + return ((k & 1) == 0) ? 1 : -1; + case 2: + checkNoOverflow(k < Long.SIZE - 1, "checkedPow", b, k); + return 1L << k; + case (-2): + checkNoOverflow(k < Long.SIZE, "checkedPow", b, k); + return ((k & 1) == 0) ? (1L << k) : (-1L << k); + default: + throw new AssertionError(); + } + } + long accum = 1; + while (true) { + switch (k) { + case 0: + return accum; + case 1: + return checkedMultiply(accum, b); + default: + if ((k & 1) != 0) { + accum = checkedMultiply(accum, b); + } + k >>= 1; + if (k > 0) { + checkNoOverflow( + -FLOOR_SQRT_MAX_LONG <= b && b <= FLOOR_SQRT_MAX_LONG, "checkedPow", b, k); + b *= b; + } + } + } + } + + /** + * Returns the sum of {@code a} and {@code b} unless it would overflow or underflow in which case + * {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + @SuppressWarnings("ShortCircuitBoolean") + public static long saturatedAdd(long a, long b) { + long naiveSum = a + b; + if ((a ^ b) < 0 | (a ^ naiveSum) >= 0) { + // If a and b have different signs or a has the same sign as the result then there was no + // overflow, return. + return naiveSum; + } + // we did over/under flow, if the sign is negative we should return MAX otherwise MIN + return Long.MAX_VALUE + ((naiveSum >>> (Long.SIZE - 1)) ^ 1); + } + + /** + * Returns the difference of {@code a} and {@code b} unless it would overflow or underflow in + * which case {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + @SuppressWarnings("ShortCircuitBoolean") + public static long saturatedSubtract(long a, long b) { + long naiveDifference = a - b; + if ((a ^ b) >= 0 | (a ^ naiveDifference) >= 0) { + // If a and b have the same signs or a has the same sign as the result then there was no + // overflow, return. + return naiveDifference; + } + // we did over/under flow + return Long.MAX_VALUE + ((naiveDifference >>> (Long.SIZE - 1)) ^ 1); + } + + /** + * Returns the product of {@code a} and {@code b} unless it would overflow or underflow in which + * case {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + @SuppressWarnings("ShortCircuitBoolean") + public static long saturatedMultiply(long a, long b) { + // see checkedMultiply for explanation + int leadingZeros = + Long.numberOfLeadingZeros(a) + + Long.numberOfLeadingZeros(~a) + + Long.numberOfLeadingZeros(b) + + Long.numberOfLeadingZeros(~b); + if (leadingZeros > Long.SIZE + 1) { + return a * b; + } + // the return value if we will overflow (which we calculate by overflowing a long :) ) + long limit = Long.MAX_VALUE + ((a ^ b) >>> (Long.SIZE - 1)); + if (leadingZeros < Long.SIZE | (a < 0 & b == Long.MIN_VALUE)) { + // overflow + return limit; + } + long result = a * b; + if (a == 0 || result / a == b) { + return result; + } + return limit; + } + + /** + * Returns the {@code b} to the {@code k}th power, unless it would overflow or underflow in which + * case {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively. + * + * @since 20.0 + */ + @SuppressWarnings("ShortCircuitBoolean") + public static long saturatedPow(long b, int k) { + checkNonNegative("exponent", k); + if (b >= -2 & b <= 2) { + switch ((int) b) { + case 0: + return (k == 0) ? 1 : 0; + case 1: + return 1; + case (-1): + return ((k & 1) == 0) ? 1 : -1; + case 2: + if (k >= Long.SIZE - 1) { + return Long.MAX_VALUE; + } + return 1L << k; + case (-2): + if (k >= Long.SIZE) { + return Long.MAX_VALUE + (k & 1); + } + return ((k & 1) == 0) ? (1L << k) : (-1L << k); + default: + throw new AssertionError(); + } + } + long accum = 1; + // if b is negative and k is odd then the limit is MIN otherwise the limit is MAX + long limit = Long.MAX_VALUE + ((b >>> (Long.SIZE - 1)) & (k & 1)); + while (true) { + switch (k) { + case 0: + return accum; + case 1: + return saturatedMultiply(accum, b); + default: + if ((k & 1) != 0) { + accum = saturatedMultiply(accum, b); + } + k >>= 1; + if (k > 0) { + if (-FLOOR_SQRT_MAX_LONG > b | b > FLOOR_SQRT_MAX_LONG) { + return limit; + } + b *= b; + } + } + } + } + + @VisibleForTesting static final long FLOOR_SQRT_MAX_LONG = 3037000499L; + + /** + * Returns {@code n!}, that is, the product of the first {@code n} positive integers, {@code 1} if + * {@code n == 0}, or {@link Long#MAX_VALUE} if the result does not fit in a {@code long}. + * + * @throws IllegalArgumentException if {@code n < 0} + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static long factorial(int n) { + checkNonNegative("n", n); + return (n < factorials.length) ? factorials[n] : Long.MAX_VALUE; + } + + static final long[] factorials = { + 1L, + 1L, + 1L * 2, + 1L * 2 * 3, + 1L * 2 * 3 * 4, + 1L * 2 * 3 * 4 * 5, + 1L * 2 * 3 * 4 * 5 * 6, + 1L * 2 * 3 * 4 * 5 * 6 * 7, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19, + 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 + }; + + /** + * Returns {@code n} choose {@code k}, also known as the binomial coefficient of {@code n} and + * {@code k}, or {@link Long#MAX_VALUE} if the result does not fit in a {@code long}. + * + * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0}, or {@code k > n} + */ + public static long binomial(int n, int k) { + checkNonNegative("n", n); + checkNonNegative("k", k); + checkArgument(k <= n, "k (%s) > n (%s)", k, n); + if (k > (n >> 1)) { + k = n - k; + } + switch (k) { + case 0: + return 1; + case 1: + return n; + default: + if (n < factorials.length) { + return factorials[n] / (factorials[k] * factorials[n - k]); + } else if (k >= biggestBinomials.length || n > biggestBinomials[k]) { + return Long.MAX_VALUE; + } else if (k < biggestSimpleBinomials.length && n <= biggestSimpleBinomials[k]) { + // guaranteed not to overflow + long result = n--; + for (int i = 2; i <= k; n--, i++) { + result *= n; + result /= i; + } + return result; + } else { + int nBits = LongMath.log2(n, RoundingMode.CEILING); + + long result = 1; + long numerator = n--; + long denominator = 1; + + int numeratorBits = nBits; + // This is an upper bound on log2(numerator, ceiling). + + /* + * We want to do this in long math for speed, but want to avoid overflow. We adapt the + * technique previously used by BigIntegerMath: maintain separate numerator and + * denominator accumulators, multiplying the fraction into result when near overflow. + */ + for (int i = 2; i <= k; i++, n--) { + if (numeratorBits + nBits < Long.SIZE - 1) { + // It's definitely safe to multiply into numerator and denominator. + numerator *= n; + denominator *= i; + numeratorBits += nBits; + } else { + // It might not be safe to multiply into numerator and denominator, + // so multiply (numerator / denominator) into result. + result = multiplyFraction(result, numerator, denominator); + numerator = n; + denominator = i; + numeratorBits = nBits; + } + } + return multiplyFraction(result, numerator, denominator); + } + } + } + + /** Returns (x * numerator / denominator), which is assumed to come out to an integral value. */ + static long multiplyFraction(long x, long numerator, long denominator) { + if (x == 1) { + return numerator / denominator; + } + long commonDivisor = gcd(x, denominator); + x /= commonDivisor; + denominator /= commonDivisor; + // We know gcd(x, denominator) = 1, and x * numerator / denominator is exact, + // so denominator must be a divisor of numerator. + return x * (numerator / denominator); + } + + /* + * binomial(biggestBinomials[k], k) fits in a long, but not binomial(biggestBinomials[k] + 1, k). + */ + static final int[] biggestBinomials = { + Integer.MAX_VALUE, + Integer.MAX_VALUE, + Integer.MAX_VALUE, + 3810779, + 121977, + 16175, + 4337, + 1733, + 887, + 534, + 361, + 265, + 206, + 169, + 143, + 125, + 111, + 101, + 94, + 88, + 83, + 79, + 76, + 74, + 72, + 70, + 69, + 68, + 67, + 67, + 66, + 66, + 66, + 66 + }; + + /* + * binomial(biggestSimpleBinomials[k], k) doesn't need to use the slower GCD-based impl, but + * binomial(biggestSimpleBinomials[k] + 1, k) does. + */ + @VisibleForTesting + static final int[] biggestSimpleBinomials = { + Integer.MAX_VALUE, + Integer.MAX_VALUE, + Integer.MAX_VALUE, + 2642246, + 86251, + 11724, + 3218, + 1313, + 684, + 419, + 287, + 214, + 169, + 139, + 119, + 105, + 95, + 87, + 81, + 76, + 73, + 70, + 68, + 66, + 64, + 63, + 62, + 62, + 61, + 61, + 61 + }; + + // These values were generated by using checkedMultiply to see when the simple multiply/divide + // algorithm would lead to an overflow. + + static boolean fitsInInt(long x) { + return (int) x == x; + } + + /** + * Returns the arithmetic mean of {@code x} and {@code y}, rounded toward negative infinity. This + * method is resilient to overflow. + * + * @since 14.0 + */ + public static long mean(long x, long y) { + // Efficient method for computing the arithmetic mean. + // The alternative (x + y) / 2 fails for large values. + // The alternative (x + y) >>> 1 fails for negative values. + return (x & y) + ((x ^ y) >> 1); + } + + /* + * This bitmask is used as an optimization for cheaply testing for divisibility by 2, 3, or 5. + * Each bit is set to 1 for all remainders that indicate divisibility by 2, 3, or 5, so + * 1, 7, 11, 13, 17, 19, 23, 29 are set to 0. 30 and up don't matter because they won't be hit. + */ + private static final int SIEVE_30 = + ~((1 << 1) | (1 << 7) | (1 << 11) | (1 << 13) | (1 << 17) | (1 << 19) | (1 << 23) + | (1 << 29)); + + /** + * Returns {@code true} if {@code n} is a prime number: an integer greater + * than one that cannot be factored into a product of smaller positive integers. + * Returns {@code false} if {@code n} is zero, one, or a composite number (one which can be + * factored into smaller positive integers). + * + *

To test larger numbers, use {@link BigInteger#isProbablePrime}. + * + * @throws IllegalArgumentException if {@code n} is negative + * @since 20.0 + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static boolean isPrime(long n) { + if (n < 2) { + checkNonNegative("n", n); + return false; + } + if (n < 66) { + // Encode all primes less than 66 into mask without 0 and 1. + long mask = + (1L << (2 - 2)) + | (1L << (3 - 2)) + | (1L << (5 - 2)) + | (1L << (7 - 2)) + | (1L << (11 - 2)) + | (1L << (13 - 2)) + | (1L << (17 - 2)) + | (1L << (19 - 2)) + | (1L << (23 - 2)) + | (1L << (29 - 2)) + | (1L << (31 - 2)) + | (1L << (37 - 2)) + | (1L << (41 - 2)) + | (1L << (43 - 2)) + | (1L << (47 - 2)) + | (1L << (53 - 2)) + | (1L << (59 - 2)) + | (1L << (61 - 2)); + // Look up n within the mask. + return ((mask >> ((int) n - 2)) & 1) != 0; + } + + if ((SIEVE_30 & (1 << (n % 30))) != 0) { + return false; + } + if (n % 7 == 0 || n % 11 == 0 || n % 13 == 0) { + return false; + } + if (n < 17 * 17) { + return true; + } + + for (long[] baseSet : millerRabinBaseSets) { + if (n <= baseSet[0]) { + for (int i = 1; i < baseSet.length; i++) { + if (!MillerRabinTester.test(baseSet[i], n)) { + return false; + } + } + return true; + } + } + throw new AssertionError(); + } + + /* + * If n <= millerRabinBases[i][0], then testing n against bases millerRabinBases[i][1..] suffices + * to prove its primality. Values from miller-rabin.appspot.com. + * + * NOTE: We could get slightly better bases that would be treated as unsigned, but benchmarks + * showed negligible performance improvements. + */ + private static final long[][] millerRabinBaseSets = { + {291830, 126401071349994536L}, + {885594168, 725270293939359937L, 3569819667048198375L}, + {273919523040L, 15, 7363882082L, 992620450144556L}, + {47636622961200L, 2, 2570940, 211991001, 3749873356L}, + { + 7999252175582850L, + 2, + 4130806001517L, + 149795463772692060L, + 186635894390467037L, + 3967304179347715805L + }, + { + 585226005592931976L, + 2, + 123635709730000L, + 9233062284813009L, + 43835965440333360L, + 761179012939631437L, + 1263739024124850375L + }, + {Long.MAX_VALUE, 2, 325, 9375, 28178, 450775, 9780504, 1795265022} + }; + + private enum MillerRabinTester { + /** Works for inputs ≤ FLOOR_SQRT_MAX_LONG. */ + SMALL { + @Override + long mulMod(long a, long b, long m) { + /* + * lowasser, 2015-Feb-12: Benchmarks suggest that changing this to UnsignedLongs.remainder + * and increasing the threshold to 2^32 doesn't pay for itself, and adding another enum + * constant hurts performance further -- I suspect because bimorphic implementation is a + * sweet spot for the JVM. + */ + return (a * b) % m; + } + + @Override + long squareMod(long a, long m) { + return (a * a) % m; + } + }, + /** Works for all nonnegative signed longs. */ + LARGE { + /** Returns (a + b) mod m. Precondition: {@code 0 <= a}, {@code b < m < 2^63}. */ + private long plusMod(long a, long b, long m) { + return (a >= m - b) ? (a + b - m) : (a + b); + } + + /** Returns (a * 2^32) mod m. a may be any unsigned long. */ + private long times2ToThe32Mod(long a, long m) { + int remainingPowersOf2 = 32; + do { + int shift = min(remainingPowersOf2, Long.numberOfLeadingZeros(a)); + // shift is either the number of powers of 2 left to multiply a by, or the biggest shift + // possible while keeping a in an unsigned long. + a = UnsignedLongs.remainder(a << shift, m); + remainingPowersOf2 -= shift; + } while (remainingPowersOf2 > 0); + return a; + } + + @Override + long mulMod(long a, long b, long m) { + long aHi = a >>> 32; // < 2^31 + long bHi = b >>> 32; // < 2^31 + long aLo = a & 0xFFFFFFFFL; // < 2^32 + long bLo = b & 0xFFFFFFFFL; // < 2^32 + + /* + * a * b == aHi * bHi * 2^64 + (aHi * bLo + aLo * bHi) * 2^32 + aLo * bLo. + * == (aHi * bHi * 2^32 + aHi * bLo + aLo * bHi) * 2^32 + aLo * bLo + * + * We carry out this computation in modular arithmetic. Since times2ToThe32Mod accepts any + * unsigned long, we don't have to do a mod on every operation, only when intermediate + * results can exceed 2^63. + */ + long result = times2ToThe32Mod(aHi * bHi /* < 2^62 */, m); // < m < 2^63 + result += aHi * bLo; // aHi * bLo < 2^63, result < 2^64 + if (result < 0) { + result = UnsignedLongs.remainder(result, m); + } + // result < 2^63 again + result += aLo * bHi; // aLo * bHi < 2^63, result < 2^64 + result = times2ToThe32Mod(result, m); // result < m < 2^63 + return plusMod(result, UnsignedLongs.remainder(aLo * bLo /* < 2^64 */, m), m); + } + + @Override + long squareMod(long a, long m) { + long aHi = a >>> 32; // < 2^31 + long aLo = a & 0xFFFFFFFFL; // < 2^32 + + /* + * a^2 == aHi^2 * 2^64 + aHi * aLo * 2^33 + aLo^2 + * == (aHi^2 * 2^32 + aHi * aLo * 2) * 2^32 + aLo^2 + * We carry out this computation in modular arithmetic. Since times2ToThe32Mod accepts any + * unsigned long, we don't have to do a mod on every operation, only when intermediate + * results can exceed 2^63. + */ + long result = times2ToThe32Mod(aHi * aHi /* < 2^62 */, m); // < m < 2^63 + long hiLo = aHi * aLo * 2; + if (hiLo < 0) { + hiLo = UnsignedLongs.remainder(hiLo, m); + } + // hiLo < 2^63 + result += hiLo; // result < 2^64 + result = times2ToThe32Mod(result, m); // result < m < 2^63 + return plusMod(result, UnsignedLongs.remainder(aLo * aLo /* < 2^64 */, m), m); + } + }; + + static boolean test(long base, long n) { + // Since base will be considered % n, it's okay if base > FLOOR_SQRT_MAX_LONG, + // so long as n <= FLOOR_SQRT_MAX_LONG. + return ((n <= FLOOR_SQRT_MAX_LONG) ? SMALL : LARGE).testWitness(base, n); + } + + /** Returns a * b mod m. */ + abstract long mulMod(long a, long b, long m); + + /** Returns a^2 mod m. */ + abstract long squareMod(long a, long m); + + /** Returns a^p mod m. */ + private long powMod(long a, long p, long m) { + long res = 1; + for (; p != 0; p >>= 1) { + if ((p & 1) != 0) { + res = mulMod(res, a, m); + } + a = squareMod(a, m); + } + return res; + } + + /** Returns true if n is a strong probable prime relative to the specified base. */ + private boolean testWitness(long base, long n) { + int r = Long.numberOfTrailingZeros(n - 1); + long d = (n - 1) >> r; + base %= n; + if (base == 0) { + return true; + } + // Calculate a := base^d mod n. + long a = powMod(base, d, n); + // n passes this test if + // base^d = 1 (mod n) + // or base^(2^j * d) = -1 (mod n) for some 0 <= j < r. + if (a == 1) { + return true; + } + int j = 0; + while (a != n - 1) { + if (++j == r) { + return false; + } + a = squareMod(a, n); + } + return true; + } + } + + /** + * Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x} + * is precisely representable as a {@code double}, its {@code double} value will be returned; + * otherwise, the rounding will choose between the two nearest representable values with {@code + * mode}. + * + *

For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754 + * default rounding mode: if the two nearest representable values are equally near, the one with + * the least significant bit zero is chosen. (In such cases, both of the nearest representable + * values are even integers; this method returns the one that is a multiple of a greater power of + * two.) + * + * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} + * is not precisely representable as a {@code double} + * @since 30.0 + */ + @SuppressWarnings("deprecation") + @J2ktIncompatible + @GwtIncompatible + public static double roundToDouble(long x, RoundingMode mode) { + // Logic adapted from ToDoubleRounder. + double roundArbitrarily = (double) x; + long roundArbitrarilyAsLong = (long) roundArbitrarily; + int cmpXToRoundArbitrarily; + + if (roundArbitrarilyAsLong == Long.MAX_VALUE) { + /* + * For most values, the conversion from roundArbitrarily to roundArbitrarilyAsLong is + * lossless. In that case we can compare x to roundArbitrarily using Longs.compare(x, + * roundArbitrarilyAsLong). The exception is for values where the conversion to double rounds + * up to give roundArbitrarily equal to 2^63, so the conversion back to long overflows and + * roundArbitrarilyAsLong is Long.MAX_VALUE. (This is the only way this condition can occur as + * otherwise the conversion back to long pads with zero bits.) In this case we know that + * roundArbitrarily > x. (This is important when x == Long.MAX_VALUE == + * roundArbitrarilyAsLong.) + */ + cmpXToRoundArbitrarily = -1; + } else { + cmpXToRoundArbitrarily = Longs.compare(x, roundArbitrarilyAsLong); + } + + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(cmpXToRoundArbitrarily == 0); + return roundArbitrarily; + case FLOOR: + return (cmpXToRoundArbitrarily >= 0) + ? roundArbitrarily + : DoubleUtils.nextDown(roundArbitrarily); + case CEILING: + return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily); + case DOWN: + if (x >= 0) { + return (cmpXToRoundArbitrarily >= 0) + ? roundArbitrarily + : DoubleUtils.nextDown(roundArbitrarily); + } else { + return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily); + } + case UP: + if (x >= 0) { + return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily); + } else { + return (cmpXToRoundArbitrarily >= 0) + ? roundArbitrarily + : DoubleUtils.nextDown(roundArbitrarily); + } + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + { + long roundFloor; + double roundFloorAsDouble; + long roundCeiling; + double roundCeilingAsDouble; + + if (cmpXToRoundArbitrarily >= 0) { + roundFloorAsDouble = roundArbitrarily; + roundFloor = roundArbitrarilyAsLong; + roundCeilingAsDouble = Math.nextUp(roundArbitrarily); + roundCeiling = (long) Math.ceil(roundCeilingAsDouble); + } else { + roundCeilingAsDouble = roundArbitrarily; + roundCeiling = roundArbitrarilyAsLong; + roundFloorAsDouble = DoubleUtils.nextDown(roundArbitrarily); + roundFloor = (long) Math.floor(roundFloorAsDouble); + } + + long deltaToFloor = x - roundFloor; + long deltaToCeiling = roundCeiling - x; + + if (roundCeiling == Long.MAX_VALUE) { + // correct for Long.MAX_VALUE as discussed above: roundCeilingAsDouble must be 2^63, but + // roundCeiling is 2^63-1. + deltaToCeiling++; + } + + int diff = Longs.compare(deltaToFloor, deltaToCeiling); + if (diff < 0) { // closer to floor + return roundFloorAsDouble; + } else if (diff > 0) { // closer to ceiling + return roundCeilingAsDouble; + } + // halfway between the representable values; do the half-whatever logic + switch (mode) { + case HALF_EVEN: + return ((DoubleUtils.getSignificand(roundFloorAsDouble) & 1L) == 0) + ? roundFloorAsDouble + : roundCeilingAsDouble; + case HALF_DOWN: + return (x >= 0) ? roundFloorAsDouble : roundCeilingAsDouble; + case HALF_UP: + return (x >= 0) ? roundCeilingAsDouble : roundFloorAsDouble; + default: + throw new AssertionError("impossible"); + } + } + } + throw new AssertionError("impossible"); + } + + private LongMath() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/MathPreconditions.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/MathPreconditions.java new file mode 100644 index 0000000000000..db64dff629944 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/MathPreconditions.java @@ -0,0 +1,110 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.math.BigInteger; +import java.math.RoundingMode; + +/** + * A collection of preconditions for math functions. + * + * @author Louis Wasserman + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class MathPreconditions { + static int checkPositive(String role, int x) { + if (x <= 0) { + throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); + } + return x; + } + + static long checkPositive(String role, long x) { + if (x <= 0) { + throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); + } + return x; + } + + static BigInteger checkPositive(String role, BigInteger x) { + if (x.signum() <= 0) { + throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); + } + return x; + } + + static int checkNonNegative(String role, int x) { + if (x < 0) { + throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); + } + return x; + } + + static long checkNonNegative(String role, long x) { + if (x < 0) { + throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); + } + return x; + } + + static BigInteger checkNonNegative(String role, BigInteger x) { + if (x.signum() < 0) { + throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); + } + return x; + } + + static double checkNonNegative(String role, double x) { + if (!(x >= 0)) { // not x < 0, to work with NaN. + throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); + } + return x; + } + + static void checkRoundingUnnecessary(boolean condition) { + if (!condition) { + throw new ArithmeticException("mode was UNNECESSARY, but rounding was necessary"); + } + } + + static void checkInRangeForRoundingInputs(boolean condition, double input, RoundingMode mode) { + if (!condition) { + throw new ArithmeticException( + "rounded value is out of range for input " + input + " and rounding mode " + mode); + } + } + + static void checkNoOverflow(boolean condition, String methodName, int a, int b) { + if (!condition) { + throw new ArithmeticException("overflow: " + methodName + "(" + a + ", " + b + ")"); + } + } + + static void checkNoOverflow(boolean condition, String methodName, long a, long b) { + if (!condition) { + throw new ArithmeticException("overflow: " + methodName + "(" + a + ", " + b + ")"); + } + } + + private MathPreconditions() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/PairedStats.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/PairedStats.java new file mode 100644 index 0000000000000..391d625b62575 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/PairedStats.java @@ -0,0 +1,325 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Objects; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static java.lang.Double.NaN; +import static java.lang.Double.doubleToLongBits; +import static java.lang.Double.isNaN; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An immutable value object capturing some basic statistics about a collection of paired double + * values (e.g. points on a plane). Build instances with {@link PairedStatsAccumulator#snapshot}. + * + * @author Pete Gillin + * @since 20.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class PairedStats implements Serializable { + + private final Stats xStats; + private final Stats yStats; + private final double sumOfProductsOfDeltas; + + /** + * Internal constructor. Users should use {@link PairedStatsAccumulator#snapshot}. + * + *

To ensure that the created instance obeys its contract, the parameters should satisfy the + * following constraints. This is the callers responsibility and is not enforced here. + * + *

    + *
  • Both {@code xStats} and {@code yStats} must have the same {@code count}. + *
  • If that {@code count} is 1, {@code sumOfProductsOfDeltas} must be exactly 0.0. + *
  • If that {@code count} is more than 1, {@code sumOfProductsOfDeltas} must be finite. + *
+ */ + PairedStats(Stats xStats, Stats yStats, double sumOfProductsOfDeltas) { + this.xStats = xStats; + this.yStats = yStats; + this.sumOfProductsOfDeltas = sumOfProductsOfDeltas; + } + + /** Returns the number of pairs in the dataset. */ + public long count() { + return xStats.count(); + } + + /** Returns the statistics on the {@code x} values alone. */ + public Stats xStats() { + return xStats; + } + + /** Returns the statistics on the {@code y} values alone. */ + public Stats yStats() { + return yStats; + } + + /** + * Returns the population covariance of the values. The count must be non-zero. + * + *

This is guaranteed to return zero if the dataset contains a single pair of finite values. It + * is not guaranteed to return zero when the dataset consists of the same pair of values multiple + * times, due to numerical errors. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double populationCovariance() { + checkState(count() != 0); + return sumOfProductsOfDeltas / count(); + } + + /** + * Returns the sample covariance of the values. The count must be greater than one. + * + *

This is not guaranteed to return zero when the dataset consists of the same pair of values + * multiple times, due to numerical errors. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single pair of values + */ + public double sampleCovariance() { + checkState(count() > 1); + return sumOfProductsOfDeltas / (count() - 1); + } + + /** + * Returns the Pearson's or + * product-moment correlation coefficient of the values. The count must greater than one, and + * the {@code x} and {@code y} values must both have non-zero population variance (i.e. {@code + * xStats().populationVariance() > 0.0 && yStats().populationVariance() > 0.0}). The result is not + * guaranteed to be exactly +/-1 even when the data are perfectly (anti-)correlated, due to + * numerical errors. However, it is guaranteed to be in the inclusive range [-1, +1]. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single pair of values, or + * either the {@code x} and {@code y} dataset has zero population variance + */ + public double pearsonsCorrelationCoefficient() { + checkState(count() > 1); + if (isNaN(sumOfProductsOfDeltas)) { + return NaN; + } + double xSumOfSquaresOfDeltas = xStats().sumOfSquaresOfDeltas(); + double ySumOfSquaresOfDeltas = yStats().sumOfSquaresOfDeltas(); + checkState(xSumOfSquaresOfDeltas > 0.0); + checkState(ySumOfSquaresOfDeltas > 0.0); + // The product of two positive numbers can be zero if the multiplication underflowed. We + // force a positive value by effectively rounding up to MIN_VALUE. + double productOfSumsOfSquaresOfDeltas = + ensurePositive(xSumOfSquaresOfDeltas * ySumOfSquaresOfDeltas); + return ensureInUnitRange(sumOfProductsOfDeltas / Math.sqrt(productOfSumsOfSquaresOfDeltas)); + } + + /** + * Returns a linear transformation giving the best fit to the data according to Ordinary Least Squares linear + * regression of {@code y} as a function of {@code x}. The count must be greater than one, and + * either the {@code x} or {@code y} data must have a non-zero population variance (i.e. {@code + * xStats().populationVariance() > 0.0 || yStats().populationVariance() > 0.0}). The result is + * guaranteed to be horizontal if there is variance in the {@code x} data but not the {@code y} + * data, and vertical if there is variance in the {@code y} data but not the {@code x} data. + * + *

This fit minimizes the root-mean-square error in {@code y} as a function of {@code x}. This + * error is defined as the square root of the mean of the squares of the differences between the + * actual {@code y} values of the data and the values predicted by the fit for the {@code x} + * values (i.e. it is the square root of the mean of the squares of the vertical distances between + * the data points and the best fit line). For this fit, this error is a fraction {@code sqrt(1 - + * R*R)} of the population standard deviation of {@code y}, where {@code R} is the Pearson's + * correlation coefficient (as given by {@link #pearsonsCorrelationCoefficient()}). + * + *

The corresponding root-mean-square error in {@code x} as a function of {@code y} is a + * fraction {@code sqrt(1/(R*R) - 1)} of the population standard deviation of {@code x}. This fit + * does not normally minimize that error: to do that, you should swap the roles of {@code x} and + * {@code y}. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link + * LinearTransformation#forNaN()}. + * + * @throws IllegalStateException if the dataset is empty or contains a single pair of values, or + * both the {@code x} and {@code y} dataset must have zero population variance + */ + public LinearTransformation leastSquaresFit() { + checkState(count() > 1); + if (isNaN(sumOfProductsOfDeltas)) { + return LinearTransformation.forNaN(); + } + double xSumOfSquaresOfDeltas = xStats.sumOfSquaresOfDeltas(); + if (xSumOfSquaresOfDeltas > 0.0) { + if (yStats.sumOfSquaresOfDeltas() > 0.0) { + return LinearTransformation.mapping(xStats.mean(), yStats.mean()) + .withSlope(sumOfProductsOfDeltas / xSumOfSquaresOfDeltas); + } else { + return LinearTransformation.horizontal(yStats.mean()); + } + } else { + checkState(yStats.sumOfSquaresOfDeltas() > 0.0); + return LinearTransformation.vertical(xStats.mean()); + } + } + + /** + * {@inheritDoc} + * + *

Note: This tests exact equality of the calculated statistics, including the floating + * point values. Two instances are guaranteed to be considered equal if one is copied from the + * other using {@code second = new PairedStatsAccumulator().addAll(first).snapshot()}, if both + * were obtained by calling {@code snapshot()} on the same {@link PairedStatsAccumulator} without + * adding any values in between the two calls, or if one is obtained from the other after + * round-tripping through java serialization. However, floating point rounding errors mean that it + * may be false for some instances where the statistics are mathematically equal, including + * instances constructed from the same values in a different order... or (in the general case) + * even in the same order. (It is guaranteed to return true for instances constructed from the + * same values in the same order if {@code strictfp} is in effect, or if the system architecture + * guarantees {@code strictfp}-like semantics.) + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PairedStats other = (PairedStats) obj; + return xStats.equals(other.xStats) + && yStats.equals(other.yStats) + && doubleToLongBits(sumOfProductsOfDeltas) == doubleToLongBits(other.sumOfProductsOfDeltas); + } + + /** + * {@inheritDoc} + * + *

Note: This hash code is consistent with exact equality of the calculated statistics, + * including the floating point values. See the note on {@link #equals} for details. + */ + @Override + public int hashCode() { + return Objects.hashCode(xStats, yStats, sumOfProductsOfDeltas); + } + + @Override + public String toString() { + if (count() > 0) { + return MoreObjects.toStringHelper(this) + .add("xStats", xStats) + .add("yStats", yStats) + .add("populationCovariance", populationCovariance()) + .toString(); + } else { + return MoreObjects.toStringHelper(this) + .add("xStats", xStats) + .add("yStats", yStats) + .toString(); + } + } + + double sumOfProductsOfDeltas() { + return sumOfProductsOfDeltas; + } + + private static double ensurePositive(double value) { + if (value > 0.0) { + return value; + } else { + return Double.MIN_VALUE; + } + } + + private static double ensureInUnitRange(double value) { + if (value >= 1.0) { + return 1.0; + } + if (value <= -1.0) { + return -1.0; + } + return value; + } + + // Serialization helpers + + /** The size of byte array representation in bytes. */ + private static final int BYTES = Stats.BYTES * 2 + Double.SIZE / Byte.SIZE; + + /** + * Gets a byte array representation of this instance. + * + *

Note: No guarantees are made regarding stability of the representation between + * versions. + */ + public byte[] toByteArray() { + ByteBuffer buffer = ByteBuffer.allocate(BYTES).order(ByteOrder.LITTLE_ENDIAN); + xStats.writeTo(buffer); + yStats.writeTo(buffer); + buffer.putDouble(sumOfProductsOfDeltas); + return buffer.array(); + } + + /** + * Creates a {@link PairedStats} instance from the given byte representation which was obtained by + * {@link #toByteArray}. + * + *

Note: No guarantees are made regarding stability of the representation between + * versions. + */ + public static PairedStats fromByteArray(byte[] byteArray) { + checkNotNull(byteArray); + checkArgument( + byteArray.length == BYTES, + "Expected PairedStats.BYTES = %s, got %s", + BYTES, + byteArray.length); + ByteBuffer buffer = ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN); + Stats xStats = Stats.readFrom(buffer); + Stats yStats = Stats.readFrom(buffer); + double sumOfProductsOfDeltas = buffer.getDouble(); + return new PairedStats(xStats, yStats, sumOfProductsOfDeltas); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/PairedStatsAccumulator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/PairedStatsAccumulator.java new file mode 100644 index 0000000000000..751b45d088717 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/PairedStatsAccumulator.java @@ -0,0 +1,248 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.Doubles; + +import static java.lang.Double.NaN; +import static java.lang.Double.isNaN; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.primitives.Doubles.isFinite; + +/** + * A mutable object which accumulates paired double values (e.g. points on a plane) and tracks some + * basic statistics over all the values added so far. This class is not thread safe. + * + * @author Pete Gillin + * @since 20.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class PairedStatsAccumulator { + + // These fields must satisfy the requirements of PairedStats' constructor as well as those of the + // stat methods of this class. + private final StatsAccumulator xStats = new StatsAccumulator(); + private final StatsAccumulator yStats = new StatsAccumulator(); + private double sumOfProductsOfDeltas = 0.0; + + /** Adds the given pair of values to the dataset. */ + public void add(double x, double y) { + // We extend the recursive expression for the one-variable case at Art of Computer Programming + // vol. 2, Knuth, 4.2.2, (16) to the two-variable case. We have two value series x_i and y_i. + // We define the arithmetic means X_n = 1/n \sum_{i=1}^n x_i, and Y_n = 1/n \sum_{i=1}^n y_i. + // We also define the sum of the products of the differences from the means + // C_n = \sum_{i=1}^n x_i y_i - n X_n Y_n + // for all n >= 1. Then for all n > 1: + // C_{n-1} = \sum_{i=1}^{n-1} x_i y_i - (n-1) X_{n-1} Y_{n-1} + // C_n - C_{n-1} = x_n y_n - n X_n Y_n + (n-1) X_{n-1} Y_{n-1} + // = x_n y_n - X_n [ y_n + (n-1) Y_{n-1} ] + [ n X_n - x_n ] Y_{n-1} + // = x_n y_n - X_n y_n - x_n Y_{n-1} + X_n Y_{n-1} + // = (x_n - X_n) (y_n - Y_{n-1}) + xStats.add(x); + if (isFinite(x) && isFinite(y)) { + if (xStats.count() > 1) { + sumOfProductsOfDeltas += (x - xStats.mean()) * (y - yStats.mean()); + } + } else { + sumOfProductsOfDeltas = NaN; + } + yStats.add(y); + } + + /** + * Adds the given statistics to the dataset, as if the individual values used to compute the + * statistics had been added directly. + */ + public void addAll(PairedStats values) { + if (values.count() == 0) { + return; + } + + xStats.addAll(values.xStats()); + if (yStats.count() == 0) { + sumOfProductsOfDeltas = values.sumOfProductsOfDeltas(); + } else { + // This is a generalized version of the calculation in add(double, double) above. Note that + // non-finite inputs will have sumOfProductsOfDeltas = NaN, so non-finite values will result + // in NaN naturally. + sumOfProductsOfDeltas += + values.sumOfProductsOfDeltas() + + (values.xStats().mean() - xStats.mean()) + * (values.yStats().mean() - yStats.mean()) + * values.count(); + } + yStats.addAll(values.yStats()); + } + + /** Returns an immutable snapshot of the current statistics. */ + public PairedStats snapshot() { + return new PairedStats(xStats.snapshot(), yStats.snapshot(), sumOfProductsOfDeltas); + } + + /** Returns the number of pairs in the dataset. */ + public long count() { + return xStats.count(); + } + + /** Returns an immutable snapshot of the statistics on the {@code x} values alone. */ + public Stats xStats() { + return xStats.snapshot(); + } + + /** Returns an immutable snapshot of the statistics on the {@code y} values alone. */ + public Stats yStats() { + return yStats.snapshot(); + } + + /** + * Returns the population covariance of the values. The count must be non-zero. + * + *

This is guaranteed to return zero if the dataset contains a single pair of finite values. It + * is not guaranteed to return zero when the dataset consists of the same pair of values multiple + * times, due to numerical errors. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double populationCovariance() { + checkState(count() != 0); + return sumOfProductsOfDeltas / count(); + } + + /** + * Returns the sample covariance of the values. The count must be greater than one. + * + *

This is not guaranteed to return zero when the dataset consists of the same pair of values + * multiple times, due to numerical errors. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single pair of values + */ + public final double sampleCovariance() { + checkState(count() > 1); + return sumOfProductsOfDeltas / (count() - 1); + } + + /** + * Returns the Pearson's or + * product-moment correlation coefficient of the values. The count must greater than one, and + * the {@code x} and {@code y} values must both have non-zero population variance (i.e. {@code + * xStats().populationVariance() > 0.0 && yStats().populationVariance() > 0.0}). The result is not + * guaranteed to be exactly +/-1 even when the data are perfectly (anti-)correlated, due to + * numerical errors. However, it is guaranteed to be in the inclusive range [-1, +1]. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single pair of values, or + * either the {@code x} and {@code y} dataset has zero population variance + */ + public final double pearsonsCorrelationCoefficient() { + checkState(count() > 1); + if (isNaN(sumOfProductsOfDeltas)) { + return NaN; + } + double xSumOfSquaresOfDeltas = xStats.sumOfSquaresOfDeltas(); + double ySumOfSquaresOfDeltas = yStats.sumOfSquaresOfDeltas(); + checkState(xSumOfSquaresOfDeltas > 0.0); + checkState(ySumOfSquaresOfDeltas > 0.0); + // The product of two positive numbers can be zero if the multiplication underflowed. We + // force a positive value by effectively rounding up to MIN_VALUE. + double productOfSumsOfSquaresOfDeltas = + ensurePositive(xSumOfSquaresOfDeltas * ySumOfSquaresOfDeltas); + return ensureInUnitRange(sumOfProductsOfDeltas / Math.sqrt(productOfSumsOfSquaresOfDeltas)); + } + + /** + * Returns a linear transformation giving the best fit to the data according to Ordinary Least Squares linear + * regression of {@code y} as a function of {@code x}. The count must be greater than one, and + * either the {@code x} or {@code y} data must have a non-zero population variance (i.e. {@code + * xStats().populationVariance() > 0.0 || yStats().populationVariance() > 0.0}). The result is + * guaranteed to be horizontal if there is variance in the {@code x} data but not the {@code y} + * data, and vertical if there is variance in the {@code y} data but not the {@code x} data. + * + *

This fit minimizes the root-mean-square error in {@code y} as a function of {@code x}. This + * error is defined as the square root of the mean of the squares of the differences between the + * actual {@code y} values of the data and the values predicted by the fit for the {@code x} + * values (i.e. it is the square root of the mean of the squares of the vertical distances between + * the data points and the best fit line). For this fit, this error is a fraction {@code sqrt(1 - + * R*R)} of the population standard deviation of {@code y}, where {@code R} is the Pearson's + * correlation coefficient (as given by {@link #pearsonsCorrelationCoefficient()}). + * + *

The corresponding root-mean-square error in {@code x} as a function of {@code y} is a + * fraction {@code sqrt(1/(R*R) - 1)} of the population standard deviation of {@code x}. This fit + * does not normally minimize that error: to do that, you should swap the roles of {@code x} and + * {@code y}. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link + * LinearTransformation#forNaN()}. + * + * @throws IllegalStateException if the dataset is empty or contains a single pair of values, or + * both the {@code x} and {@code y} dataset have zero population variance + */ + public final LinearTransformation leastSquaresFit() { + checkState(count() > 1); + if (isNaN(sumOfProductsOfDeltas)) { + return LinearTransformation.forNaN(); + } + double xSumOfSquaresOfDeltas = xStats.sumOfSquaresOfDeltas(); + if (xSumOfSquaresOfDeltas > 0.0) { + if (yStats.sumOfSquaresOfDeltas() > 0.0) { + return LinearTransformation.mapping(xStats.mean(), yStats.mean()) + .withSlope(sumOfProductsOfDeltas / xSumOfSquaresOfDeltas); + } else { + return LinearTransformation.horizontal(yStats.mean()); + } + } else { + checkState(yStats.sumOfSquaresOfDeltas() > 0.0); + return LinearTransformation.vertical(xStats.mean()); + } + } + + private double ensurePositive(double value) { + if (value > 0.0) { + return value; + } else { + return Double.MIN_VALUE; + } + } + + private static double ensureInUnitRange(double value) { + return Doubles.constrainToRange(value, -1.0, 1.0); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ParametricNullness.java new file mode 100644 index 0000000000000..cec54a2286706 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/Quantiles.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/Quantiles.java new file mode 100644 index 0000000000000..b1ddc4dc860c9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/Quantiles.java @@ -0,0 +1,705 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.Doubles; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.math.RoundingMode; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import static java.lang.Double.NEGATIVE_INFINITY; +import static java.lang.Double.NaN; +import static java.lang.Double.POSITIVE_INFINITY; +import static java.util.Arrays.sort; +import static java.util.Collections.unmodifiableMap; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * Provides a fluent API for calculating quantiles. + * + *

Examples

+ * + *

To compute the median: + * + *

{@code
+ * double myMedian = median().compute(myDataset);
+ * }
+ * + * where {@link #median()} has been statically imported. + * + *

To compute the 99th percentile: + * + *

{@code
+ * double myPercentile99 = percentiles().index(99).compute(myDataset);
+ * }
+ * + * where {@link #percentiles()} has been statically imported. + * + *

To compute median and the 90th and 99th percentiles: + * + *

{@code
+ * Map myPercentiles =
+ *     percentiles().indexes(50, 90, 99).compute(myDataset);
+ * }
+ * + * where {@link #percentiles()} has been statically imported: {@code myPercentiles} maps the keys + * 50, 90, and 99, to their corresponding quantile values. + * + *

To compute quartiles, use {@link #quartiles()} instead of {@link #percentiles()}. To compute + * arbitrary q-quantiles, use {@link #scale scale(q)}. + * + *

These examples all take a copy of your dataset. If you have a double array, you are okay with + * it being arbitrarily reordered, and you want to avoid that copy, you can use {@code + * computeInPlace} instead of {@code compute}. + * + *

Definition and notes on interpolation

+ * + *

The definition of the kth q-quantile of N values is as follows: define x = k * (N - 1) / q; if + * x is an integer, the result is the value which would appear at index x in the sorted dataset + * (unless there are {@link Double#NaN NaN} values, see below); otherwise, the result is the average + * of the values which would appear at the indexes floor(x) and ceil(x) weighted by (1-frac(x)) and + * frac(x) respectively. This is the same definition as used by Excel and by S, it is the Type 7 + * definition in R, and it is + * described by + * wikipedia as providing "Linear interpolation of the modes for the order statistics for the + * uniform distribution on [0,1]." + * + *

Handling of non-finite values

+ * + *

If any values in the input are {@link Double#NaN NaN} then all values returned are {@link + * Double#NaN NaN}. (This is the one occasion when the behaviour is not the same as you'd get from + * sorting with {@link java.util.Arrays#sort(double[]) Arrays.sort(double[])} or {@link + * java.util.Collections#sort(java.util.List) Collections.sort(List<Double>)} and selecting + * the required value(s). Those methods would sort {@link Double#NaN NaN} as if it is greater than + * any other value and place them at the end of the dataset, even after {@link + * Double#POSITIVE_INFINITY POSITIVE_INFINITY}.) + * + *

Otherwise, {@link Double#NEGATIVE_INFINITY NEGATIVE_INFINITY} and {@link + * Double#POSITIVE_INFINITY POSITIVE_INFINITY} sort to the beginning and the end of the dataset, as + * you would expect. + * + *

If required to do a weighted average between an infinity and a finite value, or between an + * infinite value and itself, the infinite value is returned. If required to do a weighted average + * between {@link Double#NEGATIVE_INFINITY NEGATIVE_INFINITY} and {@link Double#POSITIVE_INFINITY + * POSITIVE_INFINITY}, {@link Double#NaN NaN} is returned (note that this will only happen if the + * dataset contains no finite values). + * + *

Performance

+ * + *

The average time complexity of the computation is O(N) in the size of the dataset. There is a + * worst case time complexity of O(N^2). You are extremely unlikely to hit this quadratic case on + * randomly ordered data (the probability decreases faster than exponentially in N), but if you are + * passing in unsanitized user data then a malicious user could force it. A light shuffle of the + * data using an unpredictable seed should normally be enough to thwart this attack. + * + *

The time taken to compute multiple quantiles on the same dataset using {@link Scale#indexes + * indexes} is generally less than the total time taken to compute each of them separately, and + * sometimes much less. For example, on a large enough dataset, computing the 90th and 99th + * percentiles together takes about 55% as long as computing them separately. + * + *

When calling {@link ScaleAndIndex#compute} (in {@linkplain ScaleAndIndexes#compute either + * form}), the memory requirement is 8*N bytes for the copy of the dataset plus an overhead which is + * independent of N (but depends on the quantiles being computed). When calling {@link + * ScaleAndIndex#computeInPlace computeInPlace} (in {@linkplain ScaleAndIndexes#computeInPlace + * either form}), only the overhead is required. The number of object allocations is independent of + * N in both cases. + * + * @author Pete Gillin + * @since 20.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Quantiles { + + /** Specifies the computation of a median (i.e. the 1st 2-quantile). */ + public static ScaleAndIndex median() { + return scale(2).index(1); + } + + /** Specifies the computation of quartiles (i.e. 4-quantiles). */ + public static Scale quartiles() { + return scale(4); + } + + /** Specifies the computation of percentiles (i.e. 100-quantiles). */ + public static Scale percentiles() { + return scale(100); + } + + /** + * Specifies the computation of q-quantiles. + * + * @param scale the scale for the quantiles to be calculated, i.e. the q of the q-quantiles, which + * must be positive + */ + public static Scale scale(int scale) { + return new Scale(scale); + } + + /** + * Describes the point in a fluent API chain where only the scale (i.e. the q in q-quantiles) has + * been specified. + * + * @since 20.0 + */ + public static final class Scale { + + private final int scale; + + private Scale(int scale) { + checkArgument(scale > 0, "Quantile scale must be positive"); + this.scale = scale; + } + + /** + * Specifies a single quantile index to be calculated, i.e. the k in the kth q-quantile. + * + * @param index the quantile index, which must be in the inclusive range [0, q] for q-quantiles + */ + public ScaleAndIndex index(int index) { + return new ScaleAndIndex(scale, index); + } + + /** + * Specifies multiple quantile indexes to be calculated, each index being the k in the kth + * q-quantile. + * + * @param indexes the quantile indexes, each of which must be in the inclusive range [0, q] for + * q-quantiles; the order of the indexes is unimportant, duplicates will be ignored, and the + * set will be snapshotted when this method is called + * @throws IllegalArgumentException if {@code indexes} is empty + */ + public ScaleAndIndexes indexes(int... indexes) { + return new ScaleAndIndexes(scale, indexes.clone()); + } + + /** + * Specifies multiple quantile indexes to be calculated, each index being the k in the kth + * q-quantile. + * + * @param indexes the quantile indexes, each of which must be in the inclusive range [0, q] for + * q-quantiles; the order of the indexes is unimportant, duplicates will be ignored, and the + * set will be snapshotted when this method is called + * @throws IllegalArgumentException if {@code indexes} is empty + */ + public ScaleAndIndexes indexes(Collection indexes) { + return new ScaleAndIndexes(scale, Ints.toArray(indexes)); + } + } + + /** + * Describes the point in a fluent API chain where the scale and a single quantile index (i.e. the + * q and the k in the kth q-quantile) have been specified. + * + * @since 20.0 + */ + public static final class ScaleAndIndex { + + private final int scale; + private final int index; + + private ScaleAndIndex(int scale, int index) { + checkIndex(index, scale); + this.scale = scale; + this.index = index; + } + + /** + * Computes the quantile value of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will be + * cast to doubles (with any associated lost of precision), and which will not be mutated by + * this call (it is copied instead) + * @return the quantile value + */ + public double compute(Collection dataset) { + return computeInPlace(Doubles.toArray(dataset)); + } + + /** + * Computes the quantile value of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will not + * be mutated by this call (it is copied instead) + * @return the quantile value + */ + public double compute(double... dataset) { + return computeInPlace(dataset.clone()); + } + + /** + * Computes the quantile value of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will be + * cast to doubles (with any associated lost of precision), and which will not be mutated by + * this call (it is copied instead) + * @return the quantile value + */ + public double compute(long... dataset) { + return computeInPlace(longsToDoubles(dataset)); + } + + /** + * Computes the quantile value of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will be + * cast to doubles, and which will not be mutated by this call (it is copied instead) + * @return the quantile value + */ + public double compute(int... dataset) { + return computeInPlace(intsToDoubles(dataset)); + } + + /** + * Computes the quantile value of the given dataset, performing the computation in-place. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, and which will + * be arbitrarily reordered by this method call + * @return the quantile value + */ + public double computeInPlace(double... dataset) { + checkArgument(dataset.length > 0, "Cannot calculate quantiles of an empty dataset"); + if (containsNaN(dataset)) { + return NaN; + } + + // Calculate the quotient and remainder in the integer division x = k * (N-1) / q, i.e. + // index * (dataset.length - 1) / scale. If there is no remainder, we can just find the value + // whose index in the sorted dataset equals the quotient; if there is a remainder, we + // interpolate between that and the next value. + + // Since index and (dataset.length - 1) are non-negative ints, their product can be expressed + // as a long, without risk of overflow: + long numerator = (long) index * (dataset.length - 1); + // Since scale is a positive int, index is in [0, scale], and (dataset.length - 1) is a + // non-negative int, we can do long-arithmetic on index * (dataset.length - 1) / scale to get + // a rounded ratio and a remainder which can be expressed as ints, without risk of overflow: + int quotient = (int) LongMath.divide(numerator, scale, RoundingMode.DOWN); + int remainder = (int) (numerator - (long) quotient * scale); + selectInPlace(quotient, dataset, 0, dataset.length - 1); + if (remainder == 0) { + return dataset[quotient]; + } else { + selectInPlace(quotient + 1, dataset, quotient + 1, dataset.length - 1); + return interpolate(dataset[quotient], dataset[quotient + 1], remainder, scale); + } + } + } + + /** + * Describes the point in a fluent API chain where the scale and a multiple quantile indexes (i.e. + * the q and a set of values for the k in the kth q-quantile) have been specified. + * + * @since 20.0 + */ + public static final class ScaleAndIndexes { + + private final int scale; + private final int[] indexes; + + private ScaleAndIndexes(int scale, int[] indexes) { + for (int index : indexes) { + checkIndex(index, scale); + } + checkArgument(indexes.length > 0, "Indexes must be a non empty array"); + this.scale = scale; + this.indexes = indexes; + } + + /** + * Computes the quantile values of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will be + * cast to doubles (with any associated lost of precision), and which will not be mutated by + * this call (it is copied instead) + * @return an unmodifiable, ordered map of results: the keys will be the specified quantile + * indexes, and the values the corresponding quantile values. When iterating, entries in the + * map are ordered by quantile index in the same order they were passed to the {@code + * indexes} method. + */ + public Map compute(Collection dataset) { + return computeInPlace(Doubles.toArray(dataset)); + } + + /** + * Computes the quantile values of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will not + * be mutated by this call (it is copied instead) + * @return an unmodifiable, ordered map of results: the keys will be the specified quantile + * indexes, and the values the corresponding quantile values. When iterating, entries in the + * map are ordered by quantile index in the same order they were passed to the {@code + * indexes} method. + */ + public Map compute(double... dataset) { + return computeInPlace(dataset.clone()); + } + + /** + * Computes the quantile values of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will be + * cast to doubles (with any associated lost of precision), and which will not be mutated by + * this call (it is copied instead) + * @return an unmodifiable, ordered map of results: the keys will be the specified quantile + * indexes, and the values the corresponding quantile values. When iterating, entries in the + * map are ordered by quantile index in the same order they were passed to the {@code + * indexes} method. + */ + public Map compute(long... dataset) { + return computeInPlace(longsToDoubles(dataset)); + } + + /** + * Computes the quantile values of the given dataset. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, which will be + * cast to doubles, and which will not be mutated by this call (it is copied instead) + * @return an unmodifiable, ordered map of results: the keys will be the specified quantile + * indexes, and the values the corresponding quantile values. When iterating, entries in the + * map are ordered by quantile index in the same order they were passed to the {@code + * indexes} method. + */ + public Map compute(int... dataset) { + return computeInPlace(intsToDoubles(dataset)); + } + + /** + * Computes the quantile values of the given dataset, performing the computation in-place. + * + * @param dataset the dataset to do the calculation on, which must be non-empty, and which will + * be arbitrarily reordered by this method call + * @return an unmodifiable, ordered map of results: the keys will be the specified quantile + * indexes, and the values the corresponding quantile values. When iterating, entries in the + * map are ordered by quantile index in the same order that the indexes were passed to the + * {@code indexes} method. + */ + public Map computeInPlace(double... dataset) { + checkArgument(dataset.length > 0, "Cannot calculate quantiles of an empty dataset"); + if (containsNaN(dataset)) { + Map nanMap = new LinkedHashMap<>(); + for (int index : indexes) { + nanMap.put(index, NaN); + } + return unmodifiableMap(nanMap); + } + + // Calculate the quotients and remainders in the integer division x = k * (N - 1) / q, i.e. + // index * (dataset.length - 1) / scale for each index in indexes. For each, if there is no + // remainder, we can just select the value whose index in the sorted dataset equals the + // quotient; if there is a remainder, we interpolate between that and the next value. + + int[] quotients = new int[indexes.length]; + int[] remainders = new int[indexes.length]; + // The indexes to select. In the worst case, we'll need one each side of each quantile. + int[] requiredSelections = new int[indexes.length * 2]; + int requiredSelectionsCount = 0; + for (int i = 0; i < indexes.length; i++) { + // Since index and (dataset.length - 1) are non-negative ints, their product can be + // expressed as a long, without risk of overflow: + long numerator = (long) indexes[i] * (dataset.length - 1); + // Since scale is a positive int, index is in [0, scale], and (dataset.length - 1) is a + // non-negative int, we can do long-arithmetic on index * (dataset.length - 1) / scale to + // get a rounded ratio and a remainder which can be expressed as ints, without risk of + // overflow: + int quotient = (int) LongMath.divide(numerator, scale, RoundingMode.DOWN); + int remainder = (int) (numerator - (long) quotient * scale); + quotients[i] = quotient; + remainders[i] = remainder; + requiredSelections[requiredSelectionsCount] = quotient; + requiredSelectionsCount++; + if (remainder != 0) { + requiredSelections[requiredSelectionsCount] = quotient + 1; + requiredSelectionsCount++; + } + } + sort(requiredSelections, 0, requiredSelectionsCount); + selectAllInPlace( + requiredSelections, 0, requiredSelectionsCount - 1, dataset, 0, dataset.length - 1); + Map ret = new LinkedHashMap<>(); + for (int i = 0; i < indexes.length; i++) { + int quotient = quotients[i]; + int remainder = remainders[i]; + if (remainder == 0) { + ret.put(indexes[i], dataset[quotient]); + } else { + ret.put( + indexes[i], interpolate(dataset[quotient], dataset[quotient + 1], remainder, scale)); + } + } + return unmodifiableMap(ret); + } + } + + /** Returns whether any of the values in {@code dataset} are {@code NaN}. */ + private static boolean containsNaN(double... dataset) { + for (double value : dataset) { + if (Double.isNaN(value)) { + return true; + } + } + return false; + } + + /** + * Returns a value a fraction {@code (remainder / scale)} of the way between {@code lower} and + * {@code upper}. Assumes that {@code lower <= upper}. Correctly handles infinities (but not + * {@code NaN}). + */ + private static double interpolate(double lower, double upper, double remainder, double scale) { + if (lower == NEGATIVE_INFINITY) { + if (upper == POSITIVE_INFINITY) { + // Return NaN when lower == NEGATIVE_INFINITY and upper == POSITIVE_INFINITY: + return NaN; + } + // Return NEGATIVE_INFINITY when NEGATIVE_INFINITY == lower <= upper < POSITIVE_INFINITY: + return NEGATIVE_INFINITY; + } + if (upper == POSITIVE_INFINITY) { + // Return POSITIVE_INFINITY when NEGATIVE_INFINITY < lower <= upper == POSITIVE_INFINITY: + return POSITIVE_INFINITY; + } + return lower + (upper - lower) * remainder / scale; + } + + private static void checkIndex(int index, int scale) { + if (index < 0 || index > scale) { + throw new IllegalArgumentException( + "Quantile indexes must be between 0 and the scale, which is " + scale); + } + } + + private static double[] longsToDoubles(long[] longs) { + int len = longs.length; + double[] doubles = new double[len]; + for (int i = 0; i < len; i++) { + doubles[i] = longs[i]; + } + return doubles; + } + + private static double[] intsToDoubles(int[] ints) { + int len = ints.length; + double[] doubles = new double[len]; + for (int i = 0; i < len; i++) { + doubles[i] = ints[i]; + } + return doubles; + } + + /** + * Performs an in-place selection to find the element which would appear at a given index in a + * dataset if it were sorted. The following preconditions should hold: + * + *

    + *
  • {@code required}, {@code from}, and {@code to} should all be indexes into {@code array}; + *
  • {@code required} should be in the range [{@code from}, {@code to}]; + *
  • all the values with indexes in the range [0, {@code from}) should be less than or equal + * to all the values with indexes in the range [{@code from}, {@code to}]; + *
  • all the values with indexes in the range ({@code to}, {@code array.length - 1}] should be + * greater than or equal to all the values with indexes in the range [{@code from}, {@code + * to}]. + *
+ * + * This method will reorder the values with indexes in the range [{@code from}, {@code to}] such + * that all the values with indexes in the range [{@code from}, {@code required}) are less than or + * equal to the value with index {@code required}, and all the values with indexes in the range + * ({@code required}, {@code to}] are greater than or equal to that value. Therefore, the value at + * {@code required} is the value which would appear at that index in the sorted dataset. + */ + private static void selectInPlace(int required, double[] array, int from, int to) { + // If we are looking for the least element in the range, we can just do a linear search for it. + // (We will hit this whenever we are doing quantile interpolation: our first selection finds + // the lower value, our second one finds the upper value by looking for the next least element.) + if (required == from) { + int min = from; + for (int index = from + 1; index <= to; index++) { + if (array[min] > array[index]) { + min = index; + } + } + if (min != from) { + swap(array, min, from); + } + return; + } + + // Let's play quickselect! We'll repeatedly partition the range [from, to] containing the + // required element, as long as it has more than one element. + while (to > from) { + int partitionPoint = partition(array, from, to); + if (partitionPoint >= required) { + to = partitionPoint - 1; + } + if (partitionPoint <= required) { + from = partitionPoint + 1; + } + } + } + + /** + * Performs a partition operation on the slice of {@code array} with elements in the range [{@code + * from}, {@code to}]. Uses the median of {@code from}, {@code to}, and the midpoint between them + * as a pivot. Returns the index which the slice is partitioned around, i.e. if it returns {@code + * ret} then we know that the values with indexes in [{@code from}, {@code ret}) are less than or + * equal to the value at {@code ret} and the values with indexes in ({@code ret}, {@code to}] are + * greater than or equal to that. + */ + private static int partition(double[] array, int from, int to) { + // Select a pivot, and move it to the start of the slice i.e. to index from. + movePivotToStartOfSlice(array, from, to); + double pivot = array[from]; + + // Move all elements with indexes in (from, to] which are greater than the pivot to the end of + // the array. Keep track of where those elements begin. + int partitionPoint = to; + for (int i = to; i > from; i--) { + if (array[i] > pivot) { + swap(array, partitionPoint, i); + partitionPoint--; + } + } + + // We now know that all elements with indexes in (from, partitionPoint] are less than or equal + // to the pivot at from, and all elements with indexes in (partitionPoint, to] are greater than + // it. We swap the pivot into partitionPoint and we know the array is partitioned around that. + swap(array, from, partitionPoint); + return partitionPoint; + } + + /** + * Selects the pivot to use, namely the median of the values at {@code from}, {@code to}, and + * halfway between the two (rounded down), from {@code array}, and ensure (by swapping elements if + * necessary) that that pivot value appears at the start of the slice i.e. at {@code from}. + * Expects that {@code from} is strictly less than {@code to}. + */ + private static void movePivotToStartOfSlice(double[] array, int from, int to) { + int mid = (from + to) >>> 1; + // We want to make a swap such that either array[to] <= array[from] <= array[mid], or + // array[mid] <= array[from] <= array[to]. We know that from < to, so we know mid < to + // (although it's possible that mid == from, if to == from + 1). Note that the postcondition + // would be impossible to fulfil if mid == to unless we also have array[from] == array[to]. + boolean toLessThanMid = (array[to] < array[mid]); + boolean midLessThanFrom = (array[mid] < array[from]); + boolean toLessThanFrom = (array[to] < array[from]); + if (toLessThanMid == midLessThanFrom) { + // Either array[to] < array[mid] < array[from] or array[from] <= array[mid] <= array[to]. + swap(array, mid, from); + } else if (toLessThanMid != toLessThanFrom) { + // Either array[from] <= array[to] < array[mid] or array[mid] <= array[to] < array[from]. + swap(array, from, to); + } + // The postcondition now holds. So the median, our chosen pivot, is at from. + } + + /** + * Performs an in-place selection, like {@link #selectInPlace}, to select all the indexes {@code + * allRequired[i]} for {@code i} in the range [{@code requiredFrom}, {@code requiredTo}]. These + * indexes must be sorted in the array and must all be in the range [{@code from}, {@code to}]. + */ + private static void selectAllInPlace( + int[] allRequired, int requiredFrom, int requiredTo, double[] array, int from, int to) { + // Choose the first selection to do... + int requiredChosen = chooseNextSelection(allRequired, requiredFrom, requiredTo, from, to); + int required = allRequired[requiredChosen]; + + // ...do the first selection... + selectInPlace(required, array, from, to); + + // ...then recursively perform the selections in the range below... + int requiredBelow = requiredChosen - 1; + while (requiredBelow >= requiredFrom && allRequired[requiredBelow] == required) { + requiredBelow--; // skip duplicates of required in the range below + } + if (requiredBelow >= requiredFrom) { + selectAllInPlace(allRequired, requiredFrom, requiredBelow, array, from, required - 1); + } + + // ...and then recursively perform the selections in the range above. + int requiredAbove = requiredChosen + 1; + while (requiredAbove <= requiredTo && allRequired[requiredAbove] == required) { + requiredAbove++; // skip duplicates of required in the range above + } + if (requiredAbove <= requiredTo) { + selectAllInPlace(allRequired, requiredAbove, requiredTo, array, required + 1, to); + } + } + + /** + * Chooses the next selection to do from the required selections. It is required that the array + * {@code allRequired} is sorted and that {@code allRequired[i]} are in the range [{@code from}, + * {@code to}] for all {@code i} in the range [{@code requiredFrom}, {@code requiredTo}]. The + * value returned by this method is the {@code i} in that range such that {@code allRequired[i]} + * is as close as possible to the center of the range [{@code from}, {@code to}]. Choosing the + * value closest to the center of the range first is the most efficient strategy because it + * minimizes the size of the subranges from which the remaining selections must be done. + */ + private static int chooseNextSelection( + int[] allRequired, int requiredFrom, int requiredTo, int from, int to) { + if (requiredFrom == requiredTo) { + return requiredFrom; // only one thing to choose, so choose it + } + + // Find the center and round down. The true center is either centerFloor or halfway between + // centerFloor and centerFloor + 1. + int centerFloor = (from + to) >>> 1; + + // Do a binary search until we're down to the range of two which encloses centerFloor (unless + // all values are lower or higher than centerFloor, in which case we find the two highest or + // lowest respectively). If centerFloor is in allRequired, we will definitely find it. If not, + // but centerFloor + 1 is, we'll definitely find that. The closest value to the true (unrounded) + // center will be at either low or high. + int low = requiredFrom; + int high = requiredTo; + while (high > low + 1) { + int mid = (low + high) >>> 1; + if (allRequired[mid] > centerFloor) { + high = mid; + } else if (allRequired[mid] < centerFloor) { + low = mid; + } else { + return mid; // allRequired[mid] = centerFloor, so we can't get closer than that + } + } + + // Now pick the closest of the two candidates. Note that there is no rounding here. + if (from + to - allRequired[low] - allRequired[high] > 0) { + return high; + } else { + return low; + } + } + + /** Swaps the values at {@code i} and {@code j} in {@code array}. */ + private static void swap(double[] array, int i, int j) { + double temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/Stats.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/Stats.java new file mode 100644 index 0000000000000..5a1be0b648f73 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/Stats.java @@ -0,0 +1,669 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Objects; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Iterator; +import java.util.stream.Collector; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +import static java.lang.Double.NaN; +import static java.lang.Double.doubleToLongBits; +import static java.lang.Double.isNaN; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.math.DoubleUtils.ensureNonNegative; +import static org.apache.iotdb.google.common.math.StatsAccumulator.calculateNewMeanNonFinite; +import static org.apache.iotdb.google.common.primitives.Doubles.isFinite; + +/** + * A bundle of statistical summary values -- sum, count, mean/average, min and max, and several + * forms of variance -- that were computed from a single set of zero or more floating-point values. + * + *

There are two ways to obtain a {@code Stats} instance: + * + *

    + *
  • If all the values you want to summarize are already known, use the appropriate {@code + * Stats.of} factory method below. Primitive arrays, iterables and iterators of any kind of + * {@code Number}, and primitive varargs are supported. + *
  • Or, to avoid storing up all the data first, create a {@link StatsAccumulator} instance, + * feed values to it as you get them, then call {@link StatsAccumulator#snapshot}. + *
+ * + *

Static convenience methods called {@code meanOf} are also provided for users who wish to + * calculate only the mean. + * + *

Java 8 users: If you are not using any of the variance statistics, you may wish to use + * built-in JDK libraries instead of this class. + * + * @author Pete Gillin + * @author Kevin Bourrillion + * @since 20.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Stats implements Serializable { + + private final long count; + private final double mean; + private final double sumOfSquaresOfDeltas; + private final double min; + private final double max; + + /** + * Internal constructor. Users should use {@link #of} or {@link StatsAccumulator#snapshot}. + * + *

To ensure that the created instance obeys its contract, the parameters should satisfy the + * following constraints. This is the callers responsibility and is not enforced here. + * + *

    + *
  • If {@code count} is 0, {@code mean} may have any finite value (its only usage will be to + * get multiplied by 0 to calculate the sum), and the other parameters may have any values + * (they will not be used). + *
  • If {@code count} is 1, {@code sumOfSquaresOfDeltas} must be exactly 0.0 or {@link + * Double#NaN}. + *
+ */ + Stats(long count, double mean, double sumOfSquaresOfDeltas, double min, double max) { + this.count = count; + this.mean = mean; + this.sumOfSquaresOfDeltas = sumOfSquaresOfDeltas; + this.min = min; + this.max = max; + } + + /** + * Returns statistics over a dataset containing the given values. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision) + */ + public static Stats of(Iterable values) { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The iterator will be completely + * consumed by this method. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision) + */ + public static Stats of(Iterator values) { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. + * + * @param values a series of values + */ + public static Stats of(double... values) { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. + * + * @param values a series of values + */ + public static Stats of(int... values) { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + */ + public static Stats of(long... values) { + StatsAccumulator accumulator = new StatsAccumulator(); + accumulator.addAll(values); + return accumulator.snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The stream will be completely + * consumed by this method. + * + *

If you have a {@code Stream} rather than a {@code DoubleStream}, you should collect + * the values using {@link #toStats()} instead. + * + * @param values a series of values + * @since 28.2 + */ + public static Stats of(DoubleStream values) { + return values + .collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll) + .snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The stream will be completely + * consumed by this method. + * + *

If you have a {@code Stream} rather than an {@code IntStream}, you should collect + * the values using {@link #toStats()} instead. + * + * @param values a series of values + * @since 28.2 + */ + public static Stats of(IntStream values) { + return values + .collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll) + .snapshot(); + } + + /** + * Returns statistics over a dataset containing the given values. The stream will be completely + * consumed by this method. + * + *

If you have a {@code Stream} rather than a {@code LongStream}, you should collect the + * values using {@link #toStats()} instead. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + * @since 28.2 + */ + public static Stats of(LongStream values) { + return values + .collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll) + .snapshot(); + } + + /** + * Returns a {@link Collector} which accumulates statistics from a {@link java.util.stream.Stream} + * of any type of boxed {@link Number} into a {@link Stats}. Use by calling {@code + * boxedNumericStream.collect(toStats())}. The numbers will be converted to {@code double} values + * (which may cause loss of precision). + * + *

If you have any of the primitive streams {@code DoubleStream}, {@code IntStream}, or {@code + * LongStream}, you should use the factory method {@link #of} instead. + * + * @since 28.2 + */ + public static Collector toStats() { + return Collector.of( + StatsAccumulator::new, + (a, x) -> a.add(x.doubleValue()), + (l, r) -> { + l.addAll(r); + return l; + }, + StatsAccumulator::snapshot, + Collector.Characteristics.UNORDERED); + } + + /** Returns the number of values. */ + public long count() { + return count; + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains both {@link Double#POSITIVE_INFINITY} and {@link Double#NEGATIVE_INFINITY} then the + * result is {@link Double#NaN}. If it contains {@link Double#POSITIVE_INFINITY} and finite values + * only or {@link Double#POSITIVE_INFINITY} only, the result is {@link Double#POSITIVE_INFINITY}. + * If it contains {@link Double#NEGATIVE_INFINITY} and finite values only or {@link + * Double#NEGATIVE_INFINITY} only, the result is {@link Double#NEGATIVE_INFINITY}. + * + *

If you only want to calculate the mean, use {@link #meanOf} instead of creating a {@link + * Stats} instance. + * + * @throws IllegalStateException if the dataset is empty + */ + public double mean() { + checkState(count != 0); + return mean; + } + + /** + * Returns the sum of the values. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains both {@link Double#POSITIVE_INFINITY} and {@link Double#NEGATIVE_INFINITY} then the + * result is {@link Double#NaN}. If it contains {@link Double#POSITIVE_INFINITY} and finite values + * only or {@link Double#POSITIVE_INFINITY} only, the result is {@link Double#POSITIVE_INFINITY}. + * If it contains {@link Double#NEGATIVE_INFINITY} and finite values only or {@link + * Double#NEGATIVE_INFINITY} only, the result is {@link Double#NEGATIVE_INFINITY}. + */ + public double sum() { + return mean * count; + } + + /** + * Returns the population + * variance of the values. The count must be non-zero. + * + *

This is guaranteed to return zero if the dataset contains only exactly one finite value. It + * is not guaranteed to return zero when the dataset consists of the same value multiple times, + * due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double populationVariance() { + checkState(count > 0); + if (isNaN(sumOfSquaresOfDeltas)) { + return NaN; + } + if (count == 1) { + return 0.0; + } + return ensureNonNegative(sumOfSquaresOfDeltas) / count(); + } + + /** + * Returns the + * population standard deviation of the values. The count must be non-zero. + * + *

This is guaranteed to return zero if the dataset contains only exactly one finite value. It + * is not guaranteed to return zero when the dataset consists of the same value multiple times, + * due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double populationStandardDeviation() { + return Math.sqrt(populationVariance()); + } + + /** + * Returns the unbiased sample + * variance of the values. If this dataset is a sample drawn from a population, this is an + * unbiased estimator of the population variance of the population. The count must be greater than + * one. + * + *

This is not guaranteed to return zero when the dataset consists of the same value multiple + * times, due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single value + */ + public double sampleVariance() { + checkState(count > 1); + if (isNaN(sumOfSquaresOfDeltas)) { + return NaN; + } + return ensureNonNegative(sumOfSquaresOfDeltas) / (count - 1); + } + + /** + * Returns the + * corrected sample standard deviation of the values. If this dataset is a sample drawn from a + * population, this is an estimator of the population standard deviation of the population which + * is less biased than {@link #populationStandardDeviation()} (the unbiased estimator depends on + * the distribution). The count must be greater than one. + * + *

This is not guaranteed to return zero when the dataset consists of the same value multiple + * times, due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single value + */ + public double sampleStandardDeviation() { + return Math.sqrt(sampleVariance()); + } + + /** + * Returns the lowest value in the dataset. The count must be non-zero. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains {@link Double#NEGATIVE_INFINITY} and not {@link Double#NaN} then the result is {@link + * Double#NEGATIVE_INFINITY}. If it contains {@link Double#POSITIVE_INFINITY} and finite values + * only then the result is the lowest finite value. If it contains {@link + * Double#POSITIVE_INFINITY} only then the result is {@link Double#POSITIVE_INFINITY}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double min() { + checkState(count != 0); + return min; + } + + /** + * Returns the highest value in the dataset. The count must be non-zero. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains {@link Double#POSITIVE_INFINITY} and not {@link Double#NaN} then the result is {@link + * Double#POSITIVE_INFINITY}. If it contains {@link Double#NEGATIVE_INFINITY} and finite values + * only then the result is the highest finite value. If it contains {@link + * Double#NEGATIVE_INFINITY} only then the result is {@link Double#NEGATIVE_INFINITY}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double max() { + checkState(count != 0); + return max; + } + + /** + * {@inheritDoc} + * + *

Note: This tests exact equality of the calculated statistics, including the floating + * point values. Two instances are guaranteed to be considered equal if one is copied from the + * other using {@code second = new StatsAccumulator().addAll(first).snapshot()}, if both were + * obtained by calling {@code snapshot()} on the same {@link StatsAccumulator} without adding any + * values in between the two calls, or if one is obtained from the other after round-tripping + * through java serialization. However, floating point rounding errors mean that it may be false + * for some instances where the statistics are mathematically equal, including instances + * constructed from the same values in a different order... or (in the general case) even in the + * same order. (It is guaranteed to return true for instances constructed from the same values in + * the same order if {@code strictfp} is in effect, or if the system architecture guarantees + * {@code strictfp}-like semantics.) + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Stats other = (Stats) obj; + return count == other.count + && doubleToLongBits(mean) == doubleToLongBits(other.mean) + && doubleToLongBits(sumOfSquaresOfDeltas) == doubleToLongBits(other.sumOfSquaresOfDeltas) + && doubleToLongBits(min) == doubleToLongBits(other.min) + && doubleToLongBits(max) == doubleToLongBits(other.max); + } + + /** + * {@inheritDoc} + * + *

Note: This hash code is consistent with exact equality of the calculated statistics, + * including the floating point values. See the note on {@link #equals} for details. + */ + @Override + public int hashCode() { + return Objects.hashCode(count, mean, sumOfSquaresOfDeltas, min, max); + } + + @Override + public String toString() { + if (count() > 0) { + return MoreObjects.toStringHelper(this) + .add("count", count) + .add("mean", mean) + .add("populationStandardDeviation", populationStandardDeviation()) + .add("min", min) + .add("max", max) + .toString(); + } else { + return MoreObjects.toStringHelper(this).add("count", count).toString(); + } + } + + double sumOfSquaresOfDeltas() { + return sumOfSquaresOfDeltas; + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

The definition of the mean is the same as {@link Stats#mean}. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision) + * @throws IllegalArgumentException if the dataset is empty + */ + public static double meanOf(Iterable values) { + return meanOf(values.iterator()); + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

The definition of the mean is the same as {@link Stats#mean}. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision) + * @throws IllegalArgumentException if the dataset is empty + */ + public static double meanOf(Iterator values) { + checkArgument(values.hasNext()); + long count = 1; + double mean = values.next().doubleValue(); + while (values.hasNext()) { + double value = values.next().doubleValue(); + count++; + if (isFinite(value) && isFinite(mean)) { + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (value - mean) / count; + } else { + mean = calculateNewMeanNonFinite(mean, value); + } + } + return mean; + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

The definition of the mean is the same as {@link Stats#mean}. + * + * @param values a series of values + * @throws IllegalArgumentException if the dataset is empty + */ + public static double meanOf(double... values) { + checkArgument(values.length > 0); + double mean = values[0]; + for (int index = 1; index < values.length; index++) { + double value = values[index]; + if (isFinite(value) && isFinite(mean)) { + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (value - mean) / (index + 1); + } else { + mean = calculateNewMeanNonFinite(mean, value); + } + } + return mean; + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

The definition of the mean is the same as {@link Stats#mean}. + * + * @param values a series of values + * @throws IllegalArgumentException if the dataset is empty + */ + public static double meanOf(int... values) { + checkArgument(values.length > 0); + double mean = values[0]; + for (int index = 1; index < values.length; index++) { + double value = values[index]; + if (isFinite(value) && isFinite(mean)) { + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (value - mean) / (index + 1); + } else { + mean = calculateNewMeanNonFinite(mean, value); + } + } + return mean; + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

The definition of the mean is the same as {@link Stats#mean}. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + * @throws IllegalArgumentException if the dataset is empty + */ + public static double meanOf(long... values) { + checkArgument(values.length > 0); + double mean = values[0]; + for (int index = 1; index < values.length; index++) { + double value = values[index]; + if (isFinite(value) && isFinite(mean)) { + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) + mean += (value - mean) / (index + 1); + } else { + mean = calculateNewMeanNonFinite(mean, value); + } + } + return mean; + } + + // Serialization helpers + + /** The size of byte array representation in bytes. */ + static final int BYTES = (Long.SIZE + Double.SIZE * 4) / Byte.SIZE; + + /** + * Gets a byte array representation of this instance. + * + *

Note: No guarantees are made regarding stability of the representation between + * versions. + */ + public byte[] toByteArray() { + ByteBuffer buff = ByteBuffer.allocate(BYTES).order(ByteOrder.LITTLE_ENDIAN); + writeTo(buff); + return buff.array(); + } + + /** + * Writes to the given {@link ByteBuffer} a byte representation of this instance. + * + *

Note: No guarantees are made regarding stability of the representation between + * versions. + * + * @param buffer A {@link ByteBuffer} with at least BYTES {@link ByteBuffer#remaining}, ordered as + * {@link ByteOrder#LITTLE_ENDIAN}, to which a BYTES-long byte representation of this instance + * is written. In the process increases the position of {@link ByteBuffer} by BYTES. + */ + void writeTo(ByteBuffer buffer) { + checkNotNull(buffer); + checkArgument( + buffer.remaining() >= BYTES, + "Expected at least Stats.BYTES = %s remaining , got %s", + BYTES, + buffer.remaining()); + buffer + .putLong(count) + .putDouble(mean) + .putDouble(sumOfSquaresOfDeltas) + .putDouble(min) + .putDouble(max); + } + + /** + * Creates a Stats instance from the given byte representation which was obtained by {@link + * #toByteArray}. + * + *

Note: No guarantees are made regarding stability of the representation between + * versions. + */ + public static Stats fromByteArray(byte[] byteArray) { + checkNotNull(byteArray); + checkArgument( + byteArray.length == BYTES, + "Expected Stats.BYTES = %s remaining , got %s", + BYTES, + byteArray.length); + return readFrom(ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN)); + } + + /** + * Creates a Stats instance from the byte representation read from the given {@link ByteBuffer}. + * + *

Note: No guarantees are made regarding stability of the representation between + * versions. + * + * @param buffer A {@link ByteBuffer} with at least BYTES {@link ByteBuffer#remaining}, ordered as + * {@link ByteOrder#LITTLE_ENDIAN}, from which a BYTES-long byte representation of this + * instance is read. In the process increases the position of {@link ByteBuffer} by BYTES. + */ + static Stats readFrom(ByteBuffer buffer) { + checkNotNull(buffer); + checkArgument( + buffer.remaining() >= BYTES, + "Expected at least Stats.BYTES = %s remaining , got %s", + BYTES, + buffer.remaining()); + return new Stats( + buffer.getLong(), + buffer.getDouble(), + buffer.getDouble(), + buffer.getDouble(), + buffer.getDouble()); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/StatsAccumulator.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/StatsAccumulator.java new file mode 100644 index 0000000000000..b5cef50db69f1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/StatsAccumulator.java @@ -0,0 +1,435 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.Iterator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; + +import static java.lang.Double.NaN; +import static java.lang.Double.isNaN; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.math.DoubleUtils.ensureNonNegative; +import static org.apache.iotdb.google.common.primitives.Doubles.isFinite; + +/** + * A mutable object which accumulates double values and tracks some basic statistics over all the + * values added so far. The values may be added singly or in groups. This class is not thread safe. + * + * @author Pete Gillin + * @author Kevin Bourrillion + * @since 20.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class StatsAccumulator { + + // These fields must satisfy the requirements of Stats' constructor as well as those of the stat + // methods of this class. + private long count = 0; + private double mean = 0.0; // any finite value will do, we only use it to multiply by zero for sum + private double sumOfSquaresOfDeltas = 0.0; + private double min = NaN; // any value will do + private double max = NaN; // any value will do + + /** Adds the given value to the dataset. */ + public void add(double value) { + if (count == 0) { + count = 1; + mean = value; + min = value; + max = value; + if (!isFinite(value)) { + sumOfSquaresOfDeltas = NaN; + } + } else { + count++; + if (isFinite(value) && isFinite(mean)) { + // Art of Computer Programming vol. 2, Knuth, 4.2.2, (15) and (16) + double delta = value - mean; + mean += delta / count; + sumOfSquaresOfDeltas += delta * (value - mean); + } else { + mean = calculateNewMeanNonFinite(mean, value); + sumOfSquaresOfDeltas = NaN; + } + min = Math.min(min, value); + max = Math.max(max, value); + } + } + + /** + * Adds the given values to the dataset. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision) + */ + public void addAll(Iterable values) { + for (Number value : values) { + add(value.doubleValue()); + } + } + + /** + * Adds the given values to the dataset. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision) + */ + public void addAll(Iterator values) { + while (values.hasNext()) { + add(values.next().doubleValue()); + } + } + + /** + * Adds the given values to the dataset. + * + * @param values a series of values + */ + public void addAll(double... values) { + for (double value : values) { + add(value); + } + } + + /** + * Adds the given values to the dataset. + * + * @param values a series of values + */ + public void addAll(int... values) { + for (int value : values) { + add(value); + } + } + + /** + * Adds the given values to the dataset. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + */ + public void addAll(long... values) { + for (long value : values) { + add(value); + } + } + + /** + * Adds the given values to the dataset. The stream will be completely consumed by this method. + * + * @param values a series of values + * @since 28.2 + */ + public void addAll(DoubleStream values) { + addAll(values.collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll)); + } + + /** + * Adds the given values to the dataset. The stream will be completely consumed by this method. + * + * @param values a series of values + * @since 28.2 + */ + public void addAll(IntStream values) { + addAll(values.collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll)); + } + + /** + * Adds the given values to the dataset. The stream will be completely consumed by this method. + * + * @param values a series of values, which will be converted to {@code double} values (this may + * cause loss of precision for longs of magnitude over 2^53 (slightly over 9e15)) + * @since 28.2 + */ + public void addAll(LongStream values) { + addAll(values.collect(StatsAccumulator::new, StatsAccumulator::add, StatsAccumulator::addAll)); + } + + /** + * Adds the given statistics to the dataset, as if the individual values used to compute the + * statistics had been added directly. + */ + public void addAll(Stats values) { + if (values.count() == 0) { + return; + } + merge(values.count(), values.mean(), values.sumOfSquaresOfDeltas(), values.min(), values.max()); + } + + /** + * Adds the given statistics to the dataset, as if the individual values used to compute the + * statistics had been added directly. + * + * @since 28.2 + */ + public void addAll(StatsAccumulator values) { + if (values.count() == 0) { + return; + } + merge(values.count(), values.mean(), values.sumOfSquaresOfDeltas(), values.min(), values.max()); + } + + private void merge( + long otherCount, + double otherMean, + double otherSumOfSquaresOfDeltas, + double otherMin, + double otherMax) { + if (count == 0) { + count = otherCount; + mean = otherMean; + sumOfSquaresOfDeltas = otherSumOfSquaresOfDeltas; + min = otherMin; + max = otherMax; + } else { + count += otherCount; + if (isFinite(mean) && isFinite(otherMean)) { + // This is a generalized version of the calculation in add(double) above. + double delta = otherMean - mean; + mean += delta * otherCount / count; + sumOfSquaresOfDeltas += otherSumOfSquaresOfDeltas + delta * (otherMean - mean) * otherCount; + } else { + mean = calculateNewMeanNonFinite(mean, otherMean); + sumOfSquaresOfDeltas = NaN; + } + min = Math.min(min, otherMin); + max = Math.max(max, otherMax); + } + } + + /** Returns an immutable snapshot of the current statistics. */ + public Stats snapshot() { + return new Stats(count, mean, sumOfSquaresOfDeltas, min, max); + } + + /** Returns the number of values. */ + public long count() { + return count; + } + + /** + * Returns the arithmetic mean of the + * values. The count must be non-zero. + * + *

If these values are a sample drawn from a population, this is also an unbiased estimator of + * the arithmetic mean of the population. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains both {@link Double#POSITIVE_INFINITY} and {@link Double#NEGATIVE_INFINITY} then the + * result is {@link Double#NaN}. If it contains {@link Double#POSITIVE_INFINITY} and finite values + * only or {@link Double#POSITIVE_INFINITY} only, the result is {@link Double#POSITIVE_INFINITY}. + * If it contains {@link Double#NEGATIVE_INFINITY} and finite values only or {@link + * Double#NEGATIVE_INFINITY} only, the result is {@link Double#NEGATIVE_INFINITY}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double mean() { + checkState(count != 0); + return mean; + } + + /** + * Returns the sum of the values. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains both {@link Double#POSITIVE_INFINITY} and {@link Double#NEGATIVE_INFINITY} then the + * result is {@link Double#NaN}. If it contains {@link Double#POSITIVE_INFINITY} and finite values + * only or {@link Double#POSITIVE_INFINITY} only, the result is {@link Double#POSITIVE_INFINITY}. + * If it contains {@link Double#NEGATIVE_INFINITY} and finite values only or {@link + * Double#NEGATIVE_INFINITY} only, the result is {@link Double#NEGATIVE_INFINITY}. + */ + public final double sum() { + return mean * count; + } + + /** + * Returns the population + * variance of the values. The count must be non-zero. + * + *

This is guaranteed to return zero if the dataset contains only exactly one finite value. It + * is not guaranteed to return zero when the dataset consists of the same value multiple times, + * due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty + */ + public final double populationVariance() { + checkState(count != 0); + if (isNaN(sumOfSquaresOfDeltas)) { + return NaN; + } + if (count == 1) { + return 0.0; + } + return ensureNonNegative(sumOfSquaresOfDeltas) / count; + } + + /** + * Returns the + * population standard deviation of the values. The count must be non-zero. + * + *

This is guaranteed to return zero if the dataset contains only exactly one finite value. It + * is not guaranteed to return zero when the dataset consists of the same value multiple times, + * due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty + */ + public final double populationStandardDeviation() { + return Math.sqrt(populationVariance()); + } + + /** + * Returns the unbiased sample + * variance of the values. If this dataset is a sample drawn from a population, this is an + * unbiased estimator of the population variance of the population. The count must be greater than + * one. + * + *

This is not guaranteed to return zero when the dataset consists of the same value multiple + * times, due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single value + */ + public final double sampleVariance() { + checkState(count > 1); + if (isNaN(sumOfSquaresOfDeltas)) { + return NaN; + } + return ensureNonNegative(sumOfSquaresOfDeltas) / (count - 1); + } + + /** + * Returns the + * corrected sample standard deviation of the values. If this dataset is a sample drawn from a + * population, this is an estimator of the population standard deviation of the population which + * is less biased than {@link #populationStandardDeviation()} (the unbiased estimator depends on + * the distribution). The count must be greater than one. + * + *

This is not guaranteed to return zero when the dataset consists of the same value multiple + * times, due to numerical errors. However, it is guaranteed never to return a negative result. + * + *

Non-finite values

+ * + *

If the dataset contains any non-finite values ({@link Double#POSITIVE_INFINITY}, {@link + * Double#NEGATIVE_INFINITY}, or {@link Double#NaN}) then the result is {@link Double#NaN}. + * + * @throws IllegalStateException if the dataset is empty or contains a single value + */ + public final double sampleStandardDeviation() { + return Math.sqrt(sampleVariance()); + } + + /** + * Returns the lowest value in the dataset. The count must be non-zero. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains {@link Double#NEGATIVE_INFINITY} and not {@link Double#NaN} then the result is {@link + * Double#NEGATIVE_INFINITY}. If it contains {@link Double#POSITIVE_INFINITY} and finite values + * only then the result is the lowest finite value. If it contains {@link + * Double#POSITIVE_INFINITY} only then the result is {@link Double#POSITIVE_INFINITY}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double min() { + checkState(count != 0); + return min; + } + + /** + * Returns the highest value in the dataset. The count must be non-zero. + * + *

Non-finite values

+ * + *

If the dataset contains {@link Double#NaN} then the result is {@link Double#NaN}. If it + * contains {@link Double#POSITIVE_INFINITY} and not {@link Double#NaN} then the result is {@link + * Double#POSITIVE_INFINITY}. If it contains {@link Double#NEGATIVE_INFINITY} and finite values + * only then the result is the highest finite value. If it contains {@link + * Double#NEGATIVE_INFINITY} only then the result is {@link Double#NEGATIVE_INFINITY}. + * + * @throws IllegalStateException if the dataset is empty + */ + public double max() { + checkState(count != 0); + return max; + } + + double sumOfSquaresOfDeltas() { + return sumOfSquaresOfDeltas; + } + + /** + * Calculates the new value for the accumulated mean when a value is added, in the case where at + * least one of the previous mean and the value is non-finite. + */ + static double calculateNewMeanNonFinite(double previousMean, double value) { + /* + * Desired behaviour is to match the results of applying the naive mean formula. In particular, + * the update formula can subtract infinities in cases where the naive formula would add them. + * + * Consequently: + * 1. If the previous mean is finite and the new value is non-finite then the new mean is that + * value (whether it is NaN or infinity). + * 2. If the new value is finite and the previous mean is non-finite then the mean is unchanged + * (whether it is NaN or infinity). + * 3. If both the previous mean and the new value are non-finite and... + * 3a. ...either or both is NaN (so mean != value) then the new mean is NaN. + * 3b. ...they are both the same infinities (so mean == value) then the mean is unchanged. + * 3c. ...they are different infinities (so mean != value) then the new mean is NaN. + */ + if (isFinite(previousMean)) { + // This is case 1. + return value; + } else if (isFinite(value) || previousMean == value) { + // This is case 2. or 3b. + return previousMean; + } else { + // This is case 3a. or 3c. + return NaN; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ToDoubleRounder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ToDoubleRounder.java new file mode 100644 index 0000000000000..4195590efcdfa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/ToDoubleRounder.java @@ -0,0 +1,161 @@ +/* + * 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.iotdb.google.common.math; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.math.RoundingMode; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.math.MathPreconditions.checkRoundingUnnecessary; + +/** + * Helper type to implement rounding {@code X} to a representable {@code double} value according to + * a {@link RoundingMode}. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class ToDoubleRounder> { + /** + * Returns x rounded to either the greatest double less than or equal to the precise value of x, + * or the least double greater than or equal to the precise value of x. + */ + abstract double roundToDoubleArbitrarily(X x); + + /** Returns the sign of x: either -1, 0, or 1. */ + abstract int sign(X x); + + /** Returns d's value as an X, rounded with the specified mode. */ + abstract X toX(double d, RoundingMode mode); + + /** Returns a - b, guaranteed that both arguments are nonnegative. */ + abstract X minus(X a, X b); + + /** Rounds {@code x} to a {@code double}. */ + final double roundToDouble(X x, RoundingMode mode) { + checkNotNull(x, "x"); + checkNotNull(mode, "mode"); + double roundArbitrarily = roundToDoubleArbitrarily(x); + if (Double.isInfinite(roundArbitrarily)) { + switch (mode) { + case DOWN: + case HALF_EVEN: + case HALF_DOWN: + case HALF_UP: + return Double.MAX_VALUE * sign(x); + case FLOOR: + return (roundArbitrarily == Double.POSITIVE_INFINITY) + ? Double.MAX_VALUE + : Double.NEGATIVE_INFINITY; + case CEILING: + return (roundArbitrarily == Double.POSITIVE_INFINITY) + ? Double.POSITIVE_INFINITY + : -Double.MAX_VALUE; + case UP: + return roundArbitrarily; + case UNNECESSARY: + throw new ArithmeticException(x + " cannot be represented precisely as a double"); + } + } + X roundArbitrarilyAsX = toX(roundArbitrarily, RoundingMode.UNNECESSARY); + int cmpXToRoundArbitrarily = x.compareTo(roundArbitrarilyAsX); + switch (mode) { + case UNNECESSARY: + checkRoundingUnnecessary(cmpXToRoundArbitrarily == 0); + return roundArbitrarily; + case FLOOR: + return (cmpXToRoundArbitrarily >= 0) + ? roundArbitrarily + : DoubleUtils.nextDown(roundArbitrarily); + case CEILING: + return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily); + case DOWN: + if (sign(x) >= 0) { + return (cmpXToRoundArbitrarily >= 0) + ? roundArbitrarily + : DoubleUtils.nextDown(roundArbitrarily); + } else { + return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily); + } + case UP: + if (sign(x) >= 0) { + return (cmpXToRoundArbitrarily <= 0) ? roundArbitrarily : Math.nextUp(roundArbitrarily); + } else { + return (cmpXToRoundArbitrarily >= 0) + ? roundArbitrarily + : DoubleUtils.nextDown(roundArbitrarily); + } + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + { + X roundFloor; + double roundFloorAsDouble; + X roundCeiling; + double roundCeilingAsDouble; + + if (cmpXToRoundArbitrarily >= 0) { + roundFloorAsDouble = roundArbitrarily; + roundFloor = roundArbitrarilyAsX; + roundCeilingAsDouble = Math.nextUp(roundArbitrarily); + if (roundCeilingAsDouble == Double.POSITIVE_INFINITY) { + return roundFloorAsDouble; + } + roundCeiling = toX(roundCeilingAsDouble, RoundingMode.CEILING); + } else { + roundCeilingAsDouble = roundArbitrarily; + roundCeiling = roundArbitrarilyAsX; + roundFloorAsDouble = DoubleUtils.nextDown(roundArbitrarily); + if (roundFloorAsDouble == Double.NEGATIVE_INFINITY) { + return roundCeilingAsDouble; + } + roundFloor = toX(roundFloorAsDouble, RoundingMode.FLOOR); + } + + X deltaToFloor = minus(x, roundFloor); + X deltaToCeiling = minus(roundCeiling, x); + int diff = deltaToFloor.compareTo(deltaToCeiling); + if (diff < 0) { // closer to floor + return roundFloorAsDouble; + } else if (diff > 0) { // closer to ceiling + return roundCeilingAsDouble; + } + // halfway between the representable values; do the half-whatever logic + switch (mode) { + case HALF_EVEN: + // roundFloorAsDouble and roundCeilingAsDouble are neighbors, so precisely + // one of them should have an even long representation + return ((Double.doubleToRawLongBits(roundFloorAsDouble) & 1L) == 0) + ? roundFloorAsDouble + : roundCeilingAsDouble; + case HALF_DOWN: + return (sign(x) >= 0) ? roundFloorAsDouble : roundCeilingAsDouble; + case HALF_UP: + return (sign(x) >= 0) ? roundCeilingAsDouble : roundFloorAsDouble; + default: + throw new AssertionError("impossible"); + } + } + } + throw new AssertionError("impossible"); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/package-info.java new file mode 100644 index 0000000000000..6911e2a9d5ed8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/math/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.math; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..a2ef61f522c2a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HostAndPort.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HostAndPort.java new file mode 100644 index 0000000000000..abc21658ac65e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HostAndPort.java @@ -0,0 +1,323 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Strings; + +import java.io.Serializable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An immutable representation of a host and port. + * + *

Example usage: + * + *

+ * HostAndPort hp = HostAndPort.fromString("[2001:db8::1]")
+ *     .withDefaultPort(80)
+ *     .requireBracketsForIPv6();
+ * hp.getHost();   // returns "2001:db8::1"
+ * hp.getPort();   // returns 80
+ * hp.toString();  // returns "[2001:db8::1]:80"
+ * 
+ * + *

Here are some examples of recognized formats: + * + *

    + *
  • example.com + *
  • example.com:80 + *
  • 192.0.2.1 + *
  • 192.0.2.1:80 + *
  • [2001:db8::1] - {@link #getHost()} omits brackets + *
  • [2001:db8::1]:80 - {@link #getHost()} omits brackets + *
  • 2001:db8::1 - Use {@link #requireBracketsForIPv6()} to prohibit this + *
+ * + *

Note that this is not an exhaustive list, because these methods are only concerned with + * brackets, colons, and port numbers. Full validation of the host field (if desired) is the + * caller's responsibility. + * + * @author Paul Marks + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class HostAndPort implements Serializable { + /** Magic value indicating the absence of a port number. */ + private static final int NO_PORT = -1; + + /** Hostname, IPv4/IPv6 literal, or unvalidated nonsense. */ + private final String host; + + /** Validated port number in the range [0..65535], or NO_PORT */ + private final int port; + + /** True if the parsed host has colons, but no surrounding brackets. */ + private final boolean hasBracketlessColons; + + private HostAndPort(String host, int port, boolean hasBracketlessColons) { + this.host = host; + this.port = port; + this.hasBracketlessColons = hasBracketlessColons; + } + + /** + * Returns the portion of this {@code HostAndPort} instance that should represent the hostname or + * IPv4/IPv6 literal. + * + *

A successful parse does not imply any degree of sanity in this field. For additional + * validation, see the {@link HostSpecifier} class. + * + * @since 20.0 (since 10.0 as {@code getHostText}) + */ + public String getHost() { + return host; + } + + /** Return true if this instance has a defined port. */ + public boolean hasPort() { + return port >= 0; + } + + /** + * Get the current port number, failing if no port is defined. + * + * @return a validated port number, in the range [0..65535] + * @throws IllegalStateException if no port is defined. You can use {@link #withDefaultPort(int)} + * to prevent this from occurring. + */ + public int getPort() { + checkState(hasPort()); + return port; + } + + /** Returns the current port number, with a default if no port is defined. */ + public int getPortOrDefault(int defaultPort) { + return hasPort() ? port : defaultPort; + } + + /** + * Build a HostAndPort instance from separate host and port values. + * + *

Note: Non-bracketed IPv6 literals are allowed. Use {@link #requireBracketsForIPv6()} to + * prohibit these. + * + * @param host the host string to parse. Must not contain a port number. + * @param port a port number from [0..65535] + * @return if parsing was successful, a populated HostAndPort object. + * @throws IllegalArgumentException if {@code host} contains a port number, or {@code port} is out + * of range. + */ + public static HostAndPort fromParts(String host, int port) { + checkArgument(isValidPort(port), "Port out of range: %s", port); + HostAndPort parsedHost = fromString(host); + checkArgument(!parsedHost.hasPort(), "Host has a port: %s", host); + return new HostAndPort(parsedHost.host, port, parsedHost.hasBracketlessColons); + } + + /** + * Build a HostAndPort instance from a host only. + * + *

Note: Non-bracketed IPv6 literals are allowed. Use {@link #requireBracketsForIPv6()} to + * prohibit these. + * + * @param host the host-only string to parse. Must not contain a port number. + * @return if parsing was successful, a populated HostAndPort object. + * @throws IllegalArgumentException if {@code host} contains a port number. + * @since 17.0 + */ + public static HostAndPort fromHost(String host) { + HostAndPort parsedHost = fromString(host); + checkArgument(!parsedHost.hasPort(), "Host has a port: %s", host); + return parsedHost; + } + + /** + * Split a freeform string into a host and port, without strict validation. + * + *

Note that the host-only formats will leave the port field undefined. You can use {@link + * #withDefaultPort(int)} to patch in a default value. + * + * @param hostPortString the input string to parse. + * @return if parsing was successful, a populated HostAndPort object. + * @throws IllegalArgumentException if nothing meaningful could be parsed. + */ + // TODO(b/219820829): consider removing + public static HostAndPort fromString(String hostPortString) { + checkNotNull(hostPortString); + String host; + String portString = null; + boolean hasBracketlessColons = false; + + if (hostPortString.startsWith("[")) { + String[] hostAndPort = getHostAndPortFromBracketedHost(hostPortString); + host = hostAndPort[0]; + portString = hostAndPort[1]; + } else { + int colonPos = hostPortString.indexOf(':'); + if (colonPos >= 0 && hostPortString.indexOf(':', colonPos + 1) == -1) { + // Exactly 1 colon. Split into host:port. + host = hostPortString.substring(0, colonPos); + portString = hostPortString.substring(colonPos + 1); + } else { + // 0 or 2+ colons. Bare hostname or IPv6 literal. + host = hostPortString; + hasBracketlessColons = (colonPos >= 0); + } + } + + int port = NO_PORT; + if (!Strings.isNullOrEmpty(portString)) { + // Try to parse the whole port string as a number. + // JDK7 accepts leading plus signs. We don't want to. + checkArgument( + !portString.startsWith("+") && CharMatcher.ascii().matchesAllOf(portString), + "Unparseable port number: %s", + hostPortString); + try { + port = Integer.parseInt(portString); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Unparseable port number: " + hostPortString); + } + checkArgument(isValidPort(port), "Port number out of range: %s", hostPortString); + } + + return new HostAndPort(host, port, hasBracketlessColons); + } + + /** + * Parses a bracketed host-port string, throwing IllegalArgumentException if parsing fails. + * + * @param hostPortString the full bracketed host-port specification. Post might not be specified. + * @return an array with 2 strings: host and port, in that order. + * @throws IllegalArgumentException if parsing the bracketed host-port string fails. + */ + private static String[] getHostAndPortFromBracketedHost(String hostPortString) { + checkArgument( + hostPortString.charAt(0) == '[', + "Bracketed host-port string must start with a bracket: %s", + hostPortString); + int colonIndex = hostPortString.indexOf(':'); + int closeBracketIndex = hostPortString.lastIndexOf(']'); + checkArgument( + colonIndex > -1 && closeBracketIndex > colonIndex, + "Invalid bracketed host/port: %s", + hostPortString); + + String host = hostPortString.substring(1, closeBracketIndex); + if (closeBracketIndex + 1 == hostPortString.length()) { + return new String[] {host, ""}; + } else { + checkArgument( + hostPortString.charAt(closeBracketIndex + 1) == ':', + "Only a colon may follow a close bracket: %s", + hostPortString); + for (int i = closeBracketIndex + 2; i < hostPortString.length(); ++i) { + checkArgument( + Character.isDigit(hostPortString.charAt(i)), + "Port must be numeric: %s", + hostPortString); + } + return new String[] {host, hostPortString.substring(closeBracketIndex + 2)}; + } + } + + /** + * Provide a default port if the parsed string contained only a host. + * + *

You can chain this after {@link #fromString(String)} to include a port in case the port was + * omitted from the input string. If a port was already provided, then this method is a no-op. + * + * @param defaultPort a port number, from [0..65535] + * @return a HostAndPort instance, guaranteed to have a defined port. + */ + public HostAndPort withDefaultPort(int defaultPort) { + checkArgument(isValidPort(defaultPort)); + if (hasPort()) { + return this; + } + return new HostAndPort(host, defaultPort, hasBracketlessColons); + } + + /** + * Generate an error if the host might be a non-bracketed IPv6 literal. + * + *

URI formatting requires that IPv6 literals be surrounded by brackets, like "[2001:db8::1]". + * Chain this call after {@link #fromString(String)} to increase the strictness of the parser, and + * disallow IPv6 literals that don't contain these brackets. + * + *

Note that this parser identifies IPv6 literals solely based on the presence of a colon. To + * perform actual validation of IP addresses, see the {@link InetAddresses#forString(String)} + * method. + * + * @return {@code this}, to enable chaining of calls. + * @throws IllegalArgumentException if bracketless IPv6 is detected. + */ + public HostAndPort requireBracketsForIPv6() { + checkArgument(!hasBracketlessColons, "Possible bracketless IPv6 literal: %s", host); + return this; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof HostAndPort) { + HostAndPort that = (HostAndPort) other; + return Objects.equal(this.host, that.host) && this.port == that.port; + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(host, port); + } + + /** Rebuild the host:port string, including brackets if necessary. */ + @Override + public String toString() { + // "[]:12345" requires 8 extra bytes. + StringBuilder builder = new StringBuilder(host.length() + 8); + if (host.indexOf(':') >= 0) { + builder.append('[').append(host).append(']'); + } else { + builder.append(host); + } + if (hasPort()) { + builder.append(':').append(port); + } + return builder.toString(); + } + + /** Return true for valid port numbers. */ + private static boolean isValidPort(int port) { + return port >= 0 && port <= 65535; + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HostSpecifier.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HostSpecifier.java new file mode 100644 index 0000000000000..4c9844a170dde --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HostSpecifier.java @@ -0,0 +1,175 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.net.InetAddress; +import java.text.ParseException; + +/** + * A syntactically valid host specifier, suitable for use in a URI. This may be either a numeric IP + * address in IPv4 or IPv6 notation, or a domain name. + * + *

Because this class is intended to represent host specifiers which can reasonably be used in a + * URI, the domain name case is further restricted to include only those domain names which end in a + * recognized public suffix; see {@link InternetDomainName#isPublicSuffix()} for details. + * + *

Note that no network lookups are performed by any {@code HostSpecifier} methods. No attempt is + * made to verify that a provided specifier corresponds to a real or accessible host. Only syntactic + * and pattern-based checks are performed. + * + *

If you know that a given string represents a numeric IP address, use {@link InetAddresses} to + * obtain and manipulate a {@link java.net.InetAddress} instance from it rather than using this + * class. Similarly, if you know that a given string represents a domain name, use {@link + * InternetDomainName} rather than this class. + * + * @author Craig Berry + * @since 5.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class HostSpecifier { + + private final String canonicalForm; + + private HostSpecifier(String canonicalForm) { + this.canonicalForm = canonicalForm; + } + + /** + * Returns a {@code HostSpecifier} built from the provided {@code specifier}, which is already + * known to be valid. If the {@code specifier} might be invalid, use {@link #from(String)} + * instead. + * + *

The specifier must be in one of these formats: + * + *

    + *
  • A domain name, like {@code google.com} + *
  • A IPv4 address string, like {@code 127.0.0.1} + *
  • An IPv6 address string with or without brackets, like {@code [2001:db8::1]} or {@code + * 2001:db8::1} + *
+ * + * @throws IllegalArgumentException if the specifier is not valid. + */ + public static HostSpecifier fromValid(String specifier) { + // Verify that no port was specified, and strip optional brackets from + // IPv6 literals. + HostAndPort parsedHost = HostAndPort.fromString(specifier); + Preconditions.checkArgument(!parsedHost.hasPort()); + String host = parsedHost.getHost(); + + // Try to interpret the specifier as an IP address. Note we build + // the address rather than using the .is* methods because we want to + // use InetAddresses.toUriString to convert the result to a string in + // canonical form. + InetAddress addr = null; + try { + addr = InetAddresses.forString(host); + } catch (IllegalArgumentException e) { + // It is not an IPv4 or IPv6 literal + } + + if (addr != null) { + return new HostSpecifier(InetAddresses.toUriString(addr)); + } + + // It is not any kind of IP address; must be a domain name or invalid. + + // TODO(user): different versions of this for different factories? + InternetDomainName domain = InternetDomainName.from(host); + + if (domain.hasPublicSuffix()) { + return new HostSpecifier(domain.toString()); + } + + throw new IllegalArgumentException( + "Domain name does not have a recognized public suffix: " + host); + } + + /** + * Attempts to return a {@code HostSpecifier} for the given string, throwing an exception if + * parsing fails. Always use this method in preference to {@link #fromValid(String)} for a + * specifier that is not already known to be valid. + * + * @throws ParseException if the specifier is not valid. + */ + // TODO(b/219820829): consider removing + public static HostSpecifier from(String specifier) throws ParseException { + try { + return fromValid(specifier); + } catch (IllegalArgumentException e) { + // Since the IAE can originate at several different points inside + // fromValid(), we implement this method in terms of that one rather + // than the reverse. + + ParseException parseException = new ParseException("Invalid host specifier: " + specifier, 0); + parseException.initCause(e); + throw parseException; + } + } + + /** + * Determines whether {@code specifier} represents a valid {@link HostSpecifier} as described in + * the documentation for {@link #fromValid(String)}. + */ + public static boolean isValid(String specifier) { + try { + HostSpecifier unused = fromValid(specifier); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other instanceof HostSpecifier) { + HostSpecifier that = (HostSpecifier) other; + return this.canonicalForm.equals(that.canonicalForm); + } + + return false; + } + + @Override + public int hashCode() { + return canonicalForm.hashCode(); + } + + /** + * Returns a string representation of the host specifier suitable for inclusion in a URI. If the + * host specifier is a domain name, the string will be normalized to all lower case. If the + * specifier was an IPv6 address without brackets, brackets are added so that the result will be + * usable in the host part of a URI. + */ + @Override + public String toString() { + return canonicalForm; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HttpHeaders.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HttpHeaders.java new file mode 100644 index 0000000000000..1f92304e27530 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/HttpHeaders.java @@ -0,0 +1,1008 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Contains constant definitions for the HTTP header field names. See: + * + * + * + * @author Kurt Alfred Kluever + * @since 11.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class HttpHeaders { + private HttpHeaders() {} + + // HTTP Request and Response header fields + + /** The HTTP {@code Cache-Control} header field name. */ + public static final String CACHE_CONTROL = "Cache-Control"; + + /** The HTTP {@code Content-Length} header field name. */ + public static final String CONTENT_LENGTH = "Content-Length"; + + /** The HTTP {@code Content-Type} header field name. */ + public static final String CONTENT_TYPE = "Content-Type"; + + /** The HTTP {@code Date} header field name. */ + public static final String DATE = "Date"; + + /** The HTTP {@code Pragma} header field name. */ + public static final String PRAGMA = "Pragma"; + + /** The HTTP {@code Via} header field name. */ + public static final String VIA = "Via"; + + /** The HTTP {@code Warning} header field name. */ + public static final String WARNING = "Warning"; + + // HTTP Request header fields + + /** The HTTP {@code Accept} header field name. */ + public static final String ACCEPT = "Accept"; + + /** The HTTP {@code Accept-Charset} header field name. */ + public static final String ACCEPT_CHARSET = "Accept-Charset"; + + /** The HTTP {@code Accept-Encoding} header field name. */ + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + + /** The HTTP {@code Accept-Language} header field name. */ + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + + /** The HTTP {@code Access-Control-Request-Headers} header field name. */ + public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; + + /** The HTTP {@code Access-Control-Request-Method} header field name. */ + public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; + + /** The HTTP {@code Authorization} header field name. */ + public static final String AUTHORIZATION = "Authorization"; + + /** The HTTP {@code Connection} header field name. */ + public static final String CONNECTION = "Connection"; + + /** The HTTP {@code Cookie} header field name. */ + public static final String COOKIE = "Cookie"; + + /** + * The HTTP {@code + * Cross-Origin-Resource-Policy} header field name. + * + * @since 28.0 + */ + public static final String CROSS_ORIGIN_RESOURCE_POLICY = "Cross-Origin-Resource-Policy"; + + /** + * The HTTP {@code Early-Data} header field + * name. + * + * @since 27.0 + */ + public static final String EARLY_DATA = "Early-Data"; + + /** The HTTP {@code Expect} header field name. */ + public static final String EXPECT = "Expect"; + + /** The HTTP {@code From} header field name. */ + public static final String FROM = "From"; + + /** + * The HTTP {@code Forwarded} header field name. + * + * @since 20.0 + */ + public static final String FORWARDED = "Forwarded"; + + /** + * The HTTP {@code Follow-Only-When-Prerender-Shown} header field name. + * + * @since 17.0 + */ + public static final String FOLLOW_ONLY_WHEN_PRERENDER_SHOWN = "Follow-Only-When-Prerender-Shown"; + + /** The HTTP {@code Host} header field name. */ + public static final String HOST = "Host"; + + /** + * The HTTP {@code HTTP2-Settings} + * header field name. + * + * @since 24.0 + */ + public static final String HTTP2_SETTINGS = "HTTP2-Settings"; + + /** The HTTP {@code If-Match} header field name. */ + public static final String IF_MATCH = "If-Match"; + + /** The HTTP {@code If-Modified-Since} header field name. */ + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + + /** The HTTP {@code If-None-Match} header field name. */ + public static final String IF_NONE_MATCH = "If-None-Match"; + + /** The HTTP {@code If-Range} header field name. */ + public static final String IF_RANGE = "If-Range"; + + /** The HTTP {@code If-Unmodified-Since} header field name. */ + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + + /** The HTTP {@code Last-Event-ID} header field name. */ + public static final String LAST_EVENT_ID = "Last-Event-ID"; + + /** The HTTP {@code Max-Forwards} header field name. */ + public static final String MAX_FORWARDS = "Max-Forwards"; + + /** The HTTP {@code Origin} header field name. */ + public static final String ORIGIN = "Origin"; + + /** + * The HTTP {@code Origin-Isolation} header + * field name. + * + * @since 30.1 + */ + public static final String ORIGIN_ISOLATION = "Origin-Isolation"; + + /** The HTTP {@code Proxy-Authorization} header field name. */ + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + + /** The HTTP {@code Range} header field name. */ + public static final String RANGE = "Range"; + + /** The HTTP {@code Referer} header field name. */ + public static final String REFERER = "Referer"; + + /** + * The HTTP {@code Referrer-Policy} header + * field name. + * + * @since 23.4 + */ + public static final String REFERRER_POLICY = "Referrer-Policy"; + + /** + * Values for the {@code Referrer-Policy} + * header. + * + * @since 23.4 + */ + public static final class ReferrerPolicyValues { + private ReferrerPolicyValues() {} + + public static final String NO_REFERRER = "no-referrer"; + public static final String NO_REFFERER_WHEN_DOWNGRADE = "no-referrer-when-downgrade"; + public static final String SAME_ORIGIN = "same-origin"; + public static final String ORIGIN = "origin"; + public static final String STRICT_ORIGIN = "strict-origin"; + public static final String ORIGIN_WHEN_CROSS_ORIGIN = "origin-when-cross-origin"; + public static final String STRICT_ORIGIN_WHEN_CROSS_ORIGIN = "strict-origin-when-cross-origin"; + public static final String UNSAFE_URL = "unsafe-url"; + } + + /** + * The HTTP {@code + * Service-Worker} header field name. + * + * @since 20.0 + */ + public static final String SERVICE_WORKER = "Service-Worker"; + + /** The HTTP {@code TE} header field name. */ + public static final String TE = "TE"; + + /** The HTTP {@code Upgrade} header field name. */ + public static final String UPGRADE = "Upgrade"; + + /** + * The HTTP {@code + * Upgrade-Insecure-Requests} header field name. + * + * @since 28.1 + */ + public static final String UPGRADE_INSECURE_REQUESTS = "Upgrade-Insecure-Requests"; + + /** The HTTP {@code User-Agent} header field name. */ + public static final String USER_AGENT = "User-Agent"; + + // HTTP Response header fields + + /** The HTTP {@code Accept-Ranges} header field name. */ + public static final String ACCEPT_RANGES = "Accept-Ranges"; + + /** The HTTP {@code Access-Control-Allow-Headers} header field name. */ + public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + + /** The HTTP {@code Access-Control-Allow-Methods} header field name. */ + public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + + /** The HTTP {@code Access-Control-Allow-Origin} header field name. */ + public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + + /** + * The HTTP {@code + * Access-Control-Allow-Private-Network} header field name. + * + * @since 31.1 + */ + public static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = + "Access-Control-Allow-Private-Network"; + + /** The HTTP {@code Access-Control-Allow-Credentials} header field name. */ + public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + + /** The HTTP {@code Access-Control-Expose-Headers} header field name. */ + public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; + + /** The HTTP {@code Access-Control-Max-Age} header field name. */ + public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + + /** The HTTP {@code Age} header field name. */ + public static final String AGE = "Age"; + + /** The HTTP {@code Allow} header field name. */ + public static final String ALLOW = "Allow"; + + /** The HTTP {@code Content-Disposition} header field name. */ + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + + /** The HTTP {@code Content-Encoding} header field name. */ + public static final String CONTENT_ENCODING = "Content-Encoding"; + + /** The HTTP {@code Content-Language} header field name. */ + public static final String CONTENT_LANGUAGE = "Content-Language"; + + /** The HTTP {@code Content-Location} header field name. */ + public static final String CONTENT_LOCATION = "Content-Location"; + + /** The HTTP {@code Content-MD5} header field name. */ + public static final String CONTENT_MD5 = "Content-MD5"; + + /** The HTTP {@code Content-Range} header field name. */ + public static final String CONTENT_RANGE = "Content-Range"; + + /** + * The HTTP {@code + * Content-Security-Policy} header field name. + * + * @since 15.0 + */ + public static final String CONTENT_SECURITY_POLICY = "Content-Security-Policy"; + + /** + * The HTTP + * {@code Content-Security-Policy-Report-Only} header field name. + * + * @since 15.0 + */ + public static final String CONTENT_SECURITY_POLICY_REPORT_ONLY = + "Content-Security-Policy-Report-Only"; + + /** + * The HTTP nonstandard {@code X-Content-Security-Policy} header field name. It was introduced in + * CSP v.1 and used by the Firefox until + * version 23 and the Internet Explorer version 10. Please, use {@link #CONTENT_SECURITY_POLICY} + * to pass the CSP. + * + * @since 20.0 + */ + public static final String X_CONTENT_SECURITY_POLICY = "X-Content-Security-Policy"; + + /** + * The HTTP nonstandard {@code X-Content-Security-Policy-Report-Only} header field name. It was + * introduced in CSP v.1 and used by the + * Firefox until version 23 and the Internet Explorer version 10. Please, use {@link + * #CONTENT_SECURITY_POLICY_REPORT_ONLY} to pass the CSP. + * + * @since 20.0 + */ + public static final String X_CONTENT_SECURITY_POLICY_REPORT_ONLY = + "X-Content-Security-Policy-Report-Only"; + + /** + * The HTTP nonstandard {@code X-WebKit-CSP} header field name. It was introduced in CSP v.1 and used by the Chrome until + * version 25. Please, use {@link #CONTENT_SECURITY_POLICY} to pass the CSP. + * + * @since 20.0 + */ + public static final String X_WEBKIT_CSP = "X-WebKit-CSP"; + + /** + * The HTTP nonstandard {@code X-WebKit-CSP-Report-Only} header field name. It was introduced in + * CSP v.1 and used by the Chrome until + * version 25. Please, use {@link #CONTENT_SECURITY_POLICY_REPORT_ONLY} to pass the CSP. + * + * @since 20.0 + */ + public static final String X_WEBKIT_CSP_REPORT_ONLY = "X-WebKit-CSP-Report-Only"; + + /** + * The HTTP {@code + * Cross-Origin-Embedder-Policy} header field name. + * + * @since 30.0 + */ + public static final String CROSS_ORIGIN_EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy"; + + /** + * The HTTP {@code + * Cross-Origin-Embedder-Policy-Report-Only} header field name. + * + * @since 30.0 + */ + public static final String CROSS_ORIGIN_EMBEDDER_POLICY_REPORT_ONLY = + "Cross-Origin-Embedder-Policy-Report-Only"; + + /** + * The HTTP Cross-Origin-Opener-Policy header field name. + * + * @since 28.2 + */ + public static final String CROSS_ORIGIN_OPENER_POLICY = "Cross-Origin-Opener-Policy"; + + /** The HTTP {@code ETag} header field name. */ + public static final String ETAG = "ETag"; + + /** The HTTP {@code Expires} header field name. */ + public static final String EXPIRES = "Expires"; + + /** The HTTP {@code Last-Modified} header field name. */ + public static final String LAST_MODIFIED = "Last-Modified"; + + /** The HTTP {@code Link} header field name. */ + public static final String LINK = "Link"; + + /** The HTTP {@code Location} header field name. */ + public static final String LOCATION = "Location"; + + /** + * The HTTP {@code Keep-Alive} header field name. + * + * @since 31.0 + */ + public static final String KEEP_ALIVE = "Keep-Alive"; + + /** + * The HTTP {@code + * No-Vary-Seearch} header field name. + * + * @since 32.0.0 + */ + public static final String NO_VARY_SEARCH = "No-Vary-Search"; + + /** + * The HTTP {@code Origin-Trial} + * header field name. + * + * @since 27.1 + */ + public static final String ORIGIN_TRIAL = "Origin-Trial"; + + /** The HTTP {@code P3P} header field name. Limited browser support. */ + public static final String P3P = "P3P"; + + /** The HTTP {@code Proxy-Authenticate} header field name. */ + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + + /** The HTTP {@code Refresh} header field name. Non-standard header supported by most browsers. */ + public static final String REFRESH = "Refresh"; + + /** + * The HTTP {@code Report-To} header field name. + * + * @since 27.1 + */ + public static final String REPORT_TO = "Report-To"; + + /** The HTTP {@code Retry-After} header field name. */ + public static final String RETRY_AFTER = "Retry-After"; + + /** The HTTP {@code Server} header field name. */ + public static final String SERVER = "Server"; + + /** + * The HTTP {@code Server-Timing} header field + * name. + * + * @since 23.6 + */ + public static final String SERVER_TIMING = "Server-Timing"; + + /** + * The HTTP {@code + * Service-Worker-Allowed} header field name. + * + * @since 20.0 + */ + public static final String SERVICE_WORKER_ALLOWED = "Service-Worker-Allowed"; + + /** The HTTP {@code Set-Cookie} header field name. */ + public static final String SET_COOKIE = "Set-Cookie"; + + /** The HTTP {@code Set-Cookie2} header field name. */ + public static final String SET_COOKIE2 = "Set-Cookie2"; + + /** + * The HTTP {@code SourceMap} header field name. + * + * @since 27.1 + */ + public static final String SOURCE_MAP = "SourceMap"; + + /** + * The HTTP {@code + * Supports-Loading-Mode} header field name. This can be used to specify, for example, fenced + * frames. + * + * @since 32.0.0 + */ + public static final String SUPPORTS_LOADING_MODE = "Supports-Loading-Mode"; + + /** + * The HTTP {@code + * Strict-Transport-Security} header field name. + * + * @since 15.0 + */ + public static final String STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security"; + + /** + * The HTTP {@code + * Timing-Allow-Origin} header field name. + * + * @since 15.0 + */ + public static final String TIMING_ALLOW_ORIGIN = "Timing-Allow-Origin"; + + /** The HTTP {@code Trailer} header field name. */ + public static final String TRAILER = "Trailer"; + + /** The HTTP {@code Transfer-Encoding} header field name. */ + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + + /** The HTTP {@code Vary} header field name. */ + public static final String VARY = "Vary"; + + /** The HTTP {@code WWW-Authenticate} header field name. */ + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + + // Common, non-standard HTTP header fields + + /** The HTTP {@code DNT} header field name. */ + public static final String DNT = "DNT"; + + /** The HTTP {@code X-Content-Type-Options} header field name. */ + public static final String X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; + + /** + * The HTTP {@code + * X-Device-IP} header field name. Header used for VAST requests to provide the IP address of + * the device on whose behalf the request is being made. + * + * @since 31.0 + */ + public static final String X_DEVICE_IP = "X-Device-IP"; + + /** + * The HTTP {@code + * X-Device-Referer} header field name. Header used for VAST requests to provide the {@link + * #REFERER} header value that the on-behalf-of client would have used when making a request + * itself. + * + * @since 31.0 + */ + public static final String X_DEVICE_REFERER = "X-Device-Referer"; + + /** + * The HTTP {@code + * X-Device-Accept-Language} header field name. Header used for VAST requests to provide the + * {@link #ACCEPT_LANGUAGE} header value that the on-behalf-of client would have used when making + * a request itself. + * + * @since 31.0 + */ + public static final String X_DEVICE_ACCEPT_LANGUAGE = "X-Device-Accept-Language"; + + /** + * The HTTP {@code + * X-Device-Requested-With} header field name. Header used for VAST requests to provide the + * {@link #X_REQUESTED_WITH} header value that the on-behalf-of client would have used when making + * a request itself. + * + * @since 31.0 + */ + public static final String X_DEVICE_REQUESTED_WITH = "X-Device-Requested-With"; + + /** The HTTP {@code X-Do-Not-Track} header field name. */ + public static final String X_DO_NOT_TRACK = "X-Do-Not-Track"; + + /** The HTTP {@code X-Forwarded-For} header field name (superseded by {@code Forwarded}). */ + public static final String X_FORWARDED_FOR = "X-Forwarded-For"; + + /** The HTTP {@code X-Forwarded-Proto} header field name. */ + public static final String X_FORWARDED_PROTO = "X-Forwarded-Proto"; + + /** + * The HTTP {@code X-Forwarded-Host} header field name. + * + * @since 20.0 + */ + public static final String X_FORWARDED_HOST = "X-Forwarded-Host"; + + /** + * The HTTP {@code X-Forwarded-Port} header field name. + * + * @since 20.0 + */ + public static final String X_FORWARDED_PORT = "X-Forwarded-Port"; + + /** The HTTP {@code X-Frame-Options} header field name. */ + public static final String X_FRAME_OPTIONS = "X-Frame-Options"; + + /** The HTTP {@code X-Powered-By} header field name. */ + public static final String X_POWERED_BY = "X-Powered-By"; + + /** + * The HTTP {@code + * Public-Key-Pins} header field name. + * + * @since 15.0 + */ + public static final String PUBLIC_KEY_PINS = "Public-Key-Pins"; + + /** + * The HTTP {@code + * Public-Key-Pins-Report-Only} header field name. + * + * @since 15.0 + */ + public static final String PUBLIC_KEY_PINS_REPORT_ONLY = "Public-Key-Pins-Report-Only"; + + /** + * The HTTP {@code X-Request-ID} header field name. + * + * @since 30.1 + */ + public static final String X_REQUEST_ID = "X-Request-ID"; + + /** The HTTP {@code X-Requested-With} header field name. */ + public static final String X_REQUESTED_WITH = "X-Requested-With"; + + /** The HTTP {@code X-User-IP} header field name. */ + public static final String X_USER_IP = "X-User-IP"; + + /** + * The HTTP {@code X-Download-Options} header field name. + * + *

When the new X-Download-Options header is present with the value {@code noopen}, the user is + * prevented from opening a file download directly; instead, they must first save the file + * locally. + * + * @since 24.1 + */ + public static final String X_DOWNLOAD_OPTIONS = "X-Download-Options"; + + /** The HTTP {@code X-XSS-Protection} header field name. */ + public static final String X_XSS_PROTECTION = "X-XSS-Protection"; + + /** + * The HTTP {@code + * X-DNS-Prefetch-Control} header controls DNS prefetch behavior. Value can be "on" or "off". + * By default, DNS prefetching is "on" for HTTP pages and "off" for HTTPS pages. + */ + public static final String X_DNS_PREFETCH_CONTROL = "X-DNS-Prefetch-Control"; + + /** + * The HTTP + * {@code Ping-From} header field name. + * + * @since 19.0 + */ + public static final String PING_FROM = "Ping-From"; + + /** + * The HTTP + * {@code Ping-To} header field name. + * + * @since 19.0 + */ + public static final String PING_TO = "Ping-To"; + + /** + * The HTTP {@code + * Purpose} header field name. + * + * @since 28.0 + */ + public static final String PURPOSE = "Purpose"; + + /** + * The HTTP {@code + * X-Purpose} header field name. + * + * @since 28.0 + */ + public static final String X_PURPOSE = "X-Purpose"; + + /** + * The HTTP {@code + * X-Moz} header field name. + * + * @since 28.0 + */ + public static final String X_MOZ = "X-Moz"; + + /** + * The HTTP {@code + * Device-Memory} header field name. + * + * @since 31.0 + */ + public static final String DEVICE_MEMORY = "Device-Memory"; + + /** + * The HTTP {@code + * Downlink} header field name. + * + * @since 31.0 + */ + public static final String DOWNLINK = "Downlink"; + + /** + * The HTTP {@code + * ECT} header field name. + * + * @since 31.0 + */ + public static final String ECT = "ECT"; + + /** + * The HTTP {@code + * RTT} header field name. + * + * @since 31.0 + */ + public static final String RTT = "RTT"; + + /** + * The HTTP {@code + * Save-Data} header field name. + * + * @since 31.0 + */ + public static final String SAVE_DATA = "Save-Data"; + + /** + * The HTTP {@code + * Viewport-Width} header field name. + * + * @since 31.0 + */ + public static final String VIEWPORT_WIDTH = "Viewport-Width"; + + /** + * The HTTP {@code + * Width} header field name. + * + * @since 31.0 + */ + public static final String WIDTH = "Width"; + + /** + * The HTTP {@code Permissions-Policy} + * header field name. + * + * @since 31.0 + */ + public static final String PERMISSIONS_POLICY = "Permissions-Policy"; + + /** + * The HTTP {@code + * Sec-CH-Prefers-Color-Scheme} header field name. + * + *

This header is experimental. + * + * @since 31.0 + */ + public static final String SEC_CH_PREFERS_COLOR_SCHEME = "Sec-CH-Prefers-Color-Scheme"; + + /** + * The HTTP {@code + * Accept-CH} header field name. + * + * @since 31.0 + */ + public static final String ACCEPT_CH = "Accept-CH"; + + /** + * The HTTP {@code + * Critical-CH} header field name. + * + * @since 31.0 + */ + public static final String CRITICAL_CH = "Critical-CH"; + + /** + * The HTTP {@code Sec-CH-UA} + * header field name. + * + * @since 30.0 + */ + public static final String SEC_CH_UA = "Sec-CH-UA"; + + /** + * The HTTP {@code + * Sec-CH-UA-Arch} header field name. + * + * @since 30.0 + */ + public static final String SEC_CH_UA_ARCH = "Sec-CH-UA-Arch"; + + /** + * The HTTP {@code + * Sec-CH-UA-Model} header field name. + * + * @since 30.0 + */ + public static final String SEC_CH_UA_MODEL = "Sec-CH-UA-Model"; + + /** + * The HTTP {@code + * Sec-CH-UA-Platform} header field name. + * + * @since 30.0 + */ + public static final String SEC_CH_UA_PLATFORM = "Sec-CH-UA-Platform"; + + /** + * The HTTP {@code + * Sec-CH-UA-Platform-Version} header field name. + * + * @since 30.0 + */ + public static final String SEC_CH_UA_PLATFORM_VERSION = "Sec-CH-UA-Platform-Version"; + + /** + * The HTTP {@code + * Sec-CH-UA-Full-Version} header field name. + * + * @deprecated Prefer {@link SEC_CH_UA_FULL_VERSION_LIST}. + * @since 30.0 + */ + @Deprecated public static final String SEC_CH_UA_FULL_VERSION = "Sec-CH-UA-Full-Version"; + + /** + * The HTTP {@code + * Sec-CH-UA-Full-Version} header field name. + * + * @since 31.1 + */ + public static final String SEC_CH_UA_FULL_VERSION_LIST = "Sec-CH-UA-Full-Version-List"; + + /** + * The HTTP {@code + * Sec-CH-UA-Mobile} header field name. + * + * @since 30.0 + */ + public static final String SEC_CH_UA_MOBILE = "Sec-CH-UA-Mobile"; + + /** + * The HTTP {@code + * Sec-CH-UA-WoW64} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_UA_WOW64 = "Sec-CH-UA-WoW64"; + + /** + * The HTTP {@code + * Sec-CH-UA-Bitness} header field name. + * + * @since 31.0 + */ + public static final String SEC_CH_UA_BITNESS = "Sec-CH-UA-Bitness"; + + /** + * The HTTP {@code + * Sec-CH-UA-Form-Factor} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_UA_FORM_FACTOR = "Sec-CH-UA-Form-Factor"; + + /** + * The HTTP {@code + * Sec-CH-Viewport-Width} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_VIEWPORT_WIDTH = "Sec-CH-Viewport-Width"; + + /** + * The HTTP {@code + * Sec-CH-Viewport-Height} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_VIEWPORT_HEIGHT = "Sec-CH-Viewport-Height"; + + /** + * The HTTP {@code + * Sec-CH-DPR} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_CH_DPR = "Sec-CH-DPR"; + + /** + * The HTTP {@code Sec-Fetch-Dest} + * header field name. + * + * @since 27.1 + */ + public static final String SEC_FETCH_DEST = "Sec-Fetch-Dest"; + + /** + * The HTTP {@code Sec-Fetch-Mode} + * header field name. + * + * @since 27.1 + */ + public static final String SEC_FETCH_MODE = "Sec-Fetch-Mode"; + + /** + * The HTTP {@code Sec-Fetch-Site} + * header field name. + * + * @since 27.1 + */ + public static final String SEC_FETCH_SITE = "Sec-Fetch-Site"; + + /** + * The HTTP {@code Sec-Fetch-User} + * header field name. + * + * @since 27.1 + */ + public static final String SEC_FETCH_USER = "Sec-Fetch-User"; + + /** + * The HTTP {@code Sec-Metadata} + * header field name. + * + * @since 26.0 + */ + public static final String SEC_METADATA = "Sec-Metadata"; + + /** + * The HTTP {@code + * Sec-Token-Binding} header field name. + * + * @since 25.1 + */ + public static final String SEC_TOKEN_BINDING = "Sec-Token-Binding"; + + /** + * The HTTP {@code + * Sec-Provided-Token-Binding-ID} header field name. + * + * @since 25.1 + */ + public static final String SEC_PROVIDED_TOKEN_BINDING_ID = "Sec-Provided-Token-Binding-ID"; + + /** + * The HTTP {@code + * Sec-Referred-Token-Binding-ID} header field name. + * + * @since 25.1 + */ + public static final String SEC_REFERRED_TOKEN_BINDING_ID = "Sec-Referred-Token-Binding-ID"; + + /** + * The HTTP {@code Sec-WebSocket-Accept} header + * field name. + * + * @since 28.0 + */ + public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; + + /** + * The HTTP {@code Sec-WebSocket-Extensions} + * header field name. + * + * @since 28.0 + */ + public static final String SEC_WEBSOCKET_EXTENSIONS = "Sec-WebSocket-Extensions"; + + /** + * The HTTP {@code Sec-WebSocket-Key} header + * field name. + * + * @since 28.0 + */ + public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; + + /** + * The HTTP {@code Sec-WebSocket-Protocol} + * header field name. + * + * @since 28.0 + */ + public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; + + /** + * The HTTP {@code Sec-WebSocket-Version} header + * field name. + * + * @since 28.0 + */ + public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; + + /** + * The HTTP {@code + * Sec-Browsing-Topics} header field name. + * + * @since 32.0.0 + */ + public static final String SEC_BROWSING_TOPICS = "Sec-Browsing-Topics"; + + /** + * The HTTP {@code + * Observe-Browsing-Topics} header field name. + * + * @since 32.0.0 + */ + public static final String OBSERVE_BROWSING_TOPICS = "Observe-Browsing-Topics"; + + /** + * The HTTP {@code CDN-Loop} header field name. + * + * @since 28.0 + */ + public static final String CDN_LOOP = "CDN-Loop"; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/InetAddresses.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/InetAddresses.java new file mode 100644 index 0000000000000..a7da67ef70734 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/InetAddresses.java @@ -0,0 +1,1156 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.hash.Hashing; +import org.apache.iotdb.google.common.io.ByteStreams; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Locale; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@link InetAddress} instances. + * + *

Important note: Unlike {@code InetAddress.getByName()}, the methods of this class never + * cause DNS services to be accessed. For this reason, you should prefer these methods as much as + * possible over their JDK equivalents whenever you are expecting to handle only IP address string + * literals -- there is no blocking DNS penalty for a malformed string. + * + *

When dealing with {@link Inet4Address} and {@link Inet6Address} objects as byte arrays (vis. + * {@code InetAddress.getAddress()}) they are 4 and 16 bytes in length, respectively, and represent + * the address in network byte order. + * + *

Examples of IP addresses and their byte representations: + * + *

+ *
The IPv4 loopback address, {@code "127.0.0.1"}. + *
{@code 7f 00 00 01} + *
The IPv6 loopback address, {@code "::1"}. + *
{@code 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01} + *
From the IPv6 reserved documentation prefix ({@code 2001:db8::/32}), {@code "2001:db8::1"}. + *
{@code 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 01} + *
An IPv6 "IPv4 compatible" (or "compat") address, {@code "::192.168.0.1"}. + *
{@code 00 00 00 00 00 00 00 00 00 00 00 00 c0 a8 00 01} + *
An IPv6 "IPv4 mapped" address, {@code "::ffff:192.168.0.1"}. + *
{@code 00 00 00 00 00 00 00 00 00 00 ff ff c0 a8 00 01} + *
+ * + *

A few notes about IPv6 "IPv4 mapped" addresses and their observed use in Java. + * + *

"IPv4 mapped" addresses were originally a representation of IPv4 addresses for use on an IPv6 + * socket that could receive both IPv4 and IPv6 connections (by disabling the {@code IPV6_V6ONLY} + * socket option on an IPv6 socket). Yes, it's confusing. Nevertheless, these "mapped" addresses + * were never supposed to be seen on the wire. That assumption was dropped, some say mistakenly, in + * later RFCs with the apparent aim of making IPv4-to-IPv6 transition simpler. + * + *

Technically one can create a 128bit IPv6 address with the wire format of a "mapped" + * address, as shown above, and transmit it in an IPv6 packet header. However, Java's InetAddress + * creation methods appear to adhere doggedly to the original intent of the "mapped" address: all + * "mapped" addresses return {@link Inet4Address} objects. + * + *

For added safety, it is common for IPv6 network operators to filter all packets where either + * the source or destination address appears to be a "compat" or "mapped" address. Filtering + * suggestions usually recommend discarding any packets with source or destination addresses in the + * invalid range {@code ::/3}, which includes both of these bizarre address formats. For more + * information on "bogons", including lists of IPv6 bogon space, see: + * + *

+ * + * @author Erik Kline + * @since 5.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class InetAddresses { + private static final int IPV4_PART_COUNT = 4; + private static final int IPV6_PART_COUNT = 8; + private static final char IPV4_DELIMITER = '.'; + private static final char IPV6_DELIMITER = ':'; + private static final CharMatcher IPV4_DELIMITER_MATCHER = CharMatcher.is(IPV4_DELIMITER); + private static final CharMatcher IPV6_DELIMITER_MATCHER = CharMatcher.is(IPV6_DELIMITER); + private static final Inet4Address LOOPBACK4 = (Inet4Address) forString("127.0.0.1"); + private static final Inet4Address ANY4 = (Inet4Address) forString("0.0.0.0"); + + private InetAddresses() {} + + /** + * Returns an {@link Inet4Address}, given a byte array representation of the IPv4 address. + * + * @param bytes byte array representing an IPv4 address (should be of length 4) + * @return {@link Inet4Address} corresponding to the supplied byte array + * @throws IllegalArgumentException if a valid {@link Inet4Address} can not be created + */ + private static Inet4Address getInet4Address(byte[] bytes) { + checkArgument( + bytes.length == 4, + "Byte array has invalid length for an IPv4 address: %s != 4.", + bytes.length); + + // Given a 4-byte array, this cast should always succeed. + return (Inet4Address) bytesToInetAddress(bytes); + } + + /** + * Returns the {@link InetAddress} having the given string representation. + * + *

This deliberately avoids all nameservice lookups (e.g. no DNS). + * + *

Anything after a {@code %} in an IPv6 address is ignored (assumed to be a Scope ID). + * + *

This method accepts non-ASCII digits, for example {@code "192.168.0.1"} (those are fullwidth + * characters). That is consistent with {@link InetAddress}, but not with various RFCs. If you + * want to accept ASCII digits only, you can use something like {@code + * CharMatcher.ascii().matchesAllOf(ipString)}. + * + * @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. {@code + * "192.168.0.1"} or {@code "2001:db8::1"} + * @return {@link InetAddress} representing the argument + * @throws IllegalArgumentException if the argument is not a valid IP string literal + */ + // TODO(b/219820829): consider removing + public static InetAddress forString(String ipString) { + byte[] addr = ipStringToBytes(ipString); + + // The argument was malformed, i.e. not an IP string literal. + if (addr == null) { + throw formatIllegalArgumentException("'%s' is not an IP string literal.", ipString); + } + + return bytesToInetAddress(addr); + } + + /** + * Returns {@code true} if the supplied string is a valid IP string literal, {@code false} + * otherwise. + * + *

This method accepts non-ASCII digits, for example {@code "192.168.0.1"} (those are fullwidth + * characters). That is consistent with {@link InetAddress}, but not with various RFCs. If you + * want to accept ASCII digits only, you can use something like {@code + * CharMatcher.ascii().matchesAllOf(ipString)}. + * + * @param ipString {@code String} to evaluated as an IP string literal + * @return {@code true} if the argument is a valid IP string literal + */ + public static boolean isInetAddress(String ipString) { + return ipStringToBytes(ipString) != null; + } + + /** Returns {@code null} if unable to parse into a {@code byte[]}. */ + private static byte[] ipStringToBytes(String ipStringParam) { + String ipString = ipStringParam; + // Make a first pass to categorize the characters in this string. + boolean hasColon = false; + boolean hasDot = false; + int percentIndex = -1; + for (int i = 0; i < ipString.length(); i++) { + char c = ipString.charAt(i); + if (c == '.') { + hasDot = true; + } else if (c == ':') { + if (hasDot) { + return null; // Colons must not appear after dots. + } + hasColon = true; + } else if (c == '%') { + percentIndex = i; + break; // everything after a '%' is ignored (it's a Scope ID): http://superuser.com/a/99753 + } else if (Character.digit(c, 16) == -1) { + return null; // Everything else must be a decimal or hex digit. + } + } + + // Now decide which address family to parse. + if (hasColon) { + if (hasDot) { + ipString = convertDottedQuadToHex(ipString); + if (ipString == null) { + return null; + } + } + if (percentIndex != -1) { + ipString = ipString.substring(0, percentIndex); + } + return textToNumericFormatV6(ipString); + } else if (hasDot) { + if (percentIndex != -1) { + return null; // Scope IDs are not supported for IPV4 + } + return textToNumericFormatV4(ipString); + } + return null; + } + + private static byte[] textToNumericFormatV4(String ipString) { + if (IPV4_DELIMITER_MATCHER.countIn(ipString) + 1 != IPV4_PART_COUNT) { + return null; // Wrong number of parts + } + + byte[] bytes = new byte[IPV4_PART_COUNT]; + int start = 0; + // Iterate through the parts of the ip string. + // Invariant: start is always the beginning of an octet. + for (int i = 0; i < IPV4_PART_COUNT; i++) { + int end = ipString.indexOf(IPV4_DELIMITER, start); + if (end == -1) { + end = ipString.length(); + } + try { + bytes[i] = parseOctet(ipString, start, end); + } catch (NumberFormatException ex) { + return null; + } + start = end + 1; + } + + return bytes; + } + + private static byte[] textToNumericFormatV6(String ipString) { + // An address can have [2..8] colons. + int delimiterCount = IPV6_DELIMITER_MATCHER.countIn(ipString); + if (delimiterCount < 2 || delimiterCount > IPV6_PART_COUNT) { + return null; + } + int partsSkipped = IPV6_PART_COUNT - (delimiterCount + 1); // estimate; may be modified later + boolean hasSkip = false; + // Scan for the appearance of ::, to mark a skip-format IPV6 string and adjust the partsSkipped + // estimate. + for (int i = 0; i < ipString.length() - 1; i++) { + if (ipString.charAt(i) == IPV6_DELIMITER && ipString.charAt(i + 1) == IPV6_DELIMITER) { + if (hasSkip) { + return null; // Can't have more than one :: + } + hasSkip = true; + partsSkipped++; // :: means we skipped an extra part in between the two delimiters. + if (i == 0) { + partsSkipped++; // Begins with ::, so we skipped the part preceding the first : + } + if (i == ipString.length() - 2) { + partsSkipped++; // Ends with ::, so we skipped the part after the last : + } + } + } + if (ipString.charAt(0) == IPV6_DELIMITER && ipString.charAt(1) != IPV6_DELIMITER) { + return null; // ^: requires ^:: + } + if (ipString.charAt(ipString.length() - 1) == IPV6_DELIMITER + && ipString.charAt(ipString.length() - 2) != IPV6_DELIMITER) { + return null; // :$ requires ::$ + } + if (hasSkip && partsSkipped <= 0) { + return null; // :: must expand to at least one '0' + } + if (!hasSkip && delimiterCount + 1 != IPV6_PART_COUNT) { + return null; // Incorrect number of parts + } + + ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT); + try { + // Iterate through the parts of the ip string. + // Invariant: start is always the beginning of a hextet, or the second ':' of the skip + // sequence "::" + int start = 0; + if (ipString.charAt(0) == IPV6_DELIMITER) { + start = 1; + } + while (start < ipString.length()) { + int end = ipString.indexOf(IPV6_DELIMITER, start); + if (end == -1) { + end = ipString.length(); + } + if (ipString.charAt(start) == IPV6_DELIMITER) { + // expand zeroes + for (int i = 0; i < partsSkipped; i++) { + rawBytes.putShort((short) 0); + } + + } else { + rawBytes.putShort(parseHextet(ipString, start, end)); + } + start = end + 1; + } + } catch (NumberFormatException ex) { + return null; + } + return rawBytes.array(); + } + + private static String convertDottedQuadToHex(String ipString) { + int lastColon = ipString.lastIndexOf(':'); + String initialPart = ipString.substring(0, lastColon + 1); + String dottedQuad = ipString.substring(lastColon + 1); + byte[] quad = textToNumericFormatV4(dottedQuad); + if (quad == null) { + return null; + } + String penultimate = Integer.toHexString(((quad[0] & 0xff) << 8) | (quad[1] & 0xff)); + String ultimate = Integer.toHexString(((quad[2] & 0xff) << 8) | (quad[3] & 0xff)); + return initialPart + penultimate + ":" + ultimate; + } + + private static byte parseOctet(String ipString, int start, int end) { + // Note: we already verified that this string contains only hex digits, but the string may still + // contain non-decimal characters. + int length = end - start; + if (length <= 0 || length > 3) { + throw new NumberFormatException(); + } + // Disallow leading zeroes, because no clear standard exists on + // whether these should be interpreted as decimal or octal. + if (length > 1 && ipString.charAt(start) == '0') { + throw new NumberFormatException(); + } + int octet = 0; + for (int i = start; i < end; i++) { + octet *= 10; + int digit = Character.digit(ipString.charAt(i), 10); + if (digit < 0) { + throw new NumberFormatException(); + } + octet += digit; + } + if (octet > 255) { + throw new NumberFormatException(); + } + return (byte) octet; + } + + // Parse a hextet out of the ipString from start (inclusive) to end (exclusive) + private static short parseHextet(String ipString, int start, int end) { + // Note: we already verified that this string contains only hex digits. + int length = end - start; + if (length <= 0 || length > 4) { + throw new NumberFormatException(); + } + int hextet = 0; + for (int i = start; i < end; i++) { + hextet = hextet << 4; + hextet |= Character.digit(ipString.charAt(i), 16); + } + return (short) hextet; + } + + /** + * Convert a byte array into an InetAddress. + * + *

{@link InetAddress#getByAddress} is documented as throwing a checked exception "if IP + * address is of illegal length." We replace it with an unchecked exception, for use by callers + * who already know that addr is an array of length 4 or 16. + * + * @param addr the raw 4-byte or 16-byte IP address in big-endian order + * @return an InetAddress object created from the raw IP address + */ + private static InetAddress bytesToInetAddress(byte[] addr) { + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + throw new AssertionError(e); + } + } + + /** + * Returns the string representation of an {@link InetAddress}. + * + *

For IPv4 addresses, this is identical to {@link InetAddress#getHostAddress()}, but for IPv6 + * addresses, the output follows RFC 5952 section + * 4. The main difference is that this method uses "::" for zero compression, while Java's version + * uses the uncompressed form. + * + *

This method uses hexadecimal for all IPv6 addresses, including IPv4-mapped IPv6 addresses + * such as "::c000:201". The output does not include a Scope ID. + * + * @param ip {@link InetAddress} to be converted to an address string + * @return {@code String} containing the text-formatted IP address + * @since 10.0 + */ + public static String toAddrString(InetAddress ip) { + checkNotNull(ip); + if (ip instanceof Inet4Address) { + // For IPv4, Java's formatting is good enough. + return ip.getHostAddress(); + } + checkArgument(ip instanceof Inet6Address); + byte[] bytes = ip.getAddress(); + int[] hextets = new int[IPV6_PART_COUNT]; + for (int i = 0; i < hextets.length; i++) { + hextets[i] = Ints.fromBytes((byte) 0, (byte) 0, bytes[2 * i], bytes[2 * i + 1]); + } + compressLongestRunOfZeroes(hextets); + return hextetsToIPv6String(hextets); + } + + /** + * Identify and mark the longest run of zeroes in an IPv6 address. + * + *

Only runs of two or more hextets are considered. In case of a tie, the leftmost run wins. If + * a qualifying run is found, its hextets are replaced by the sentinel value -1. + * + * @param hextets {@code int[]} mutable array of eight 16-bit hextets + */ + private static void compressLongestRunOfZeroes(int[] hextets) { + int bestRunStart = -1; + int bestRunLength = -1; + int runStart = -1; + for (int i = 0; i < hextets.length + 1; i++) { + if (i < hextets.length && hextets[i] == 0) { + if (runStart < 0) { + runStart = i; + } + } else if (runStart >= 0) { + int runLength = i - runStart; + if (runLength > bestRunLength) { + bestRunStart = runStart; + bestRunLength = runLength; + } + runStart = -1; + } + } + if (bestRunLength >= 2) { + Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1); + } + } + + /** + * Convert a list of hextets into a human-readable IPv6 address. + * + *

In order for "::" compression to work, the input should contain negative sentinel values in + * place of the elided zeroes. + * + * @param hextets {@code int[]} array of eight 16-bit hextets, or -1s + */ + private static String hextetsToIPv6String(int[] hextets) { + // While scanning the array, handle these state transitions: + // start->num => "num" start->gap => "::" + // num->num => ":num" num->gap => "::" + // gap->num => "num" gap->gap => "" + StringBuilder buf = new StringBuilder(39); + boolean lastWasNumber = false; + for (int i = 0; i < hextets.length; i++) { + boolean thisIsNumber = hextets[i] >= 0; + if (thisIsNumber) { + if (lastWasNumber) { + buf.append(':'); + } + buf.append(Integer.toHexString(hextets[i])); + } else { + if (i == 0 || lastWasNumber) { + buf.append("::"); + } + } + lastWasNumber = thisIsNumber; + } + return buf.toString(); + } + + /** + * Returns the string representation of an {@link InetAddress} suitable for inclusion in a URI. + * + *

For IPv4 addresses, this is identical to {@link InetAddress#getHostAddress()}, but for IPv6 + * addresses it compresses zeroes and surrounds the text with square brackets; for example {@code + * "[2001:db8::1]"}. + * + *

Per section 3.2.2 of RFC 3986, a URI containing an IPv6 + * string literal is of the form {@code "http://[2001:db8::1]:8888/index.html"}. + * + *

Use of either {@link InetAddresses#toAddrString}, {@link InetAddress#getHostAddress()}, or + * this method is recommended over {@link InetAddress#toString()} when an IP address string + * literal is desired. This is because {@link InetAddress#toString()} prints the hostname and the + * IP address string joined by a "/". + * + * @param ip {@link InetAddress} to be converted to URI string literal + * @return {@code String} containing URI-safe string literal + */ + public static String toUriString(InetAddress ip) { + if (ip instanceof Inet6Address) { + return "[" + toAddrString(ip) + "]"; + } + return toAddrString(ip); + } + + /** + * Returns an InetAddress representing the literal IPv4 or IPv6 host portion of a URL, encoded in + * the format specified by RFC 3986 section 3.2.2. + * + *

This method is similar to {@link InetAddresses#forString(String)}, however, it requires that + * IPv6 addresses are surrounded by square brackets. + * + *

This method is the inverse of {@link InetAddresses#toUriString(java.net.InetAddress)}. + * + *

This method accepts non-ASCII digits, for example {@code "192.168.0.1"} (those are fullwidth + * characters). That is consistent with {@link InetAddress}, but not with various RFCs. If you + * want to accept ASCII digits only, you can use something like {@code + * CharMatcher.ascii().matchesAllOf(ipString)}. + * + * @param hostAddr an RFC 3986 section 3.2.2 encoded IPv4 or IPv6 address + * @return an InetAddress representing the address in {@code hostAddr} + * @throws IllegalArgumentException if {@code hostAddr} is not a valid IPv4 address, or IPv6 + * address surrounded by square brackets + */ + public static InetAddress forUriString(String hostAddr) { + InetAddress addr = forUriStringNoThrow(hostAddr); + if (addr == null) { + throw formatIllegalArgumentException("Not a valid URI IP literal: '%s'", hostAddr); + } + + return addr; + } + + private static InetAddress forUriStringNoThrow(String hostAddr) { + checkNotNull(hostAddr); + + // Decide if this should be an IPv6 or IPv4 address. + String ipString; + int expectBytes; + if (hostAddr.startsWith("[") && hostAddr.endsWith("]")) { + ipString = hostAddr.substring(1, hostAddr.length() - 1); + expectBytes = 16; + } else { + ipString = hostAddr; + expectBytes = 4; + } + + // Parse the address, and make sure the length/version is correct. + byte[] addr = ipStringToBytes(ipString); + if (addr == null || addr.length != expectBytes) { + return null; + } + + return bytesToInetAddress(addr); + } + + /** + * Returns {@code true} if the supplied string is a valid URI IP string literal, {@code false} + * otherwise. + * + *

This method accepts non-ASCII digits, for example {@code "192.168.0.1"} (those are fullwidth + * characters). That is consistent with {@link InetAddress}, but not with various RFCs. If you + * want to accept ASCII digits only, you can use something like {@code + * CharMatcher.ascii().matchesAllOf(ipString)}. + * + * @param ipString {@code String} to evaluated as an IP URI host string literal + * @return {@code true} if the argument is a valid IP URI host + */ + public static boolean isUriInetAddress(String ipString) { + return forUriStringNoThrow(ipString) != null; + } + + /** + * Evaluates whether the argument is an IPv6 "compat" address. + * + *

An "IPv4 compatible", or "compat", address is one with 96 leading bits of zero, with the + * remaining 32 bits interpreted as an IPv4 address. These are conventionally represented in + * string literals as {@code "::192.168.0.1"}, though {@code "::c0a8:1"} is also considered an + * IPv4 compatible address (and equivalent to {@code "::192.168.0.1"}). + * + *

For more on IPv4 compatible addresses see section 2.5.5.1 of RFC 4291. + * + *

NOTE: This method is different from {@link Inet6Address#isIPv4CompatibleAddress} in that it + * more correctly classifies {@code "::"} and {@code "::1"} as proper IPv6 addresses (which they + * are), NOT IPv4 compatible addresses (which they are generally NOT considered to be). + * + * @param ip {@link Inet6Address} to be examined for embedded IPv4 compatible address format + * @return {@code true} if the argument is a valid "compat" address + */ + public static boolean isCompatIPv4Address(Inet6Address ip) { + if (!ip.isIPv4CompatibleAddress()) { + return false; + } + + byte[] bytes = ip.getAddress(); + if ((bytes[12] == 0) + && (bytes[13] == 0) + && (bytes[14] == 0) + && ((bytes[15] == 0) || (bytes[15] == 1))) { + return false; + } + + return true; + } + + /** + * Returns the IPv4 address embedded in an IPv4 compatible address. + * + * @param ip {@link Inet6Address} to be examined for an embedded IPv4 address + * @return {@link Inet4Address} of the embedded IPv4 address + * @throws IllegalArgumentException if the argument is not a valid IPv4 compatible address + */ + public static Inet4Address getCompatIPv4Address(Inet6Address ip) { + checkArgument( + isCompatIPv4Address(ip), "Address '%s' is not IPv4-compatible.", toAddrString(ip)); + + return getInet4Address(Arrays.copyOfRange(ip.getAddress(), 12, 16)); + } + + /** + * Evaluates whether the argument is a 6to4 address. + * + *

6to4 addresses begin with the {@code "2002::/16"} prefix. The next 32 bits are the IPv4 + * address of the host to which IPv6-in-IPv4 tunneled packets should be routed. + * + *

For more on 6to4 addresses see section 2 of RFC 3056. + * + * @param ip {@link Inet6Address} to be examined for 6to4 address format + * @return {@code true} if the argument is a 6to4 address + */ + public static boolean is6to4Address(Inet6Address ip) { + byte[] bytes = ip.getAddress(); + return (bytes[0] == (byte) 0x20) && (bytes[1] == (byte) 0x02); + } + + /** + * Returns the IPv4 address embedded in a 6to4 address. + * + * @param ip {@link Inet6Address} to be examined for embedded IPv4 in 6to4 address + * @return {@link Inet4Address} of embedded IPv4 in 6to4 address + * @throws IllegalArgumentException if the argument is not a valid IPv6 6to4 address + */ + public static Inet4Address get6to4IPv4Address(Inet6Address ip) { + checkArgument(is6to4Address(ip), "Address '%s' is not a 6to4 address.", toAddrString(ip)); + + return getInet4Address(Arrays.copyOfRange(ip.getAddress(), 2, 6)); + } + + /** + * A simple immutable data class to encapsulate the information to be found in a Teredo address. + * + *

All of the fields in this class are encoded in various portions of the IPv6 address as part + * of the protocol. More protocols details can be found at: http://en.wikipedia. + * org/wiki/Teredo_tunneling. + * + *

The RFC can be found here: RFC + * 4380. + * + * @since 5.0 + */ + public static final class TeredoInfo { + private final Inet4Address server; + private final Inet4Address client; + private final int port; + private final int flags; + + /** + * Constructs a TeredoInfo instance. + * + *

Both server and client can be {@code null}, in which case the value {@code "0.0.0.0"} will + * be assumed. + * + * @throws IllegalArgumentException if either of the {@code port} or the {@code flags} arguments + * are out of range of an unsigned short + */ + // TODO: why is this public? + public TeredoInfo(Inet4Address server, Inet4Address client, int port, int flags) { + checkArgument( + (port >= 0) && (port <= 0xffff), "port '%s' is out of range (0 <= port <= 0xffff)", port); + checkArgument( + (flags >= 0) && (flags <= 0xffff), + "flags '%s' is out of range (0 <= flags <= 0xffff)", + flags); + + this.server = MoreObjects.firstNonNull(server, ANY4); + this.client = MoreObjects.firstNonNull(client, ANY4); + this.port = port; + this.flags = flags; + } + + public Inet4Address getServer() { + return server; + } + + public Inet4Address getClient() { + return client; + } + + public int getPort() { + return port; + } + + public int getFlags() { + return flags; + } + } + + /** + * Evaluates whether the argument is a Teredo address. + * + *

Teredo addresses begin with the {@code "2001::/32"} prefix. + * + * @param ip {@link Inet6Address} to be examined for Teredo address format + * @return {@code true} if the argument is a Teredo address + */ + public static boolean isTeredoAddress(Inet6Address ip) { + byte[] bytes = ip.getAddress(); + return (bytes[0] == (byte) 0x20) + && (bytes[1] == (byte) 0x01) + && (bytes[2] == 0) + && (bytes[3] == 0); + } + + /** + * Returns the Teredo information embedded in a Teredo address. + * + * @param ip {@link Inet6Address} to be examined for embedded Teredo information + * @return extracted {@code TeredoInfo} + * @throws IllegalArgumentException if the argument is not a valid IPv6 Teredo address + */ + public static TeredoInfo getTeredoInfo(Inet6Address ip) { + checkArgument(isTeredoAddress(ip), "Address '%s' is not a Teredo address.", toAddrString(ip)); + + byte[] bytes = ip.getAddress(); + Inet4Address server = getInet4Address(Arrays.copyOfRange(bytes, 4, 8)); + + int flags = ByteStreams.newDataInput(bytes, 8).readShort() & 0xffff; + + // Teredo obfuscates the mapped client port, per section 4 of the RFC. + int port = ~ByteStreams.newDataInput(bytes, 10).readShort() & 0xffff; + + byte[] clientBytes = Arrays.copyOfRange(bytes, 12, 16); + for (int i = 0; i < clientBytes.length; i++) { + // Teredo obfuscates the mapped client IP, per section 4 of the RFC. + clientBytes[i] = (byte) ~clientBytes[i]; + } + Inet4Address client = getInet4Address(clientBytes); + + return new TeredoInfo(server, client, port, flags); + } + + /** + * Evaluates whether the argument is an ISATAP address. + * + *

From RFC 5214: "ISATAP interface identifiers are constructed in Modified EUI-64 format [...] + * by concatenating the 24-bit IANA OUI (00-00-5E), the 8-bit hexadecimal value 0xFE, and a 32-bit + * IPv4 address in network byte order [...]" + * + *

For more on ISATAP addresses see section 6.1 of RFC 5214. + * + * @param ip {@link Inet6Address} to be examined for ISATAP address format + * @return {@code true} if the argument is an ISATAP address + */ + public static boolean isIsatapAddress(Inet6Address ip) { + + // If it's a Teredo address with the right port (41217, or 0xa101) + // which would be encoded as 0x5efe then it can't be an ISATAP address. + if (isTeredoAddress(ip)) { + return false; + } + + byte[] bytes = ip.getAddress(); + + if ((bytes[8] | (byte) 0x03) != (byte) 0x03) { + + // Verify that high byte of the 64 bit identifier is zero, modulo + // the U/L and G bits, with which we are not concerned. + return false; + } + + return (bytes[9] == (byte) 0x00) && (bytes[10] == (byte) 0x5e) && (bytes[11] == (byte) 0xfe); + } + + /** + * Returns the IPv4 address embedded in an ISATAP address. + * + * @param ip {@link Inet6Address} to be examined for embedded IPv4 in ISATAP address + * @return {@link Inet4Address} of embedded IPv4 in an ISATAP address + * @throws IllegalArgumentException if the argument is not a valid IPv6 ISATAP address + */ + public static Inet4Address getIsatapIPv4Address(Inet6Address ip) { + checkArgument(isIsatapAddress(ip), "Address '%s' is not an ISATAP address.", toAddrString(ip)); + + return getInet4Address(Arrays.copyOfRange(ip.getAddress(), 12, 16)); + } + + /** + * Examines the Inet6Address to determine if it is an IPv6 address of one of the specified address + * types that contain an embedded IPv4 address. + * + *

NOTE: ISATAP addresses are explicitly excluded from this method due to their trivial + * spoofability. With other transition addresses spoofing involves (at least) infection of one's + * BGP routing table. + * + * @param ip {@link Inet6Address} to be examined for embedded IPv4 client address + * @return {@code true} if there is an embedded IPv4 client address + * @since 7.0 + */ + public static boolean hasEmbeddedIPv4ClientAddress(Inet6Address ip) { + return isCompatIPv4Address(ip) || is6to4Address(ip) || isTeredoAddress(ip); + } + + /** + * Examines the Inet6Address to extract the embedded IPv4 client address if the InetAddress is an + * IPv6 address of one of the specified address types that contain an embedded IPv4 address. + * + *

NOTE: ISATAP addresses are explicitly excluded from this method due to their trivial + * spoofability. With other transition addresses spoofing involves (at least) infection of one's + * BGP routing table. + * + * @param ip {@link Inet6Address} to be examined for embedded IPv4 client address + * @return {@link Inet4Address} of embedded IPv4 client address + * @throws IllegalArgumentException if the argument does not have a valid embedded IPv4 address + */ + public static Inet4Address getEmbeddedIPv4ClientAddress(Inet6Address ip) { + if (isCompatIPv4Address(ip)) { + return getCompatIPv4Address(ip); + } + + if (is6to4Address(ip)) { + return get6to4IPv4Address(ip); + } + + if (isTeredoAddress(ip)) { + return getTeredoInfo(ip).getClient(); + } + + throw formatIllegalArgumentException("'%s' has no embedded IPv4 address.", toAddrString(ip)); + } + + /** + * Evaluates whether the argument is an "IPv4 mapped" IPv6 address. + * + *

An "IPv4 mapped" address is anything in the range ::ffff:0:0/96 (sometimes written as + * ::ffff:0.0.0.0/96), with the last 32 bits interpreted as an IPv4 address. + * + *

For more on IPv4 mapped addresses see section 2.5.5.2 of RFC 4291. + * + *

Note: This method takes a {@code String} argument because {@link InetAddress} automatically + * collapses mapped addresses to IPv4. (It is actually possible to avoid this using one of the + * obscure {@link Inet6Address} methods, but it would be unwise to depend on such a + * poorly-documented feature.) + * + *

This method accepts non-ASCII digits. That is consistent with {@link InetAddress}, but not + * with various RFCs. If you want to accept ASCII digits only, you can use something like {@code + * CharMatcher.ascii().matchesAllOf(ipString)}. + * + * @param ipString {@code String} to be examined for embedded IPv4-mapped IPv6 address format + * @return {@code true} if the argument is a valid "mapped" address + * @since 10.0 + */ + public static boolean isMappedIPv4Address(String ipString) { + byte[] bytes = ipStringToBytes(ipString); + if (bytes != null && bytes.length == 16) { + for (int i = 0; i < 10; i++) { + if (bytes[i] != 0) { + return false; + } + } + for (int i = 10; i < 12; i++) { + if (bytes[i] != (byte) 0xff) { + return false; + } + } + return true; + } + return false; + } + + /** + * Coerces an IPv6 address into an IPv4 address. + * + *

HACK: As long as applications continue to use IPv4 addresses for indexing into tables, + * accounting, et cetera, it may be necessary to coerce IPv6 addresses into IPv4 addresses. + * This method does so by hashing 64 bits of the IPv6 address into {@code 224.0.0.0/3} (64 bits + * into 29 bits): + * + *

    + *
  • If the IPv6 address contains an embedded IPv4 address, the function hashes that. + *
  • Otherwise, it hashes the upper 64 bits of the IPv6 address. + *
+ * + *

A "coerced" IPv4 address is equivalent to itself. + * + *

NOTE: This method is failsafe for security purposes: ALL IPv6 addresses (except localhost + * (::1)) are hashed to avoid the security risk associated with extracting an embedded IPv4 + * address that might permit elevated privileges. + * + * @param ip {@link InetAddress} to "coerce" + * @return {@link Inet4Address} represented "coerced" address + * @since 7.0 + */ + public static Inet4Address getCoercedIPv4Address(InetAddress ip) { + if (ip instanceof Inet4Address) { + return (Inet4Address) ip; + } + + // Special cases: + byte[] bytes = ip.getAddress(); + boolean leadingBytesOfZero = true; + for (int i = 0; i < 15; ++i) { + if (bytes[i] != 0) { + leadingBytesOfZero = false; + break; + } + } + if (leadingBytesOfZero && (bytes[15] == 1)) { + return LOOPBACK4; // ::1 + } else if (leadingBytesOfZero && (bytes[15] == 0)) { + return ANY4; // ::0 + } + + Inet6Address ip6 = (Inet6Address) ip; + long addressAsLong = 0; + if (hasEmbeddedIPv4ClientAddress(ip6)) { + addressAsLong = getEmbeddedIPv4ClientAddress(ip6).hashCode(); + } else { + // Just extract the high 64 bits (assuming the rest is user-modifiable). + addressAsLong = ByteBuffer.wrap(ip6.getAddress(), 0, 8).getLong(); + } + + // Many strategies for hashing are possible. This might suffice for now. + int coercedHash = Hashing.murmur3_32_fixed().hashLong(addressAsLong).asInt(); + + // Squash into 224/4 Multicast and 240/4 Reserved space (i.e. 224/3). + coercedHash |= 0xe0000000; + + // Fixup to avoid some "illegal" values. Currently the only potential + // illegal value is 255.255.255.255. + if (coercedHash == 0xffffffff) { + coercedHash = 0xfffffffe; + } + + return getInet4Address(Ints.toByteArray(coercedHash)); + } + + /** + * Returns an integer representing an IPv4 address regardless of whether the supplied argument is + * an IPv4 address or not. + * + *

IPv6 addresses are coerced to IPv4 addresses before being converted to integers. + * + *

As long as there are applications that assume that all IP addresses are IPv4 addresses and + * can therefore be converted safely to integers (for whatever purpose) this function can be used + * to handle IPv6 addresses as well until the application is suitably fixed. + * + *

NOTE: an IPv6 address coerced to an IPv4 address can only be used for such purposes as + * rudimentary identification or indexing into a collection of real {@link InetAddress}es. They + * cannot be used as real addresses for the purposes of network communication. + * + * @param ip {@link InetAddress} to convert + * @return {@code int}, "coerced" if ip is not an IPv4 address + * @since 7.0 + */ + public static int coerceToInteger(InetAddress ip) { + return ByteStreams.newDataInput(getCoercedIPv4Address(ip).getAddress()).readInt(); + } + + /** + * Returns a BigInteger representing the address. + * + *

Unlike {@code coerceToInteger}, IPv6 addresses are not coerced to IPv4 addresses. + * + * @param address {@link InetAddress} to convert + * @return {@code BigInteger} representation of the address + * @since 28.2 + */ + public static BigInteger toBigInteger(InetAddress address) { + return new BigInteger(1, address.getAddress()); + } + + /** + * Returns an Inet4Address having the integer value specified by the argument. + * + * @param address {@code int}, the 32bit integer address to be converted + * @return {@link Inet4Address} equivalent of the argument + */ + public static Inet4Address fromInteger(int address) { + return getInet4Address(Ints.toByteArray(address)); + } + + /** + * Returns the {@code Inet4Address} corresponding to a given {@code BigInteger}. + * + * @param address BigInteger representing the IPv4 address + * @return Inet4Address representation of the given BigInteger + * @throws IllegalArgumentException if the BigInteger is not between 0 and 2^32-1 + * @since 28.2 + */ + public static Inet4Address fromIPv4BigInteger(BigInteger address) { + return (Inet4Address) fromBigInteger(address, false); + } + + /** + * Returns the {@code Inet6Address} corresponding to a given {@code BigInteger}. + * + * @param address BigInteger representing the IPv6 address + * @return Inet6Address representation of the given BigInteger + * @throws IllegalArgumentException if the BigInteger is not between 0 and 2^128-1 + * @since 28.2 + */ + public static Inet6Address fromIPv6BigInteger(BigInteger address) { + return (Inet6Address) fromBigInteger(address, true); + } + + /** + * Converts a BigInteger to either an IPv4 or IPv6 address. If the IP is IPv4, it must be + * constrained to 32 bits, otherwise it is constrained to 128 bits. + * + * @param address the address represented as a big integer + * @param isIpv6 whether the created address should be IPv4 or IPv6 + * @return the BigInteger converted to an address + * @throws IllegalArgumentException if the BigInteger is not between 0 and maximum value for IPv4 + * or IPv6 respectively + */ + private static InetAddress fromBigInteger(BigInteger address, boolean isIpv6) { + checkArgument(address.signum() >= 0, "BigInteger must be greater than or equal to 0"); + + int numBytes = isIpv6 ? 16 : 4; + + byte[] addressBytes = address.toByteArray(); + byte[] targetCopyArray = new byte[numBytes]; + + int srcPos = Math.max(0, addressBytes.length - numBytes); + int copyLength = addressBytes.length - srcPos; + int destPos = numBytes - copyLength; + + // Check the extra bytes in the BigInteger are all zero. + for (int i = 0; i < srcPos; i++) { + if (addressBytes[i] != 0x00) { + throw formatIllegalArgumentException( + "BigInteger cannot be converted to InetAddress because it has more than %d" + + " bytes: %s", + numBytes, address); + } + } + + // Copy the bytes into the least significant positions. + System.arraycopy(addressBytes, srcPos, targetCopyArray, destPos, copyLength); + + try { + return InetAddress.getByAddress(targetCopyArray); + } catch (UnknownHostException impossible) { + throw new AssertionError(impossible); + } + } + + /** + * Returns an address from a little-endian ordered byte array (the opposite of what {@link + * InetAddress#getByAddress} expects). + * + *

IPv4 address byte array must be 4 bytes long and IPv6 byte array must be 16 bytes long. + * + * @param addr the raw IP address in little-endian byte order + * @return an InetAddress object created from the raw IP address + * @throws UnknownHostException if IP address is of illegal length + */ + public static InetAddress fromLittleEndianByteArray(byte[] addr) throws UnknownHostException { + byte[] reversed = new byte[addr.length]; + for (int i = 0; i < addr.length; i++) { + reversed[i] = addr[addr.length - i - 1]; + } + return InetAddress.getByAddress(reversed); + } + + /** + * Returns a new InetAddress that is one less than the passed in address. This method works for + * both IPv4 and IPv6 addresses. + * + * @param address the InetAddress to decrement + * @return a new InetAddress that is one less than the passed in address + * @throws IllegalArgumentException if InetAddress is at the beginning of its range + * @since 18.0 + */ + public static InetAddress decrement(InetAddress address) { + byte[] addr = address.getAddress(); + int i = addr.length - 1; + while (i >= 0 && addr[i] == (byte) 0x00) { + addr[i] = (byte) 0xff; + i--; + } + + checkArgument(i >= 0, "Decrementing %s would wrap.", address); + + addr[i]--; + return bytesToInetAddress(addr); + } + + /** + * Returns a new InetAddress that is one more than the passed in address. This method works for + * both IPv4 and IPv6 addresses. + * + * @param address the InetAddress to increment + * @return a new InetAddress that is one more than the passed in address + * @throws IllegalArgumentException if InetAddress is at the end of its range + * @since 10.0 + */ + public static InetAddress increment(InetAddress address) { + byte[] addr = address.getAddress(); + int i = addr.length - 1; + while (i >= 0 && addr[i] == (byte) 0xff) { + addr[i] = 0; + i--; + } + + checkArgument(i >= 0, "Incrementing %s would wrap.", address); + + addr[i]++; + return bytesToInetAddress(addr); + } + + /** + * Returns true if the InetAddress is either 255.255.255.255 for IPv4 or + * ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff for IPv6. + * + * @return true if the InetAddress is either 255.255.255.255 for IPv4 or + * ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff for IPv6 + * @since 10.0 + */ + public static boolean isMaximum(InetAddress address) { + byte[] addr = address.getAddress(); + for (byte b : addr) { + if (b != (byte) 0xff) { + return false; + } + } + return true; + } + + private static IllegalArgumentException formatIllegalArgumentException( + String format, Object... args) { + return new IllegalArgumentException(String.format(Locale.ROOT, format, args)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/InternetDomainName.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/InternetDomainName.java new file mode 100644 index 0000000000000..620a97d50a9ed --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/InternetDomainName.java @@ -0,0 +1,671 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Ascii; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.base.Splitter; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.thirdparty.publicsuffix.PublicSuffixPatterns; +import org.apache.iotdb.google.thirdparty.publicsuffix.PublicSuffixType; + +import java.util.List; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An immutable well-formed internet domain name, such as {@code com} or {@code foo.co.uk}. Only + * syntactic analysis is performed; no DNS lookups or other network interactions take place. Thus + * there is no guarantee that the domain actually exists on the internet. + * + *

One common use of this class is to determine whether a given string is likely to represent an + * addressable domain on the web -- that is, for a candidate string {@code "xxx"}, might browsing to + * {@code "http://xxx/"} result in a webpage being displayed? In the past, this test was frequently + * done by determining whether the domain ended with a {@linkplain #isPublicSuffix() public suffix} + * but was not itself a public suffix. However, this test is no longer accurate. There are many + * domains which are both public suffixes and addressable as hosts; {@code "uk.com"} is one example. + * Using the subset of public suffixes that are {@linkplain #isRegistrySuffix() registry suffixes}, + * one can get a better result, as only a few registry suffixes are addressable. However, the most + * useful test to determine if a domain is a plausible web host is {@link #hasPublicSuffix()}. This + * will return {@code true} for many domains which (currently) are not hosts, such as {@code "com"}, + * but given that any public suffix may become a host without warning, it is better to err on the + * side of permissiveness and thus avoid spurious rejection of valid sites. Of course, to actually + * determine addressability of any host, clients of this class will need to perform their own DNS + * lookups. + * + *

During construction, names are normalized in two ways: + * + *

    + *
  1. ASCII uppercase characters are converted to lowercase. + *
  2. Unicode dot separators other than the ASCII period ({@code '.'}) are converted to the ASCII + * period. + *
+ * + *

The normalized values will be returned from {@link #toString()} and {@link #parts()}, and will + * be reflected in the result of {@link #equals(Object)}. + * + *

Internationalized domain + * names such as {@code 网络.cn} are supported, as are the equivalent IDNA Punycode-encoded + * versions. + * + * @author Catherine Berry + * @since 5.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class InternetDomainName { + + private static final CharMatcher DOTS_MATCHER = CharMatcher.anyOf(".\u3002\uFF0E\uFF61"); + private static final Splitter DOT_SPLITTER = Splitter.on('.'); + private static final Joiner DOT_JOINER = Joiner.on('.'); + + /** + * Value of {@link #publicSuffixIndex()} or {@link #registrySuffixIndex()} which indicates that no + * relevant suffix was found. + */ + private static final int NO_SUFFIX_FOUND = -1; + + /** + * Value of {@link #publicSuffixIndexCache} or {@link #registrySuffixIndexCache} which indicates + * that they were not initialized yet. + */ + private static final int SUFFIX_NOT_INITIALIZED = -2; + + /** + * Maximum parts (labels) in a domain name. This value arises from the 255-octet limit described + * in RFC 2181 part 11 with the fact that the + * encoding of each part occupies at least two bytes (dot plus label externally, length byte plus + * label internally). Thus, if all labels have the minimum size of one byte, 127 of them will fit. + */ + private static final int MAX_PARTS = 127; + + /** + * Maximum length of a full domain name, including separators, and leaving room for the root + * label. See RFC 2181 part 11. + */ + private static final int MAX_LENGTH = 253; + + /** + * Maximum size of a single part of a domain name. See RFC 2181 part 11. + */ + private static final int MAX_DOMAIN_PART_LENGTH = 63; + + /** The full domain name, converted to lower case. */ + private final String name; + + /** The parts of the domain name, converted to lower case. */ + private final ImmutableList parts; + + /** + * Cached value of #publicSuffixIndex(). Do not use directly. + * + *

Since this field isn't {@code volatile}, if an instance of this class is shared across + * threads before it is initialized, then each thread is likely to compute their own copy of the + * value. + */ + @SuppressWarnings("Immutable") + private int publicSuffixIndexCache = SUFFIX_NOT_INITIALIZED; + + /** + * Cached value of #registrySuffixIndex(). Do not use directly. + * + *

Since this field isn't {@code volatile}, if an instance of this class is shared across + * threads before it is initialized, then each thread is likely to compute their own copy of the + * value. + */ + @SuppressWarnings("Immutable") + private int registrySuffixIndexCache = SUFFIX_NOT_INITIALIZED; + + /** Constructor used to implement {@link #from(String)}, and from subclasses. */ + InternetDomainName(String name) { + // Normalize: + // * ASCII characters to lowercase + // * All dot-like characters to '.' + // * Strip trailing '.' + + name = Ascii.toLowerCase(DOTS_MATCHER.replaceFrom(name, '.')); + + if (name.endsWith(".")) { + name = name.substring(0, name.length() - 1); + } + + checkArgument(name.length() <= MAX_LENGTH, "Domain name too long: '%s':", name); + this.name = name; + + this.parts = ImmutableList.copyOf(DOT_SPLITTER.split(name)); + checkArgument(parts.size() <= MAX_PARTS, "Domain has too many parts: '%s'", name); + checkArgument(validateSyntax(parts), "Not a valid domain name: '%s'", name); + } + + /** + * The index in the {@link #parts()} list at which the public suffix begins. For example, for the + * domain name {@code myblog.blogspot.co.uk}, the value would be 1 (the index of the {@code + * blogspot} part). The value is negative (specifically, {@link #NO_SUFFIX_FOUND}) if no public + * suffix was found. + */ + private int publicSuffixIndex() { + int publicSuffixIndexLocal = publicSuffixIndexCache; + if (publicSuffixIndexLocal == SUFFIX_NOT_INITIALIZED) { + publicSuffixIndexCache = + publicSuffixIndexLocal = findSuffixOfType(Optional.absent()); + } + return publicSuffixIndexLocal; + } + + /** + * The index in the {@link #parts()} list at which the registry suffix begins. For example, for + * the domain name {@code myblog.blogspot.co.uk}, the value would be 2 (the index of the {@code + * co} part). The value is negative (specifically, {@link #NO_SUFFIX_FOUND}) if no registry suffix + * was found. + */ + private int registrySuffixIndex() { + int registrySuffixIndexLocal = registrySuffixIndexCache; + if (registrySuffixIndexLocal == SUFFIX_NOT_INITIALIZED) { + registrySuffixIndexCache = + registrySuffixIndexLocal = findSuffixOfType(Optional.of(PublicSuffixType.REGISTRY)); + } + return registrySuffixIndexLocal; + } + + /** + * Returns the index of the leftmost part of the suffix, or -1 if not found. Note that the value + * defined as a suffix may not produce {@code true} results from {@link #isPublicSuffix()} or + * {@link #isRegistrySuffix()} if the domain ends with an excluded domain pattern such as {@code + * "nhs.uk"}. + * + *

If a {@code desiredType} is specified, this method only finds suffixes of the given type. + * Otherwise, it finds the first suffix of any type. + */ + private int findSuffixOfType(Optional desiredType) { + int partsSize = parts.size(); + + for (int i = 0; i < partsSize; i++) { + String ancestorName = DOT_JOINER.join(parts.subList(i, partsSize)); + + if (i > 0 + && matchesType( + desiredType, Optional.fromNullable(PublicSuffixPatterns.UNDER.get(ancestorName)))) { + return i - 1; + } + + if (matchesType( + desiredType, Optional.fromNullable(PublicSuffixPatterns.EXACT.get(ancestorName)))) { + return i; + } + + // Excluded domains (e.g. !nhs.uk) use the next highest + // domain as the effective public suffix (e.g. uk). + + if (PublicSuffixPatterns.EXCLUDED.containsKey(ancestorName)) { + return i + 1; + } + } + + return NO_SUFFIX_FOUND; + } + + /** + * Returns an instance of {@link InternetDomainName} after lenient validation. Specifically, + * validation against RFC 3490 + * ("Internationalizing Domain Names in Applications") is skipped, while validation against RFC 1035 is relaxed in the following ways: + * + *

    + *
  • Any part containing non-ASCII characters is considered valid. + *
  • Underscores ('_') are permitted wherever dashes ('-') are permitted. + *
  • Parts other than the final part may start with a digit, as mandated by RFC 1123. + *
+ * + * @param domain A domain name (not IP address) + * @throws IllegalArgumentException if {@code domain} is not syntactically valid according to + * {@link #isValid} + * @since 10.0 (previously named {@code fromLenient}) + */ + // TODO(b/219820829): consider removing + public static InternetDomainName from(String domain) { + return new InternetDomainName(checkNotNull(domain)); + } + + /** + * Validation method used by {@code from} to ensure that the domain name is syntactically valid + * according to RFC 1035. + * + * @return Is the domain name syntactically valid? + */ + private static boolean validateSyntax(List parts) { + int lastIndex = parts.size() - 1; + + // Validate the last part specially, as it has different syntax rules. + + if (!validatePart(parts.get(lastIndex), true)) { + return false; + } + + for (int i = 0; i < lastIndex; i++) { + String part = parts.get(i); + if (!validatePart(part, false)) { + return false; + } + } + + return true; + } + + private static final CharMatcher DASH_MATCHER = CharMatcher.anyOf("-_"); + + private static final CharMatcher DIGIT_MATCHER = CharMatcher.inRange('0', '9'); + + private static final CharMatcher LETTER_MATCHER = + CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')); + + private static final CharMatcher PART_CHAR_MATCHER = + DIGIT_MATCHER.or(LETTER_MATCHER).or(DASH_MATCHER); + + /** + * Helper method for {@link #validateSyntax(List)}. Validates that one part of a domain name is + * valid. + * + * @param part The domain name part to be validated + * @param isFinalPart Is this the final (rightmost) domain part? + * @return Whether the part is valid + */ + private static boolean validatePart(String part, boolean isFinalPart) { + + // These tests could be collapsed into one big boolean expression, but + // they have been left as independent tests for clarity. + + if (part.length() < 1 || part.length() > MAX_DOMAIN_PART_LENGTH) { + return false; + } + + /* + * GWT claims to support java.lang.Character's char-classification methods, but it actually only + * works for ASCII. So for now, assume any non-ASCII characters are valid. The only place this + * seems to be documented is here: + * https://groups.google.com/d/topic/google-web-toolkit-contributors/1UEzsryq1XI + * + *

ASCII characters in the part are expected to be valid per RFC 1035, with underscore also + * being allowed due to widespread practice. + */ + + String asciiChars = CharMatcher.ascii().retainFrom(part); + + if (!PART_CHAR_MATCHER.matchesAllOf(asciiChars)) { + return false; + } + + // No initial or final dashes or underscores. + + if (DASH_MATCHER.matches(part.charAt(0)) + || DASH_MATCHER.matches(part.charAt(part.length() - 1))) { + return false; + } + + /* + * Note that we allow (in contravention of a strict interpretation of the relevant RFCs) domain + * parts other than the last may begin with a digit (for example, "3com.com"). It's important to + * disallow an initial digit in the last part; it's the only thing that stops an IPv4 numeric + * address like 127.0.0.1 from looking like a valid domain name. + */ + + if (isFinalPart && DIGIT_MATCHER.matches(part.charAt(0))) { + return false; + } + + return true; + } + + /** + * Returns the individual components of this domain name, normalized to all lower case. For + * example, for the domain name {@code mail.google.com}, this method returns the list {@code + * ["mail", "google", "com"]}. + */ + public ImmutableList parts() { + return parts; + } + + /** + * Indicates whether this domain name represents a public suffix, as defined by the Mozilla + * Foundation's Public Suffix List (PSL). A public suffix + * is one under which Internet users can directly register names, such as {@code com}, {@code + * co.uk} or {@code pvt.k12.wy.us}. Examples of domain names that are not public suffixes + * include {@code google.com}, {@code foo.co.uk}, and {@code myblog.blogspot.com}. + * + *

Public suffixes are a proper superset of {@linkplain #isRegistrySuffix() registry suffixes}. + * The list of public suffixes additionally contains privately owned domain names under which + * Internet users can register subdomains. An example of a public suffix that is not a registry + * suffix is {@code blogspot.com}. Note that it is true that all public suffixes have + * registry suffixes, since domain name registries collectively control all internet domain names. + * + *

For considerations on whether the public suffix or registry suffix designation is more + * suitable for your application, see this article. + * + * @return {@code true} if this domain name appears exactly on the public suffix list + * @since 6.0 + */ + public boolean isPublicSuffix() { + return publicSuffixIndex() == 0; + } + + /** + * Indicates whether this domain name ends in a {@linkplain #isPublicSuffix() public suffix}, + * including if it is a public suffix itself. For example, returns {@code true} for {@code + * www.google.com}, {@code foo.co.uk} and {@code com}, but not for {@code invalid} or {@code + * google.invalid}. This is the recommended method for determining whether a domain is potentially + * an addressable host. + * + *

Note that this method is equivalent to {@link #hasRegistrySuffix()} because all registry + * suffixes are public suffixes and all public suffixes have registry suffixes. + * + * @since 6.0 + */ + public boolean hasPublicSuffix() { + return publicSuffixIndex() != NO_SUFFIX_FOUND; + } + + /** + * Returns the {@linkplain #isPublicSuffix() public suffix} portion of the domain name, or {@code + * null} if no public suffix is present. + * + * @since 6.0 + */ + public InternetDomainName publicSuffix() { + return hasPublicSuffix() ? ancestor(publicSuffixIndex()) : null; + } + + /** + * Indicates whether this domain name ends in a {@linkplain #isPublicSuffix() public suffix}, + * while not being a public suffix itself. For example, returns {@code true} for {@code + * www.google.com}, {@code foo.co.uk} and {@code myblog.blogspot.com}, but not for {@code com}, + * {@code co.uk}, {@code google.invalid}, or {@code blogspot.com}. + * + *

This method can be used to determine whether it will probably be possible to set cookies on + * the domain, though even that depends on individual browsers' implementations of cookie + * controls. See RFC 2109 for details. + * + * @since 6.0 + */ + public boolean isUnderPublicSuffix() { + return publicSuffixIndex() > 0; + } + + /** + * Indicates whether this domain name is composed of exactly one subdomain component followed by a + * {@linkplain #isPublicSuffix() public suffix}. For example, returns {@code true} for {@code + * google.com} {@code foo.co.uk}, and {@code myblog.blogspot.com}, but not for {@code + * www.google.com}, {@code co.uk}, or {@code blogspot.com}. + * + *

This method can be used to determine whether a domain is probably the highest level for + * which cookies may be set, though even that depends on individual browsers' implementations of + * cookie controls. See RFC 2109 for details. + * + * @since 6.0 + */ + public boolean isTopPrivateDomain() { + return publicSuffixIndex() == 1; + } + + /** + * Returns the portion of this domain name that is one level beneath the {@linkplain + * #isPublicSuffix() public suffix}. For example, for {@code x.adwords.google.co.uk} it returns + * {@code google.co.uk}, since {@code co.uk} is a public suffix. Similarly, for {@code + * myblog.blogspot.com} it returns the same domain, {@code myblog.blogspot.com}, since {@code + * blogspot.com} is a public suffix. + * + *

If {@link #isTopPrivateDomain()} is true, the current domain name instance is returned. + * + *

This method can be used to determine the probable highest level parent domain for which + * cookies may be set, though even that depends on individual browsers' implementations of cookie + * controls. + * + * @throws IllegalStateException if this domain does not end with a public suffix + * @since 6.0 + */ + public InternetDomainName topPrivateDomain() { + if (isTopPrivateDomain()) { + return this; + } + checkState(isUnderPublicSuffix(), "Not under a public suffix: %s", name); + return ancestor(publicSuffixIndex() - 1); + } + + /** + * Indicates whether this domain name represents a registry suffix, as defined by a subset + * of the Mozilla Foundation's Public Suffix List (PSL). A + * registry suffix is one under which Internet users can directly register names via a domain name + * registrar, and have such registrations lawfully protected by internet-governing bodies such as + * ICANN. Examples of registry suffixes include {@code com}, {@code co.uk}, and {@code + * pvt.k12.wy.us}. Examples of domain names that are not registry suffixes include {@code + * google.com} and {@code foo.co.uk}. + * + *

Registry suffixes are a proper subset of {@linkplain #isPublicSuffix() public suffixes}. The + * list of public suffixes additionally contains privately owned domain names under which Internet + * users can register subdomains. An example of a public suffix that is not a registry suffix is + * {@code blogspot.com}. Note that it is true that all public suffixes have registry + * suffixes, since domain name registries collectively control all internet domain names. + * + *

For considerations on whether the public suffix or registry suffix designation is more + * suitable for your application, see this article. + * + * @return {@code true} if this domain name appears exactly on the public suffix list as part of + * the registry suffix section (labelled "ICANN"). + * @since 23.3 + */ + public boolean isRegistrySuffix() { + return registrySuffixIndex() == 0; + } + + /** + * Indicates whether this domain name ends in a {@linkplain #isRegistrySuffix() registry suffix}, + * including if it is a registry suffix itself. For example, returns {@code true} for {@code + * www.google.com}, {@code foo.co.uk} and {@code com}, but not for {@code invalid} or {@code + * google.invalid}. + * + *

Note that this method is equivalent to {@link #hasPublicSuffix()} because all registry + * suffixes are public suffixes and all public suffixes have registry suffixes. + * + * @since 23.3 + */ + public boolean hasRegistrySuffix() { + return registrySuffixIndex() != NO_SUFFIX_FOUND; + } + + /** + * Returns the {@linkplain #isRegistrySuffix() registry suffix} portion of the domain name, or + * {@code null} if no registry suffix is present. + * + * @since 23.3 + */ + public InternetDomainName registrySuffix() { + return hasRegistrySuffix() ? ancestor(registrySuffixIndex()) : null; + } + + /** + * Indicates whether this domain name ends in a {@linkplain #isRegistrySuffix() registry suffix}, + * while not being a registry suffix itself. For example, returns {@code true} for {@code + * www.google.com}, {@code foo.co.uk} and {@code blogspot.com}, but not for {@code com}, {@code + * co.uk}, or {@code google.invalid}. + * + * @since 23.3 + */ + public boolean isUnderRegistrySuffix() { + return registrySuffixIndex() > 0; + } + + /** + * Indicates whether this domain name is composed of exactly one subdomain component followed by a + * {@linkplain #isRegistrySuffix() registry suffix}. For example, returns {@code true} for {@code + * google.com}, {@code foo.co.uk}, and {@code blogspot.com}, but not for {@code www.google.com}, + * {@code co.uk}, or {@code myblog.blogspot.com}. + * + *

Warning: This method should not be used to determine the probable highest level + * parent domain for which cookies may be set. Use {@link #topPrivateDomain()} for that purpose. + * + * @since 23.3 + */ + public boolean isTopDomainUnderRegistrySuffix() { + return registrySuffixIndex() == 1; + } + + /** + * Returns the portion of this domain name that is one level beneath the {@linkplain + * #isRegistrySuffix() registry suffix}. For example, for {@code x.adwords.google.co.uk} it + * returns {@code google.co.uk}, since {@code co.uk} is a registry suffix. Similarly, for {@code + * myblog.blogspot.com} it returns {@code blogspot.com}, since {@code com} is a registry suffix. + * + *

If {@link #isTopDomainUnderRegistrySuffix()} is true, the current domain name instance is + * returned. + * + *

Warning: This method should not be used to determine whether a domain is probably the + * highest level for which cookies may be set. Use {@link #isTopPrivateDomain()} for that purpose. + * + * @throws IllegalStateException if this domain does not end with a registry suffix + * @since 23.3 + */ + public InternetDomainName topDomainUnderRegistrySuffix() { + if (isTopDomainUnderRegistrySuffix()) { + return this; + } + checkState(isUnderRegistrySuffix(), "Not under a registry suffix: %s", name); + return ancestor(registrySuffixIndex() - 1); + } + + /** Indicates whether this domain is composed of two or more parts. */ + public boolean hasParent() { + return parts.size() > 1; + } + + /** + * Returns an {@code InternetDomainName} that is the immediate ancestor of this one; that is, the + * current domain with the leftmost part removed. For example, the parent of {@code + * www.google.com} is {@code google.com}. + * + * @throws IllegalStateException if the domain has no parent, as determined by {@link #hasParent} + */ + public InternetDomainName parent() { + checkState(hasParent(), "Domain '%s' has no parent", name); + return ancestor(1); + } + + /** + * Returns the ancestor of the current domain at the given number of levels "higher" (rightward) + * in the subdomain list. The number of levels must be non-negative, and less than {@code N-1}, + * where {@code N} is the number of parts in the domain. + * + *

TODO: Reasonable candidate for addition to public API. + */ + private InternetDomainName ancestor(int levels) { + return from(DOT_JOINER.join(parts.subList(levels, parts.size()))); + } + + /** + * Creates and returns a new {@code InternetDomainName} by prepending the argument and a dot to + * the current name. For example, {@code InternetDomainName.from("foo.com").child("www.bar")} + * returns a new {@code InternetDomainName} with the value {@code www.bar.foo.com}. Only lenient + * validation is performed, as described {@link #from(String) here}. + * + * @throws NullPointerException if leftParts is null + * @throws IllegalArgumentException if the resulting name is not valid + */ + public InternetDomainName child(String leftParts) { + return from(checkNotNull(leftParts) + "." + name); + } + + /** + * Indicates whether the argument is a syntactically valid domain name using lenient validation. + * Specifically, validation against RFC 3490 + * ("Internationalizing Domain Names in Applications") is skipped. + * + *

The following two code snippets are equivalent: + * + *

{@code
+   * domainName = InternetDomainName.isValid(name)
+   *     ? InternetDomainName.from(name)
+   *     : DEFAULT_DOMAIN;
+   * }
+ * + *
{@code
+   * try {
+   *   domainName = InternetDomainName.from(name);
+   * } catch (IllegalArgumentException e) {
+   *   domainName = DEFAULT_DOMAIN;
+   * }
+   * }
+ * + * @since 8.0 (previously named {@code isValidLenient}) + */ + public static boolean isValid(String name) { + try { + InternetDomainName unused = from(name); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + /** + * If a {@code desiredType} is specified, returns true only if the {@code actualType} is + * identical. Otherwise, returns true as long as {@code actualType} is present. + */ + private static boolean matchesType( + Optional desiredType, Optional actualType) { + return desiredType.isPresent() ? desiredType.equals(actualType) : actualType.isPresent(); + } + + /** Returns the domain name, normalized to all lower case. */ + @Override + public String toString() { + return name; + } + + /** + * Equality testing is based on the text supplied by the caller, after normalization as described + * in the class documentation. For example, a non-ASCII Unicode domain name and the Punycode + * version of the same domain name would not be considered equal. + */ + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + + if (object instanceof InternetDomainName) { + InternetDomainName that = (InternetDomainName) object; + return this.name.equals(that.name); + } + + return false; + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/MediaType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/MediaType.java new file mode 100644 index 0000000000000..c9433b51292fd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/MediaType.java @@ -0,0 +1,1208 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Ascii; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Joiner.MapJoiner; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Optional; +import org.apache.iotdb.google.common.collect.ImmutableListMultimap; +import org.apache.iotdb.google.common.collect.ImmutableMultiset; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Multimap; +import org.apache.iotdb.google.common.collect.Multimaps; + +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Map; +import java.util.Map.Entry; + +import static org.apache.iotdb.google.common.base.CharMatcher.ascii; +import static org.apache.iotdb.google.common.base.CharMatcher.javaIsoControl; +import static org.apache.iotdb.google.common.base.Charsets.UTF_8; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * Represents an Internet Media Type + * (also known as a MIME Type or Content Type). This class also supports the concept of media ranges + * defined by HTTP/1.1. + * As such, the {@code *} character is treated as a wildcard and is used to represent any acceptable + * type or subtype value. A media type may not have wildcard type with a declared subtype. The + * {@code *} character has no special meaning as part of a parameter. All values for type, subtype, + * parameter attributes or parameter values must be valid according to RFCs 2045 and 2046. + * + *

All portions of the media type that are case-insensitive (type, subtype, parameter attributes) + * are normalized to lowercase. The value of the {@code charset} parameter is normalized to + * lowercase, but all others are left as-is. + * + *

Note that this specifically does not represent the value of the MIME {@code + * Content-Type} header and as such has no support for header-specific considerations such as line + * folding and comments. + * + *

For media types that take a charset the predefined constants default to UTF-8 and have a + * "_UTF_8" suffix. To get a version without a character set, use {@link #withoutParameters}. + * + * @since 12.0 + * @author Gregory Kick + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class MediaType { + private static final String CHARSET_ATTRIBUTE = "charset"; + private static final ImmutableListMultimap UTF_8_CONSTANT_PARAMETERS = + ImmutableListMultimap.of(CHARSET_ATTRIBUTE, Ascii.toLowerCase(UTF_8.name())); + + /** Matcher for type, subtype and attributes. */ + private static final CharMatcher TOKEN_MATCHER = + ascii() + .and(javaIsoControl().negate()) + .and(CharMatcher.isNot(' ')) + .and(CharMatcher.noneOf("()<>@,;:\\\"/[]?=")); + + private static final CharMatcher QUOTED_TEXT_MATCHER = ascii().and(CharMatcher.noneOf("\"\\\r")); + + /* + * This matches the same characters as linear-white-space from RFC 822, but we make no effort to + * enforce any particular rules with regards to line folding as stated in the class docs. + */ + private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n"); + + // TODO(gak): make these public? + private static final String APPLICATION_TYPE = "application"; + private static final String AUDIO_TYPE = "audio"; + private static final String IMAGE_TYPE = "image"; + private static final String TEXT_TYPE = "text"; + private static final String VIDEO_TYPE = "video"; + private static final String FONT_TYPE = "font"; + + private static final String WILDCARD = "*"; + + private static final Map KNOWN_TYPES = Maps.newHashMap(); + + private static MediaType createConstant(String type, String subtype) { + MediaType mediaType = + addKnownType(new MediaType(type, subtype, ImmutableListMultimap.of())); + mediaType.parsedCharset = Optional.absent(); + return mediaType; + } + + private static MediaType createConstantUtf8(String type, String subtype) { + MediaType mediaType = addKnownType(new MediaType(type, subtype, UTF_8_CONSTANT_PARAMETERS)); + mediaType.parsedCharset = Optional.of(UTF_8); + return mediaType; + } + + private static MediaType addKnownType(MediaType mediaType) { + KNOWN_TYPES.put(mediaType, mediaType); + return mediaType; + } + + /* + * The following constants are grouped by their type and ordered alphabetically by the constant + * name within that type. The constant name should be a sensible identifier that is closest to the + * "common name" of the media. This is often, but not necessarily the same as the subtype. + * + * Be sure to declare all constants with the type and subtype in all lowercase. For types that + * take a charset (e.g. all text/* types), default to UTF-8 and suffix the constant name with + * "_UTF_8". + */ + + public static final MediaType ANY_TYPE = createConstant(WILDCARD, WILDCARD); + public static final MediaType ANY_TEXT_TYPE = createConstant(TEXT_TYPE, WILDCARD); + public static final MediaType ANY_IMAGE_TYPE = createConstant(IMAGE_TYPE, WILDCARD); + public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD); + public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD); + public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD); + + /** + * Wildcard matching any "font" top-level media type. + * + * @since 30.0 + */ + public static final MediaType ANY_FONT_TYPE = createConstant(FONT_TYPE, WILDCARD); + + /* text types */ + public static final MediaType CACHE_MANIFEST_UTF_8 = + createConstantUtf8(TEXT_TYPE, "cache-manifest"); + public static final MediaType CSS_UTF_8 = createConstantUtf8(TEXT_TYPE, "css"); + public static final MediaType CSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "csv"); + public static final MediaType HTML_UTF_8 = createConstantUtf8(TEXT_TYPE, "html"); + public static final MediaType I_CALENDAR_UTF_8 = createConstantUtf8(TEXT_TYPE, "calendar"); + public static final MediaType PLAIN_TEXT_UTF_8 = createConstantUtf8(TEXT_TYPE, "plain"); + + /** + * RFC 4329 declares {@link + * #JAVASCRIPT_UTF_8 application/javascript} to be the correct media type for JavaScript, but this + * may be necessary in certain situations for compatibility. + */ + public static final MediaType TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8(TEXT_TYPE, "javascript"); + + /** + * Tab separated + * values. + * + * @since 15.0 + */ + public static final MediaType TSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "tab-separated-values"); + + public static final MediaType VCARD_UTF_8 = createConstantUtf8(TEXT_TYPE, "vcard"); + + /** + * UTF-8 encoded Wireless Markup + * Language. + * + * @since 13.0 + */ + public static final MediaType WML_UTF_8 = createConstantUtf8(TEXT_TYPE, "vnd.wap.wml"); + + /** + * As described in RFC 3023, this constant + * ({@code text/xml}) is used for XML documents that are "readable by casual users." {@link + * #APPLICATION_XML_UTF_8} is provided for documents that are intended for applications. + */ + public static final MediaType XML_UTF_8 = createConstantUtf8(TEXT_TYPE, "xml"); + + /** + * As described in the VTT spec, this is + * used for Web Video Text Tracks (WebVTT) files, used with the HTML5 track element. + * + * @since 20.0 + */ + public static final MediaType VTT_UTF_8 = createConstantUtf8(TEXT_TYPE, "vtt"); + + /* image types */ + /** + * Bitmap file format ({@code bmp} + * files). + * + * @since 13.0 + */ + public static final MediaType BMP = createConstant(IMAGE_TYPE, "bmp"); + + /** + * The Canon Image File + * Format ({@code crw} files), a widely-used "raw image" format for cameras. It is found in + * {@code /etc/mime.types}, e.g. in Debian 3.48-1. + * + * @since 15.0 + */ + public static final MediaType CRW = createConstant(IMAGE_TYPE, "x-canon-crw"); + + public static final MediaType GIF = createConstant(IMAGE_TYPE, "gif"); + public static final MediaType ICO = createConstant(IMAGE_TYPE, "vnd.microsoft.icon"); + public static final MediaType JPEG = createConstant(IMAGE_TYPE, "jpeg"); + public static final MediaType PNG = createConstant(IMAGE_TYPE, "png"); + + /** + * The Photoshop File Format ({@code psd} files) as defined by IANA, and + * found in {@code /etc/mime.types}, e.g. of the + * Apache HTTPD project; for the specification, see + * Adobe Photoshop Document Format and Wikipedia; this is the + * regular output/input of Photoshop (which can also export to various image formats; note that + * files with extension "PSB" are in a distinct but related format). + * + *

This is a more recent replacement for the older, experimental type {@code x-photoshop}: RFC-2046.6. + * + * @since 15.0 + */ + public static final MediaType PSD = createConstant(IMAGE_TYPE, "vnd.adobe.photoshop"); + + public static final MediaType SVG_UTF_8 = createConstantUtf8(IMAGE_TYPE, "svg+xml"); + public static final MediaType TIFF = createConstant(IMAGE_TYPE, "tiff"); + + /** + * WebP image format. + * + * @since 13.0 + */ + public static final MediaType WEBP = createConstant(IMAGE_TYPE, "webp"); + + /** + * HEIF image format. + * + * @since 28.1 + */ + public static final MediaType HEIF = createConstant(IMAGE_TYPE, "heif"); + + /** + * JP2K image format. + * + * @since 28.1 + */ + public static final MediaType JP2K = createConstant(IMAGE_TYPE, "jp2"); + + /* audio types */ + public static final MediaType MP4_AUDIO = createConstant(AUDIO_TYPE, "mp4"); + public static final MediaType MPEG_AUDIO = createConstant(AUDIO_TYPE, "mpeg"); + public static final MediaType OGG_AUDIO = createConstant(AUDIO_TYPE, "ogg"); + public static final MediaType WEBM_AUDIO = createConstant(AUDIO_TYPE, "webm"); + + /** + * L16 audio, as defined by RFC 2586. + * + * @since 24.1 + */ + public static final MediaType L16_AUDIO = createConstant(AUDIO_TYPE, "l16"); + + /** + * L24 audio, as defined by RFC 3190. + * + * @since 20.0 + */ + public static final MediaType L24_AUDIO = createConstant(AUDIO_TYPE, "l24"); + + /** + * Basic Audio, as defined by RFC + * 2046. + * + * @since 20.0 + */ + public static final MediaType BASIC_AUDIO = createConstant(AUDIO_TYPE, "basic"); + + /** + * Advanced Audio Coding. For more information, see Advanced Audio Coding. + * + * @since 20.0 + */ + public static final MediaType AAC_AUDIO = createConstant(AUDIO_TYPE, "aac"); + + /** + * Vorbis Audio, as defined by RFC 5215. + * + * @since 20.0 + */ + public static final MediaType VORBIS_AUDIO = createConstant(AUDIO_TYPE, "vorbis"); + + /** + * Windows Media Audio. For more information, see file + * name extensions for Windows Media metafiles. + * + * @since 20.0 + */ + public static final MediaType WMA_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wma"); + + /** + * Windows Media metafiles. For more information, see file + * name extensions for Windows Media metafiles. + * + * @since 20.0 + */ + public static final MediaType WAX_AUDIO = createConstant(AUDIO_TYPE, "x-ms-wax"); + + /** + * Real Audio. For more information, see this link. + * + * @since 20.0 + */ + public static final MediaType VND_REAL_AUDIO = createConstant(AUDIO_TYPE, "vnd.rn-realaudio"); + + /** + * WAVE format, as defined by RFC 2361. + * + * @since 20.0 + */ + public static final MediaType VND_WAVE_AUDIO = createConstant(AUDIO_TYPE, "vnd.wave"); + + /* video types */ + public static final MediaType MP4_VIDEO = createConstant(VIDEO_TYPE, "mp4"); + public static final MediaType MPEG_VIDEO = createConstant(VIDEO_TYPE, "mpeg"); + public static final MediaType OGG_VIDEO = createConstant(VIDEO_TYPE, "ogg"); + public static final MediaType QUICKTIME = createConstant(VIDEO_TYPE, "quicktime"); + public static final MediaType WEBM_VIDEO = createConstant(VIDEO_TYPE, "webm"); + public static final MediaType WMV = createConstant(VIDEO_TYPE, "x-ms-wmv"); + + /** + * Flash video. For more information, see this link. + * + * @since 20.0 + */ + public static final MediaType FLV_VIDEO = createConstant(VIDEO_TYPE, "x-flv"); + + /** + * The 3GP multimedia container format. For more information, see 3GPP TS + * 26.244. + * + * @since 20.0 + */ + public static final MediaType THREE_GPP_VIDEO = createConstant(VIDEO_TYPE, "3gpp"); + + /** + * The 3G2 multimedia container format. For more information, see 3GPP2 + * C.S0050-B. + * + * @since 20.0 + */ + public static final MediaType THREE_GPP2_VIDEO = createConstant(VIDEO_TYPE, "3gpp2"); + + /* application types */ + /** + * As described in RFC 3023, this constant + * ({@code application/xml}) is used for XML documents that are "unreadable by casual users." + * {@link #XML_UTF_8} is provided for documents that may be read by users. + * + * @since 14.0 + */ + public static final MediaType APPLICATION_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xml"); + + public static final MediaType ATOM_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "atom+xml"); + public static final MediaType BZIP2 = createConstant(APPLICATION_TYPE, "x-bzip2"); + + /** + * Files in the dart + * programming language. + * + * @since 19.0 + */ + public static final MediaType DART_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "dart"); + + /** + * Apple Passbook. + * + * @since 19.0 + */ + public static final MediaType APPLE_PASSBOOK = + createConstant(APPLICATION_TYPE, "vnd.apple.pkpass"); + + /** + * Embedded OpenType fonts. This is + * registered + * with the IANA. + * + * @since 17.0 + */ + public static final MediaType EOT = createConstant(APPLICATION_TYPE, "vnd.ms-fontobject"); + + /** + * As described in the International Digital Publishing Forum + * EPUB is the distribution and interchange format standard for digital publications and + * documents. This media type is defined in the EPUB Open Container Format + * specification. + * + * @since 15.0 + */ + public static final MediaType EPUB = createConstant(APPLICATION_TYPE, "epub+zip"); + + public static final MediaType FORM_DATA = + createConstant(APPLICATION_TYPE, "x-www-form-urlencoded"); + + /** + * As described in PKCS #12: Personal + * Information Exchange Syntax Standard, PKCS #12 defines an archive file format for storing + * many cryptography objects as a single file. + * + * @since 15.0 + */ + public static final MediaType KEY_ARCHIVE = createConstant(APPLICATION_TYPE, "pkcs12"); + + /** + * This is a non-standard media type, but is commonly used in serving hosted binary files as it is + * + * known not to trigger content sniffing in current browsers. It should not be used in + * other situations as it is not specified by any RFC and does not appear in the /IANA MIME Media Types list. Consider + * {@link #OCTET_STREAM} for binary data that is not being served to a browser. + * + * @since 14.0 + */ + public static final MediaType APPLICATION_BINARY = createConstant(APPLICATION_TYPE, "binary"); + + /** + * Media type for the GeoJSON Format, a + * geospatial data interchange format based on JSON. + * + * @since 28.0 + */ + public static final MediaType GEO_JSON = createConstant(APPLICATION_TYPE, "geo+json"); + + public static final MediaType GZIP = createConstant(APPLICATION_TYPE, "x-gzip"); + + /** + * JSON Hypertext + * Application Language (HAL) documents. + * + * @since 26.0 + */ + public static final MediaType HAL_JSON = createConstant(APPLICATION_TYPE, "hal+json"); + + /** + * RFC 4329 declares this to be the + * correct media type for JavaScript, but {@link #TEXT_JAVASCRIPT_UTF_8 text/javascript} may be + * necessary in certain situations for compatibility. + */ + public static final MediaType JAVASCRIPT_UTF_8 = + createConstantUtf8(APPLICATION_TYPE, "javascript"); + + /** + * For JWS or JWE objects using the Compact + * Serialization. + * + * @since 27.1 + */ + public static final MediaType JOSE = createConstant(APPLICATION_TYPE, "jose"); + + /** + * For JWS or JWE objects using the JSON + * Serialization. + * + * @since 27.1 + */ + public static final MediaType JOSE_JSON = createConstant(APPLICATION_TYPE, "jose+json"); + + public static final MediaType JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "json"); + + /** + * For JWT objects using the compact Serialization. + * + * @since 32.0.0 + */ + public static final MediaType JWT = createConstant(APPLICATION_TYPE, "jwt"); + + /** + * The Manifest for a web application. + * + * @since 19.0 + */ + public static final MediaType MANIFEST_JSON_UTF_8 = + createConstantUtf8(APPLICATION_TYPE, "manifest+json"); + + /** + * OGC KML (Keyhole Markup Language). + */ + public static final MediaType KML = createConstant(APPLICATION_TYPE, "vnd.google-earth.kml+xml"); + + /** + * OGC KML (Keyhole Markup Language), + * compressed using the ZIP format into KMZ archives. + */ + public static final MediaType KMZ = createConstant(APPLICATION_TYPE, "vnd.google-earth.kmz"); + + /** + * The mbox database format. + * + * @since 13.0 + */ + public static final MediaType MBOX = createConstant(APPLICATION_TYPE, "mbox"); + + /** + * Apple over-the-air mobile configuration profiles. + * + * @since 18.0 + */ + public static final MediaType APPLE_MOBILE_CONFIG = + createConstant(APPLICATION_TYPE, "x-apple-aspen-config"); + + /** Microsoft Excel spreadsheets. */ + public static final MediaType MICROSOFT_EXCEL = createConstant(APPLICATION_TYPE, "vnd.ms-excel"); + + /** + * Microsoft Outlook items. + * + * @since 27.1 + */ + public static final MediaType MICROSOFT_OUTLOOK = + createConstant(APPLICATION_TYPE, "vnd.ms-outlook"); + + /** Microsoft Powerpoint presentations. */ + public static final MediaType MICROSOFT_POWERPOINT = + createConstant(APPLICATION_TYPE, "vnd.ms-powerpoint"); + + /** Microsoft Word documents. */ + public static final MediaType MICROSOFT_WORD = createConstant(APPLICATION_TYPE, "msword"); + + /** + * Media type for Dynamic Adaptive + * Streaming over HTTP (DASH). This is registered with + * the IANA. + * + * @since 28.2 + */ + public static final MediaType MEDIA_PRESENTATION_DESCRIPTION = + createConstant(APPLICATION_TYPE, "dash+xml"); + + /** + * WASM applications. For more information see the Web Assembly + * overview. + * + * @since 27.0 + */ + public static final MediaType WASM_APPLICATION = createConstant(APPLICATION_TYPE, "wasm"); + + /** + * NaCl applications. For more information see the + * Developer Guide for Native Client Application Structure. + * + * @since 20.0 + */ + public static final MediaType NACL_APPLICATION = createConstant(APPLICATION_TYPE, "x-nacl"); + + /** + * NaCl portable applications. For more information see the + * Developer Guide for Native Client Application Structure. + * + * @since 20.0 + */ + public static final MediaType NACL_PORTABLE_APPLICATION = + createConstant(APPLICATION_TYPE, "x-pnacl"); + + public static final MediaType OCTET_STREAM = createConstant(APPLICATION_TYPE, "octet-stream"); + + public static final MediaType OGG_CONTAINER = createConstant(APPLICATION_TYPE, "ogg"); + public static final MediaType OOXML_DOCUMENT = + createConstant( + APPLICATION_TYPE, "vnd.openxmlformats-officedocument.wordprocessingml.document"); + public static final MediaType OOXML_PRESENTATION = + createConstant( + APPLICATION_TYPE, "vnd.openxmlformats-officedocument.presentationml.presentation"); + public static final MediaType OOXML_SHEET = + createConstant(APPLICATION_TYPE, "vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + public static final MediaType OPENDOCUMENT_GRAPHICS = + createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.graphics"); + public static final MediaType OPENDOCUMENT_PRESENTATION = + createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.presentation"); + public static final MediaType OPENDOCUMENT_SPREADSHEET = + createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.spreadsheet"); + public static final MediaType OPENDOCUMENT_TEXT = + createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.text"); + + /** + * OpenSearch + * Description files are XML files that describe how a website can be used as a search engine by + * consumers (e.g. web browsers). + * + * @since 28.2 + */ + public static final MediaType OPENSEARCH_DESCRIPTION_UTF_8 = + createConstantUtf8(APPLICATION_TYPE, "opensearchdescription+xml"); + + public static final MediaType PDF = createConstant(APPLICATION_TYPE, "pdf"); + public static final MediaType POSTSCRIPT = createConstant(APPLICATION_TYPE, "postscript"); + + /** + * Protocol buffers + * + * @since 15.0 + */ + public static final MediaType PROTOBUF = createConstant(APPLICATION_TYPE, "protobuf"); + + /** + * RDF/XML documents, which are XML + * serializations of Resource Description + * Framework graphs. + * + * @since 14.0 + */ + public static final MediaType RDF_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rdf+xml"); + + public static final MediaType RTF_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rtf"); + + /** + * RFC 8081 declares {@link #FONT_SFNT + * font/sfnt} to be the correct media type for SFNT, but this may be necessary in certain + * situations for compatibility. + * + * @since 17.0 + */ + public static final MediaType SFNT = createConstant(APPLICATION_TYPE, "font-sfnt"); + + public static final MediaType SHOCKWAVE_FLASH = + createConstant(APPLICATION_TYPE, "x-shockwave-flash"); + + /** + * {@code skp} files produced by the 3D Modeling software SketchUp + * + * @since 13.0 + */ + public static final MediaType SKETCHUP = createConstant(APPLICATION_TYPE, "vnd.sketchup.skp"); + + /** + * As described in RFC 3902, this constant + * ({@code application/soap+xml}) is used to identify SOAP 1.2 message envelopes that have been + * serialized with XML 1.0. + * + *

For SOAP 1.1 messages, see {@code XML_UTF_8} per W3C Note on Simple Object Access Protocol + * (SOAP) 1.1 + * + * @since 20.0 + */ + public static final MediaType SOAP_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "soap+xml"); + + public static final MediaType TAR = createConstant(APPLICATION_TYPE, "x-tar"); + + /** + * RFC 8081 declares {@link #FONT_WOFF + * font/woff} to be the correct media type for WOFF, but this may be necessary in certain + * situations for compatibility. + * + * @since 17.0 + */ + public static final MediaType WOFF = createConstant(APPLICATION_TYPE, "font-woff"); + + /** + * RFC 8081 declares {@link #FONT_WOFF2 + * font/woff2} to be the correct media type for WOFF2, but this may be necessary in certain + * situations for compatibility. + * + * @since 20.0 + */ + public static final MediaType WOFF2 = createConstant(APPLICATION_TYPE, "font-woff2"); + + public static final MediaType XHTML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xhtml+xml"); + + /** + * Extensible Resource Descriptors. This is not yet registered with the IANA, but it is specified + * by OASIS in the XRD + * definition and implemented in projects such as WebFinger. + * + * @since 14.0 + */ + public static final MediaType XRD_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xrd+xml"); + + public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); + + /* font types */ + + /** + * A collection of font outlines as defined by RFC + * 8081. + * + * @since 30.0 + */ + public static final MediaType FONT_COLLECTION = createConstant(FONT_TYPE, "collection"); + + /** + * Open Type Font Format (OTF) as defined by + * RFC 8081. + * + * @since 30.0 + */ + public static final MediaType FONT_OTF = createConstant(FONT_TYPE, "otf"); + + /** + * Spline or Scalable Font Format (SFNT). RFC 8081 declares this to be the correct media + * type for SFNT, but {@link #SFNT application/font-sfnt} may be necessary in certain situations + * for compatibility. + * + * @since 30.0 + */ + public static final MediaType FONT_SFNT = createConstant(FONT_TYPE, "sfnt"); + + /** + * True Type Font Format (TTF) as defined by + * RFC 8081. + * + * @since 30.0 + */ + public static final MediaType FONT_TTF = createConstant(FONT_TYPE, "ttf"); + + /** + * Web Open Font Format (WOFF). RFC 8081 declares this to be the correct media + * type for SFNT, but {@link #WOFF application/font-woff} may be necessary in certain situations + * for compatibility. + * + * @since 30.0 + */ + public static final MediaType FONT_WOFF = createConstant(FONT_TYPE, "woff"); + + /** + * Web Open Font Format (WOFF2). + * RFC 8081 declares this to be the correct + * media type for SFNT, but {@link #WOFF2 application/font-woff2} may be necessary in certain + * situations for compatibility. + * + * @since 30.0 + */ + public static final MediaType FONT_WOFF2 = createConstant(FONT_TYPE, "woff2"); + + private final String type; + private final String subtype; + private final ImmutableListMultimap parameters; + + private String toString; + + private int hashCode; + + private Optional parsedCharset; + + private MediaType(String type, String subtype, ImmutableListMultimap parameters) { + this.type = type; + this.subtype = subtype; + this.parameters = parameters; + } + + /** Returns the top-level media type. For example, {@code "text"} in {@code "text/plain"}. */ + public String type() { + return type; + } + + /** Returns the media subtype. For example, {@code "plain"} in {@code "text/plain"}. */ + public String subtype() { + return subtype; + } + + /** Returns a multimap containing the parameters of this media type. */ + public ImmutableListMultimap parameters() { + return parameters; + } + + private Map> parametersAsMap() { + return Maps.transformValues(parameters.asMap(), ImmutableMultiset::copyOf); + } + + /** + * Returns an optional charset for the value of the charset parameter if it is specified. + * + * @throws IllegalStateException if multiple charset values have been set for this media type + * @throws IllegalCharsetNameException if a charset value is present, but illegal + * @throws UnsupportedCharsetException if a charset value is present, but no support is available + * in this instance of the Java virtual machine + */ + public Optional charset() { + // racy single-check idiom, this is safe because Optional is immutable. + Optional local = parsedCharset; + if (local == null) { + String value = null; + local = Optional.absent(); + for (String currentValue : parameters.get(CHARSET_ATTRIBUTE)) { + if (value == null) { + value = currentValue; + local = Optional.of(Charset.forName(value)); + } else if (!value.equals(currentValue)) { + throw new IllegalStateException( + "Multiple charset values defined: " + value + ", " + currentValue); + } + } + parsedCharset = local; + } + return local; + } + + /** + * Returns a new instance with the same type and subtype as this instance, but without any + * parameters. + */ + public MediaType withoutParameters() { + return parameters.isEmpty() ? this : create(type, subtype); + } + + /** + * Replaces all parameters with the given parameters. + * + * @throws IllegalArgumentException if any parameter or value is invalid + */ + public MediaType withParameters(Multimap parameters) { + return create(type, subtype, parameters); + } + + /** + * Replaces all parameters with the given attribute with parameters using the given + * values. If there are no values, any existing parameters with the given attribute are removed. + * + * @throws IllegalArgumentException if either {@code attribute} or {@code values} is invalid + * @since 24.0 + */ + public MediaType withParameters(String attribute, Iterable values) { + checkNotNull(attribute); + checkNotNull(values); + String normalizedAttribute = normalizeToken(attribute); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + for (Entry entry : parameters.entries()) { + String key = entry.getKey(); + if (!normalizedAttribute.equals(key)) { + builder.put(key, entry.getValue()); + } + } + for (String value : values) { + builder.put(normalizedAttribute, normalizeParameterValue(normalizedAttribute, value)); + } + MediaType mediaType = new MediaType(type, subtype, builder.build()); + // if the attribute isn't charset, we can just inherit the current parsedCharset + if (!normalizedAttribute.equals(CHARSET_ATTRIBUTE)) { + mediaType.parsedCharset = this.parsedCharset; + } + // Return one of the constants if the media type is a known type. + return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); + } + + /** + * Replaces all parameters with the given attribute with a single parameter with the + * given value. If multiple parameters with the same attributes are necessary use {@link + * #withParameters(String, Iterable)}. Prefer {@link #withCharset} for setting the {@code charset} + * parameter when using a {@link Charset} object. + * + * @throws IllegalArgumentException if either {@code attribute} or {@code value} is invalid + */ + public MediaType withParameter(String attribute, String value) { + return withParameters(attribute, ImmutableSet.of(value)); + } + + /** + * Returns a new instance with the same type and subtype as this instance, with the {@code + * charset} parameter set to the {@link Charset#name name} of the given charset. Only one {@code + * charset} parameter will be present on the new instance regardless of the number set on this + * one. + * + *

If a charset must be specified that is not supported on this JVM (and thus is not + * representable as a {@link Charset} instance), use {@link #withParameter}. + */ + public MediaType withCharset(Charset charset) { + checkNotNull(charset); + MediaType withCharset = withParameter(CHARSET_ATTRIBUTE, charset.name()); + // precache the charset so we don't need to parse it + withCharset.parsedCharset = Optional.of(charset); + return withCharset; + } + + /** Returns true if either the type or subtype is the wildcard. */ + public boolean hasWildcard() { + return WILDCARD.equals(type) || WILDCARD.equals(subtype); + } + + /** + * Returns {@code true} if this instance falls within the range (as defined by the HTTP Accept header) given + * by the argument according to three criteria: + * + *

    + *
  1. The type of the argument is the wildcard or equal to the type of this instance. + *
  2. The subtype of the argument is the wildcard or equal to the subtype of this instance. + *
  3. All of the parameters present in the argument are present in this instance. + *
+ * + *

For example: + * + *

{@code
+   * PLAIN_TEXT_UTF_8.is(PLAIN_TEXT_UTF_8) // true
+   * PLAIN_TEXT_UTF_8.is(HTML_UTF_8) // false
+   * PLAIN_TEXT_UTF_8.is(ANY_TYPE) // true
+   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE) // true
+   * PLAIN_TEXT_UTF_8.is(ANY_IMAGE_TYPE) // false
+   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_8)) // true
+   * PLAIN_TEXT_UTF_8.withoutParameters().is(ANY_TEXT_TYPE.withCharset(UTF_8)) // false
+   * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_16)) // false
+   * }
+ * + *

Note that while it is possible to have the same parameter declared multiple times within a + * media type this method does not consider the number of occurrences of a parameter. For example, + * {@code "text/plain; charset=UTF-8"} satisfies {@code "text/plain; charset=UTF-8; + * charset=UTF-8"}. + */ + public boolean is(MediaType mediaTypeRange) { + return (mediaTypeRange.type.equals(WILDCARD) || mediaTypeRange.type.equals(this.type)) + && (mediaTypeRange.subtype.equals(WILDCARD) || mediaTypeRange.subtype.equals(this.subtype)) + && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries()); + } + + /** + * Creates a new media type with the given type and subtype. + * + * @throws IllegalArgumentException if type or subtype is invalid or if a wildcard is used for the + * type, but not the subtype. + */ + public static MediaType create(String type, String subtype) { + MediaType mediaType = create(type, subtype, ImmutableListMultimap.of()); + mediaType.parsedCharset = Optional.absent(); + return mediaType; + } + + private static MediaType create( + String type, String subtype, Multimap parameters) { + checkNotNull(type); + checkNotNull(subtype); + checkNotNull(parameters); + String normalizedType = normalizeToken(type); + String normalizedSubtype = normalizeToken(subtype); + checkArgument( + !WILDCARD.equals(normalizedType) || WILDCARD.equals(normalizedSubtype), + "A wildcard type cannot be used with a non-wildcard subtype"); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + for (Entry entry : parameters.entries()) { + String attribute = normalizeToken(entry.getKey()); + builder.put(attribute, normalizeParameterValue(attribute, entry.getValue())); + } + MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); + // Return one of the constants if the media type is a known type. + return MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); + } + + /** + * Creates a media type with the "application" type and the given subtype. + * + * @throws IllegalArgumentException if subtype is invalid + */ + static MediaType createApplicationType(String subtype) { + return create(APPLICATION_TYPE, subtype); + } + + /** + * Creates a media type with the "audio" type and the given subtype. + * + * @throws IllegalArgumentException if subtype is invalid + */ + static MediaType createAudioType(String subtype) { + return create(AUDIO_TYPE, subtype); + } + + /** + * Creates a media type with the "font" type and the given subtype. + * + * @throws IllegalArgumentException if subtype is invalid + */ + static MediaType createFontType(String subtype) { + return create(FONT_TYPE, subtype); + } + + /** + * Creates a media type with the "image" type and the given subtype. + * + * @throws IllegalArgumentException if subtype is invalid + */ + static MediaType createImageType(String subtype) { + return create(IMAGE_TYPE, subtype); + } + + /** + * Creates a media type with the "text" type and the given subtype. + * + * @throws IllegalArgumentException if subtype is invalid + */ + static MediaType createTextType(String subtype) { + return create(TEXT_TYPE, subtype); + } + + /** + * Creates a media type with the "video" type and the given subtype. + * + * @throws IllegalArgumentException if subtype is invalid + */ + static MediaType createVideoType(String subtype) { + return create(VIDEO_TYPE, subtype); + } + + private static String normalizeToken(String token) { + checkArgument(TOKEN_MATCHER.matchesAllOf(token)); + checkArgument(!token.isEmpty()); + return Ascii.toLowerCase(token); + } + + private static String normalizeParameterValue(String attribute, String value) { + checkNotNull(value); // for GWT + checkArgument(ascii().matchesAllOf(value), "parameter values must be ASCII: %s", value); + return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value; + } + + /** + * Parses a media type from its string representation. + * + * @throws IllegalArgumentException if the input is not parsable + */ + // TODO(b/219820829): consider removing + public static MediaType parse(String input) { + checkNotNull(input); + Tokenizer tokenizer = new Tokenizer(input); + try { + String type = tokenizer.consumeToken(TOKEN_MATCHER); + tokenizer.consumeCharacter('/'); + String subtype = tokenizer.consumeToken(TOKEN_MATCHER); + ImmutableListMultimap.Builder parameters = ImmutableListMultimap.builder(); + while (tokenizer.hasMore()) { + tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); + tokenizer.consumeCharacter(';'); + tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); + String attribute = tokenizer.consumeToken(TOKEN_MATCHER); + tokenizer.consumeCharacter('='); + String value; + if ('"' == tokenizer.previewChar()) { + tokenizer.consumeCharacter('"'); + StringBuilder valueBuilder = new StringBuilder(); + while ('"' != tokenizer.previewChar()) { + if ('\\' == tokenizer.previewChar()) { + tokenizer.consumeCharacter('\\'); + valueBuilder.append(tokenizer.consumeCharacter(ascii())); + } else { + valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); + } + } + value = valueBuilder.toString(); + tokenizer.consumeCharacter('"'); + } else { + value = tokenizer.consumeToken(TOKEN_MATCHER); + } + parameters.put(attribute, value); + } + return create(type, subtype, parameters.build()); + } catch (IllegalStateException e) { + throw new IllegalArgumentException("Could not parse '" + input + "'", e); + } + } + + private static final class Tokenizer { + final String input; + int position = 0; + + Tokenizer(String input) { + this.input = input; + } + + String consumeTokenIfPresent(CharMatcher matcher) { + checkState(hasMore()); + int startPosition = position; + position = matcher.negate().indexIn(input, startPosition); + return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition); + } + + String consumeToken(CharMatcher matcher) { + int startPosition = position; + String token = consumeTokenIfPresent(matcher); + checkState(position != startPosition); + return token; + } + + char consumeCharacter(CharMatcher matcher) { + checkState(hasMore()); + char c = previewChar(); + checkState(matcher.matches(c)); + position++; + return c; + } + + char consumeCharacter(char c) { + checkState(hasMore()); + checkState(previewChar() == c); + position++; + return c; + } + + char previewChar() { + checkState(hasMore()); + return input.charAt(position); + } + + boolean hasMore() { + return (position >= 0) && (position < input.length()); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof MediaType) { + MediaType that = (MediaType) obj; + return this.type.equals(that.type) + && this.subtype.equals(that.subtype) + // compare parameters regardless of order + && this.parametersAsMap().equals(that.parametersAsMap()); + } else { + return false; + } + } + + @Override + public int hashCode() { + // racy single-check idiom + int h = hashCode; + if (h == 0) { + h = Objects.hashCode(type, subtype, parametersAsMap()); + hashCode = h; + } + return h; + } + + private static final MapJoiner PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("="); + + /** + * Returns the string representation of this media type in the format described in RFC 2045. + */ + @Override + public String toString() { + // racy single-check idiom, safe because String is immutable + String result = toString; + if (result == null) { + result = computeToString(); + toString = result; + } + return result; + } + + private String computeToString() { + StringBuilder builder = new StringBuilder().append(type).append('/').append(subtype); + if (!parameters.isEmpty()) { + builder.append("; "); + Multimap quotedParameters = + Multimaps.transformValues( + parameters, + (String value) -> + (TOKEN_MATCHER.matchesAllOf(value) && !value.isEmpty()) + ? value + : escapeAndQuote(value)); + PARAMETER_JOINER.appendTo(builder, quotedParameters.entries()); + } + return builder.toString(); + } + + private static String escapeAndQuote(String value) { + StringBuilder escaped = new StringBuilder(value.length() + 16).append('"'); + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + if (ch == '\r' || ch == '\\' || ch == '"') { + escaped.append('\\'); + } + escaped.append(ch); + } + return escaped.append('"').toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/ParametricNullness.java new file mode 100644 index 0000000000000..bc0ae57e4a56e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/PercentEscaper.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/PercentEscaper.java new file mode 100644 index 0000000000000..deefb04904f5c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/PercentEscaper.java @@ -0,0 +1,237 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.escape.UnicodeEscaper; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@code UnicodeEscaper} that escapes some set of Java characters using a UTF-8 based percent + * encoding scheme. The set of safe characters (those which remain unescaped) can be specified on + * construction. + * + *

This class is primarily used for creating URI escapers in {@link UrlEscapers} but can be used + * directly if required. While URI escapers impose specific semantics on which characters are + * considered 'safe', this class has a minimal set of restrictions. + * + *

When escaping a String, the following rules apply: + * + *

    + *
  • All specified safe characters remain unchanged. + *
  • If {@code plusForSpace} was specified, the space character " " is converted into a plus + * sign {@code "+"}. + *
  • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XX", where "XX" is the two-digit, + * uppercase, hexadecimal representation of the byte value. + *
+ * + *

For performance reasons the only currently supported character encoding of this class is + * UTF-8. + * + *

Note: This escaper produces uppercase hexadecimal sequences. + * + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class PercentEscaper extends UnicodeEscaper { + + // In some escapers spaces are escaped to '+' + private static final char[] PLUS_SIGN = {'+'}; + + // Percent escapers output upper case hex digits (uri escapers require this). + private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray(); + + /** If true we should convert space to the {@code +} character. */ + private final boolean plusForSpace; + + /** + * An array of flags where for any {@code char c} if {@code safeOctets[c]} is true then {@code c} + * should remain unmodified in the output. If {@code c >= safeOctets.length} then it should be + * escaped. + */ + private final boolean[] safeOctets; + + /** + * Constructs a percent escaper with the specified safe characters and optional handling of the + * space character. + * + *

Not that it is allowed, but not necessarily desirable to specify {@code %} as a safe + * character. This has the effect of creating an escaper which has no well-defined inverse but it + * can be useful when escaping additional characters. + * + * @param safeChars a non-null string specifying additional safe characters for this escaper (the + * ranges 0..9, a..z and A..Z are always safe and should not be specified here) + * @param plusForSpace true if ASCII space should be escaped to {@code +} rather than {@code %20} + * @throws IllegalArgumentException if any of the parameters were invalid + */ + public PercentEscaper(String safeChars, boolean plusForSpace) { + // TODO(dbeaumont): Switch to static factory methods for creation now that class is final. + // TODO(dbeaumont): Support escapers where alphanumeric chars are not safe. + checkNotNull(safeChars); // eager for GWT. + // Avoid any misunderstandings about the behavior of this escaper + if (safeChars.matches(".*[0-9A-Za-z].*")) { + throw new IllegalArgumentException( + "Alphanumeric characters are always 'safe' and should not be explicitly specified"); + } + safeChars += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + // Avoid ambiguous parameters. Safe characters are never modified so if + // space is a safe character then setting plusForSpace is meaningless. + if (plusForSpace && safeChars.contains(" ")) { + throw new IllegalArgumentException( + "plusForSpace cannot be specified when space is a 'safe' character"); + } + this.plusForSpace = plusForSpace; + this.safeOctets = createSafeOctets(safeChars); + } + + /** + * Creates a boolean array with entries corresponding to the character values specified in + * safeChars set to true. The array is as small as is required to hold the given character + * information. + */ + private static boolean[] createSafeOctets(String safeChars) { + int maxChar = -1; + char[] safeCharArray = safeChars.toCharArray(); + for (char c : safeCharArray) { + maxChar = Math.max(c, maxChar); + } + boolean[] octets = new boolean[maxChar + 1]; + for (char c : safeCharArray) { + octets[c] = true; + } + return octets; + } + + /* + * Overridden for performance. For unescaped strings this improved the performance of the uri + * escaper from ~760ns to ~400ns as measured by {@link CharEscapersBenchmark}. + */ + @Override + protected int nextEscapeIndex(CharSequence csq, int index, int end) { + checkNotNull(csq); + for (; index < end; index++) { + char c = csq.charAt(index); + if (c >= safeOctets.length || !safeOctets[c]) { + break; + } + } + return index; + } + + /* + * Overridden for performance. For unescaped strings this improved the performance of the uri + * escaper from ~400ns to ~170ns as measured by {@link CharEscapersBenchmark}. + */ + @Override + public String escape(String s) { + checkNotNull(s); + int slen = s.length(); + for (int index = 0; index < slen; index++) { + char c = s.charAt(index); + if (c >= safeOctets.length || !safeOctets[c]) { + return escapeSlow(s, index); + } + } + return s; + } + + /** Escapes the given Unicode code point in UTF-8. */ + @Override + protected char[] escape(int cp) { + // We should never get negative values here but if we do it will throw an + // IndexOutOfBoundsException, so at least it will get spotted. + if (cp < safeOctets.length && safeOctets[cp]) { + return null; + } else if (cp == ' ' && plusForSpace) { + return PLUS_SIGN; + } else if (cp <= 0x7F) { + // Single byte UTF-8 characters + // Start with "%--" and fill in the blanks + char[] dest = new char[3]; + dest[0] = '%'; + dest[2] = UPPER_HEX_DIGITS[cp & 0xF]; + dest[1] = UPPER_HEX_DIGITS[cp >>> 4]; + return dest; + } else if (cp <= 0x7ff) { + // Two byte UTF-8 characters [cp >= 0x80 && cp <= 0x7ff] + // Start with "%--%--" and fill in the blanks + char[] dest = new char[6]; + dest[0] = '%'; + dest[3] = '%'; + dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + cp >>>= 2; + dest[2] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[1] = UPPER_HEX_DIGITS[0xC | cp]; + return dest; + } else if (cp <= 0xffff) { + // Three byte UTF-8 characters [cp >= 0x800 && cp <= 0xffff] + // Start with "%E-%--%--" and fill in the blanks + char[] dest = new char[9]; + dest[0] = '%'; + dest[1] = 'E'; + dest[3] = '%'; + dest[6] = '%'; + dest[8] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + cp >>>= 2; + dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + cp >>>= 2; + dest[2] = UPPER_HEX_DIGITS[cp]; + return dest; + } else if (cp <= 0x10ffff) { + char[] dest = new char[12]; + // Four byte UTF-8 characters [cp >= 0xffff && cp <= 0x10ffff] + // Start with "%F-%--%--%--" and fill in the blanks + dest[0] = '%'; + dest[1] = 'F'; + dest[3] = '%'; + dest[6] = '%'; + dest[9] = '%'; + dest[11] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[10] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + cp >>>= 2; + dest[8] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + cp >>>= 2; + dest[5] = UPPER_HEX_DIGITS[cp & 0xF]; + cp >>>= 4; + dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)]; + cp >>>= 2; + dest[2] = UPPER_HEX_DIGITS[cp & 0x7]; + return dest; + } else { + // If this ever happens it is due to bug in UnicodeEscaper, not bad input. + throw new IllegalArgumentException("Invalid unicode character value " + cp); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/UrlEscapers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/UrlEscapers.java new file mode 100644 index 0000000000000..f863a59dc79cb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/UrlEscapers.java @@ -0,0 +1,152 @@ +/* + * 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.iotdb.google.common.net; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.escape.Escaper; + +/** + * {@code Escaper} instances suitable for strings to be included in particular sections of URLs. + * + *

If the resulting URLs are inserted into an HTML or XML document, they will require additional + * escaping with {@link org.apache.iotdb.google.common.html.HtmlEscapers} or {@link + * org.apache.iotdb.google.common.xml.XmlEscapers}. + * + * @author David Beaumont + * @author Chris Povirk + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class UrlEscapers { + private UrlEscapers() {} + + // For each xxxEscaper() method, please add links to external reference pages + // that are considered authoritative for the behavior of that escaper. + + static final String URL_FORM_PARAMETER_OTHER_SAFE_CHARS = "-_.*"; + + static final String URL_PATH_OTHER_SAFE_CHARS_LACKING_PLUS = + "-._~" // Unreserved characters. + + "!$'()*,;&=" // The subdelim characters (excluding '+'). + + "@:"; // The gendelim characters permitted in paths. + + /** + * Returns an {@link Escaper} instance that escapes strings so they can be safely included in URL form parameter names and values. Escaping is performed + * with the UTF-8 character encoding. The caller is responsible for replacing any unpaired carriage return or line feed characters + * with a CR+LF pair on any non-file inputs before escaping them with this escaper. + * + *

When escaping a String, the following rules apply: + * + *

    + *
  • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
  • The special characters ".", "-", "*", and "_" remain the same. + *
  • The space character " " is converted into a plus sign "+". + *
  • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. + *
+ * + *

This escaper is suitable for escaping parameter names and values even when using the non-standard semicolon, rather than the ampersand, as + * a parameter delimiter. Nevertheless, we recommend using the ampersand unless you must + * interoperate with systems that require semicolons. + * + *

Note: Unlike other escapers, URL escapers produce uppercase hexadecimal sequences. + */ + public static Escaper urlFormParameterEscaper() { + return URL_FORM_PARAMETER_ESCAPER; + } + + private static final Escaper URL_FORM_PARAMETER_ESCAPER = + new PercentEscaper(URL_FORM_PARAMETER_OTHER_SAFE_CHARS, true); + + /** + * Returns an {@link Escaper} instance that escapes strings so they can be safely included in URL path segments. The returned escaper escapes all non-ASCII + * characters, even though many of these are accepted in modern + * URLs. (If the escaper were to leave these characters + * unescaped, they would be escaped by the consumer at parse time, anyway.) Additionally, the + * escaper escapes the slash character ("/"). While slashes are acceptable in URL paths, they are + * considered by the specification to be separators between "path segments." This implies that, if + * you wish for your path to contain slashes, you must escape each segment separately and then + * join them. + * + *

When escaping a String, the following rules apply: + * + *

    + *
  • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
  • The unreserved characters ".", "-", "~", and "_" remain the same. + *
  • The general delimiters "@" and ":" remain the same. + *
  • The subdelimiters "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", and "=" remain + * the same. + *
  • The space character " " is converted into %20. + *
  • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. + *
+ * + *

Note: Unlike other escapers, URL escapers produce uppercase hexadecimal sequences. + */ + public static Escaper urlPathSegmentEscaper() { + return URL_PATH_SEGMENT_ESCAPER; + } + + private static final Escaper URL_PATH_SEGMENT_ESCAPER = + new PercentEscaper(URL_PATH_OTHER_SAFE_CHARS_LACKING_PLUS + "+", false); + + /** + * Returns an {@link Escaper} instance that escapes strings so they can be safely included in a URL fragment. The returned escaper escapes all non-ASCII + * characters, even though many of these are accepted in modern + * URLs. + * + *

When escaping a String, the following rules apply: + * + *

    + *
  • The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain + * the same. + *
  • The unreserved characters ".", "-", "~", and "_" remain the same. + *
  • The general delimiters "@" and ":" remain the same. + *
  • The subdelimiters "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", and "=" remain + * the same. + *
  • The space character " " is converted into %20. + *
  • Fragments allow unescaped "/" and "?", so they remain the same. + *
  • All other characters are converted into one or more bytes using UTF-8 encoding and each + * byte is then represented by the 3-character string "%XY", where "XY" is the two-digit, + * uppercase, hexadecimal representation of the byte value. + *
+ * + *

Note: Unlike other escapers, URL escapers produce uppercase hexadecimal sequences. + */ + public static Escaper urlFragmentEscaper() { + return URL_FRAGMENT_ESCAPER; + } + + private static final Escaper URL_FRAGMENT_ESCAPER = + new PercentEscaper(URL_PATH_OTHER_SAFE_CHARS_LACKING_PLUS + "+/?", false); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/package-info.java new file mode 100644 index 0000000000000..fbd834c96c34e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/net/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.net; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Booleans.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Booleans.java new file mode 100644 index 0000000000000..685e005d7ffe2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Booleans.java @@ -0,0 +1,606 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code boolean} primitives, that are not already found in + * either {@link Boolean} or {@link Arrays}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Booleans { + private Booleans() {} + + /** Comparators for {@code Boolean} values. */ + private enum BooleanComparator implements Comparator { + TRUE_FIRST(1, "Booleans.trueFirst()"), + FALSE_FIRST(-1, "Booleans.falseFirst()"); + + private final int trueValue; + private final String toString; + + BooleanComparator(int trueValue, String toString) { + this.trueValue = trueValue; + this.toString = toString; + } + + @Override + public int compare(Boolean a, Boolean b) { + int aVal = a ? trueValue : 0; + int bVal = b ? trueValue : 0; + return bVal - aVal; + } + + @Override + public String toString() { + return toString; + } + } + + /** + * Returns a {@code Comparator} that sorts {@code true} before {@code false}. + * + *

This is particularly useful in Java 8+ in combination with {@code Comparators.comparing}, + * e.g. {@code Comparators.comparing(Foo::hasBar, trueFirst())}. + * + * @since 21.0 + */ + public static Comparator trueFirst() { + return BooleanComparator.TRUE_FIRST; + } + + /** + * Returns a {@code Comparator} that sorts {@code false} before {@code true}. + * + *

This is particularly useful in Java 8+ in combination with {@code Comparators.comparing}, + * e.g. {@code Comparators.comparing(Foo::hasBar, falseFirst())}. + * + * @since 21.0 + */ + public static Comparator falseFirst() { + return BooleanComparator.FALSE_FIRST; + } + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Boolean) + * value).hashCode()}. + * + *

Java 8 users: use {@link Boolean#hashCode(boolean)} instead. + * + * @param value a primitive {@code boolean} value + * @return a hash code for the value + */ + public static int hashCode(boolean value) { + return value ? 1231 : 1237; + } + + /** + * Compares the two specified {@code boolean} values in the standard way ({@code false} is + * considered less than {@code true}). The sign of the value returned is the same as that of + * {@code ((Boolean) a).compareTo(b)}. + * + *

Note for Java 7 and later: this method should be treated as deprecated; use the + * equivalent {@link Boolean#compare} method instead. + * + * @param a the first {@code boolean} to compare + * @param b the second {@code boolean} to compare + * @return a positive number if only {@code a} is {@code true}, a negative number if only {@code + * b} is true, or zero if {@code a == b} + */ + public static int compare(boolean a, boolean b) { + return (a == b) ? 0 : (a ? 1 : -1); + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. + * + *

Note: consider representing the array as a {@link java.util.BitSet} instead, + * replacing {@code Booleans.contains(array, true)} with {@code !bitSet.isEmpty()} and {@code + * Booleans.contains(array, false)} with {@code bitSet.nextClearBit(0) == sizeOfBitSet}. + * + * @param array an array of {@code boolean} values, possibly empty + * @param target a primitive {@code boolean} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(boolean[] array, boolean target) { + for (boolean value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. + * + *

Note: consider representing the array as a {@link java.util.BitSet} instead, and + * using {@link java.util.BitSet#nextSetBit(int)} or {@link java.util.BitSet#nextClearBit(int)}. + * + * @param array an array of {@code boolean} values, possibly empty + * @param target a primitive {@code boolean} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(boolean[] array, boolean target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(boolean[] array, boolean target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(boolean[] array, boolean[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code boolean} values, possibly empty + * @param target a primitive {@code boolean} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(boolean[] array, boolean target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(boolean[] array, boolean target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new boolean[] {a, b}, new boolean[] {}, new boolean[] {c}} returns the array {@code {a, + * b, c}}. + * + * @param arrays zero or more {@code boolean} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static boolean[] concat(boolean[]... arrays) { + int length = 0; + for (boolean[] array : arrays) { + length += array.length; + } + boolean[] result = new boolean[length]; + int pos = 0; + for (boolean[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static boolean[] ensureCapacity(boolean[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code boolean} values separated by {@code separator}. + * For example, {@code join("-", false, true, false)} returns the string {@code + * "false-true-false"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code boolean} values, possibly empty + */ + public static String join(String separator, boolean... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 7); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code boolean} arrays lexicographically. That is, it + * compares, using {@link #compare(boolean, boolean)}), the first pair of values that follow any + * common prefix, or when one array is a prefix of the other, treats the shorter array as the + * lesser. For example, {@code [] < [false] < [false, true] < [true]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(boolean[], + * boolean[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(boolean[] left, boolean[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Booleans.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Booleans.lexicographicalComparator()"; + } + } + + /** + * Copies a collection of {@code Boolean} instances into a new array of primitive {@code boolean} + * values. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + *

Note: consider representing the collection as a {@link java.util.BitSet} instead. + * + * @param collection a collection of {@code Boolean} objects + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + */ + public static boolean[] toArray(Collection collection) { + if (collection instanceof BooleanArrayAsList) { + return ((BooleanArrayAsList) collection).toBooleanArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + boolean[] array = new boolean[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = (Boolean) checkNotNull(boxedArray[i]); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

There are at most two distinct objects in this list, {@code (Boolean) true} and {@code + * (Boolean) false}. Java guarantees that those are always represented by the same objects. + * + *

The returned list is serializable. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(boolean... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new BooleanArrayAsList(backingArray); + } + + @GwtCompatible + private static class BooleanArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final boolean[] array; + final int start; + final int end; + + BooleanArrayAsList(boolean[] array) { + this(array, 0, array.length); + } + + BooleanArrayAsList(boolean[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Boolean get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Boolean) + && Booleans.indexOf(array, (Boolean) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Boolean) { + int i = Booleans.indexOf(array, (Boolean) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Boolean) { + int i = Booleans.lastIndexOf(array, (Boolean) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Boolean set(int index, Boolean element) { + checkElementIndex(index, size()); + boolean oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new BooleanArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof BooleanArrayAsList) { + BooleanArrayAsList that = (BooleanArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Booleans.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 7); + builder.append(array[start] ? "[true" : "[false"); + for (int i = start + 1; i < end; i++) { + builder.append(array[i] ? ", true" : ", false"); + } + return builder.append(']').toString(); + } + + boolean[] toBooleanArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the number of {@code values} that are {@code true}. + * + * @since 16.0 + */ + public static int countTrue(boolean... values) { + int count = 0; + for (boolean value : values) { + if (value) { + count++; + } + } + return count; + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Booleans.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(boolean[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Booleans.asList(array).subList(fromIndex, toIndex))}, but is likely to be + * more efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(boolean[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + boolean tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Booleans.asList(array), + * distance)}, but is somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(boolean[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Booleans.asList(array).subList(fromIndex, toIndex), distance)}, but is + * somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(boolean[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Bytes.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Bytes.java new file mode 100644 index 0000000000000..edd664735a71a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Bytes.java @@ -0,0 +1,456 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.RandomAccess; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code byte} primitives, that are not already found in + * either {@link Byte} or {@link Arrays}, and interpret bytes as neither signed nor unsigned. + * The methods which specifically treat bytes as signed or unsigned are found in {@link SignedBytes} + * and {@link UnsignedBytes}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +// TODO(kevinb): how to prevent warning on UnsignedBytes when building GWT +// javadoc? +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Bytes { + private Bytes() {} + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Byte) + * value).hashCode()}. + * + *

Java 8 users: use {@link Byte#hashCode(byte)} instead. + * + * @param value a primitive {@code byte} value + * @return a hash code for the value + */ + public static int hashCode(byte value) { + return value; + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. + * + * @param array an array of {@code byte} values, possibly empty + * @param target a primitive {@code byte} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(byte[] array, byte target) { + for (byte value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code byte} values, possibly empty + * @param target a primitive {@code byte} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(byte[] array, byte target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(byte[] array, byte target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(byte[] array, byte[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code byte} values, possibly empty + * @param target a primitive {@code byte} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(byte[] array, byte target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(byte[] array, byte target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new byte[] {a, b}, new byte[] {}, new byte[] {c}} returns the array {@code {a, b, c}}. + * + * @param arrays zero or more {@code byte} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static byte[] concat(byte[]... arrays) { + int length = 0; + for (byte[] array : arrays) { + length += array.length; + } + byte[] result = new byte[length]; + int pos = 0; + for (byte[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static byte[] ensureCapacity(byte[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns an array containing each value of {@code collection}, converted to a {@code byte} value + * in the manner of {@link Number#byteValue}. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Number} instances + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + * @since 1.0 (parameter was {@code Collection} before 12.0) + */ + public static byte[] toArray(Collection collection) { + if (collection instanceof ByteArrayAsList) { + return ((ByteArrayAsList) collection).toByteArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + byte[] array = new byte[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = ((Number) checkNotNull(boxedArray[i])).byteValue(); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Byte} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list is serializable. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(byte... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new ByteArrayAsList(backingArray); + } + + @GwtCompatible + private static class ByteArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final byte[] array; + final int start; + final int end; + + ByteArrayAsList(byte[] array) { + this(array, 0, array.length); + } + + ByteArrayAsList(byte[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Byte get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Byte) && Bytes.indexOf(array, (Byte) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Byte) { + int i = Bytes.indexOf(array, (Byte) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Byte) { + int i = Bytes.lastIndexOf(array, (Byte) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Byte set(int index, Byte element) { + checkElementIndex(index, size()); + byte oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new ByteArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ByteArrayAsList) { + ByteArrayAsList that = (ByteArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Bytes.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 5); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + byte[] toByteArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Bytes.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(byte[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Bytes.asList(array).subList(fromIndex, toIndex))}, but is likely to be more + * efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(byte[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + byte tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is somewhat faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(byte[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is somewhat + * faster. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(byte[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Chars.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Chars.java new file mode 100644 index 0000000000000..2228ee1d19a0a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Chars.java @@ -0,0 +1,694 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code char} primitives, that are not already found in + * either {@link Character} or {@link Arrays}. + * + *

All the operations in this class treat {@code char} values strictly numerically; they are + * neither Unicode-aware nor locale-dependent. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Chars { + private Chars() {} + + /** + * The number of bytes required to represent a primitive {@code char} value. + * + *

Java 8 users: use {@link Character#BYTES} instead. + */ + public static final int BYTES = Character.SIZE / Byte.SIZE; + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Character) + * value).hashCode()}. + * + *

Java 8 users: use {@link Character#hashCode(char)} instead. + * + * @param value a primitive {@code char} value + * @return a hash code for the value + */ + public static int hashCode(char value) { + return value; + } + + /** + * Returns the {@code char} value that is equal to {@code value}, if possible. + * + * @param value any value in the range of the {@code char} type + * @return the {@code char} value that equals {@code value} + * @throws IllegalArgumentException if {@code value} is greater than {@link Character#MAX_VALUE} + * or less than {@link Character#MIN_VALUE} + */ + public static char checkedCast(long value) { + char result = (char) value; + checkArgument(result == value, "Out of range: %s", value); + return result; + } + + /** + * Returns the {@code char} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code char} if it is in the range of the {@code char} type, + * {@link Character#MAX_VALUE} if it is too large, or {@link Character#MIN_VALUE} if it is too + * small + */ + public static char saturatedCast(long value) { + if (value > Character.MAX_VALUE) { + return Character.MAX_VALUE; + } + if (value < Character.MIN_VALUE) { + return Character.MIN_VALUE; + } + return (char) value; + } + + /** + * Compares the two specified {@code char} values. The sign of the value returned is the same as + * that of {@code ((Character) a).compareTo(b)}. + * + *

Note for Java 7 and later: this method should be treated as deprecated; use the + * equivalent {@link Character#compare} method instead. + * + * @param a the first {@code char} to compare + * @param b the second {@code char} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(char a, char b) { + return a - b; // safe due to restricted range + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. + * + * @param array an array of {@code char} values, possibly empty + * @param target a primitive {@code char} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(char[] array, char target) { + for (char value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code char} values, possibly empty + * @param target a primitive {@code char} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(char[] array, char target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(char[] array, char target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(char[] array, char[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code char} values, possibly empty + * @param target a primitive {@code char} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(char[] array, char target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(char[] array, char target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the least value present in {@code array}. + * + * @param array a nonempty array of {@code char} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + public static char min(char... array) { + checkArgument(array.length > 0); + char min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } + + /** + * Returns the greatest value present in {@code array}. + * + * @param array a nonempty array of {@code char} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + public static char max(char... array) { + checkArgument(array.length > 0); + char max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } + + /** + * Returns the value nearest to {@code value} which is within the closed range {@code [min..max]}. + * + *

If {@code value} is within the range {@code [min..max]}, {@code value} is returned + * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code + * value} is greater than {@code max}, {@code max} is returned. + * + * @param value the {@code char} value to constrain + * @param min the lower bound (inclusive) of the range to constrain {@code value} to + * @param max the upper bound (inclusive) of the range to constrain {@code value} to + * @throws IllegalArgumentException if {@code min > max} + * @since 21.0 + */ + public static char constrainToRange(char value, char min, char max) { + checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); + return value < min ? min : value < max ? value : max; + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new char[] {a, b}, new char[] {}, new char[] {c}} returns the array {@code {a, b, c}}. + * + * @param arrays zero or more {@code char} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static char[] concat(char[]... arrays) { + int length = 0; + for (char[] array : arrays) { + length += array.length; + } + char[] result = new char[length]; + int pos = 0; + for (char[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + /** + * Returns a big-endian representation of {@code value} in a 2-element byte array; equivalent to + * {@code ByteBuffer.allocate(2).putChar(value).array()}. For example, the input value {@code + * '\\u5432'} would yield the byte array {@code {0x54, 0x32}}. + * + *

If you need to convert and concatenate several values (possibly even of different types), + * use a shared {@link java.nio.ByteBuffer} instance, or use {@link + * org.apache.iotdb.google.common.io.ByteStreams#newDataOutput()} to get a growable buffer. + */ + @GwtIncompatible // doesn't work + public static byte[] toByteArray(char value) { + return new byte[] {(byte) (value >> 8), (byte) value}; + } + + /** + * Returns the {@code char} value whose big-endian representation is stored in the first 2 bytes + * of {@code bytes}; equivalent to {@code ByteBuffer.wrap(bytes).getChar()}. For example, the + * input byte array {@code {0x54, 0x32}} would yield the {@code char} value {@code '\\u5432'}. + * + *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that library exposes much more + * flexibility at little cost in readability. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than 2 elements + */ + @GwtIncompatible // doesn't work + public static char fromByteArray(byte[] bytes) { + checkArgument(bytes.length >= BYTES, "array too small: %s < %s", bytes.length, BYTES); + return fromBytes(bytes[0], bytes[1]); + } + + /** + * Returns the {@code char} value whose byte representation is the given 2 bytes, in big-endian + * order; equivalent to {@code Chars.fromByteArray(new byte[] {b1, b2})}. + * + * @since 7.0 + */ + @GwtIncompatible // doesn't work + public static char fromBytes(byte b1, byte b2) { + return (char) ((b1 << 8) | (b2 & 0xFF)); + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static char[] ensureCapacity(char[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code char} values separated by {@code separator}. + * For example, {@code join("-", '1', '2', '3')} returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code char} values, possibly empty + */ + public static String join(String separator, char... array) { + checkNotNull(separator); + int len = array.length; + if (len == 0) { + return ""; + } + + StringBuilder builder = new StringBuilder(len + separator.length() * (len - 1)); + builder.append(array[0]); + for (int i = 1; i < len; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code char} arrays lexicographically; not advisable + * for sorting user-visible strings as the ordering may not match the conventions of the user's + * locale. That is, it compares, using {@link #compare(char, char)}), the first pair of values + * that follow any common prefix, or when one array is a prefix of the other, treats the shorter + * array as the lesser. For example, {@code [] < ['a'] < ['a', 'b'] < ['b']}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(char[], + * char[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(char[] left, char[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Chars.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Chars.lexicographicalComparator()"; + } + } + + /** + * Copies a collection of {@code Character} instances into a new array of primitive {@code char} + * values. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Character} objects + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + */ + public static char[] toArray(Collection collection) { + if (collection instanceof CharArrayAsList) { + return ((CharArrayAsList) collection).toCharArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + char[] array = new char[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = (Character) checkNotNull(boxedArray[i]); + } + return array; + } + + /** + * Sorts the elements of {@code array} in descending order. + * + * @since 23.1 + */ + public static void sortDescending(char[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + * @since 23.1 + */ + public static void sortDescending(char[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Chars.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(char[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Chars.asList(array).subList(fromIndex, toIndex))}, but is likely to be more + * efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(char[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + char tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Chars.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(char[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Chars.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(char[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Character} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list is serializable. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(char... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new CharArrayAsList(backingArray); + } + + @GwtCompatible + private static class CharArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final char[] array; + final int start; + final int end; + + CharArrayAsList(char[] array) { + this(array, 0, array.length); + } + + CharArrayAsList(char[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Character get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Character) + && Chars.indexOf(array, (Character) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Character) { + int i = Chars.indexOf(array, (Character) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Character) { + int i = Chars.lastIndexOf(array, (Character) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Character set(int index, Character element) { + checkElementIndex(index, size()); + char oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new CharArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof CharArrayAsList) { + CharArrayAsList that = (CharArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Chars.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 3); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + char[] toCharArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Doubles.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Doubles.java new file mode 100644 index 0000000000000..92c1d8adae805 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Doubles.java @@ -0,0 +1,771 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Converter; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.Spliterators; + +import static java.lang.Double.NEGATIVE_INFINITY; +import static java.lang.Double.POSITIVE_INFINITY; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.base.Strings.lenientFormat; + +/** + * Static utility methods pertaining to {@code double} primitives, that are not already found in + * either {@link Double} or {@link Arrays}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Doubles extends DoublesMethodsForWeb { + private Doubles() {} + + /** + * The number of bytes required to represent a primitive {@code double} value. + * + *

Java 8 users: use {@link Double#BYTES} instead. + * + * @since 10.0 + */ + public static final int BYTES = Double.SIZE / Byte.SIZE; + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Double) + * value).hashCode()}. + * + *

Java 8 users: use {@link Double#hashCode(double)} instead. + * + * @param value a primitive {@code double} value + * @return a hash code for the value + */ + public static int hashCode(double value) { + return ((Double) value).hashCode(); + // TODO(kevinb): do it this way when we can (GWT problem): + // long bits = Double.doubleToLongBits(value); + // return (int) (bits ^ (bits >>> 32)); + } + + /** + * Compares the two specified {@code double} values. The sign of the value returned is the same as + * that of ((Double) a).{@linkplain Double#compareTo compareTo}(b). As with that + * method, {@code NaN} is treated as greater than all other values, and {@code 0.0 > -0.0}. + * + *

Note: this method simply delegates to the JDK method {@link Double#compare}. It is + * provided for consistency with the other primitive types, whose compare methods were not added + * to the JDK until JDK 7. + * + * @param a the first {@code double} to compare + * @param b the second {@code double} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(double a, double b) { + return Double.compare(a, b); + } + + /** + * Returns {@code true} if {@code value} represents a real number. This is equivalent to, but not + * necessarily implemented as, {@code !(Double.isInfinite(value) || Double.isNaN(value))}. + * + *

Java 8 users: use {@link Double#isFinite(double)} instead. + * + * @since 10.0 + */ + public static boolean isFinite(double value) { + return NEGATIVE_INFINITY < value && value < POSITIVE_INFINITY; + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. Note + * that this always returns {@code false} when {@code target} is {@code NaN}. + * + * @param array an array of {@code double} values, possibly empty + * @param target a primitive {@code double} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(double[] array, double target) { + for (double value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. Note + * that this always returns {@code -1} when {@code target} is {@code NaN}. + * + * @param array an array of {@code double} values, possibly empty + * @param target a primitive {@code double} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(double[] array, double target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(double[] array, double target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + *

Note that this always returns {@code -1} when {@code target} contains {@code NaN}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(double[] array, double[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. Note + * that this always returns {@code -1} when {@code target} is {@code NaN}. + * + * @param array an array of {@code double} values, possibly empty + * @param target a primitive {@code double} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(double[] array, double target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(double[] array, double target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the least value present in {@code array}, using the same rules of comparison as {@link + * Math#min(double, double)}. + * + * @param array a nonempty array of {@code double} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static double min(double... array) { + checkArgument(array.length > 0); + double min = array[0]; + for (int i = 1; i < array.length; i++) { + min = Math.min(min, array[i]); + } + return min; + } + + /** + * Returns the greatest value present in {@code array}, using the same rules of comparison as + * {@link Math#max(double, double)}. + * + * @param array a nonempty array of {@code double} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static double max(double... array) { + checkArgument(array.length > 0); + double max = array[0]; + for (int i = 1; i < array.length; i++) { + max = Math.max(max, array[i]); + } + return max; + } + + /** + * Returns the value nearest to {@code value} which is within the closed range {@code [min..max]}. + * + *

If {@code value} is within the range {@code [min..max]}, {@code value} is returned + * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code + * value} is greater than {@code max}, {@code max} is returned. + * + * @param value the {@code double} value to constrain + * @param min the lower bound (inclusive) of the range to constrain {@code value} to + * @param max the upper bound (inclusive) of the range to constrain {@code value} to + * @throws IllegalArgumentException if {@code min > max} + * @since 21.0 + */ + public static double constrainToRange(double value, double min, double max) { + // avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984 + // Reject NaN by testing for the good case (min <= max) instead of the bad (min > max). + if (min <= max) { + return Math.min(Math.max(value, min), max); + } + throw new IllegalArgumentException( + lenientFormat("min (%s) must be less than or equal to max (%s)", min, max)); + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new double[] {a, b}, new double[] {}, new double[] {c}} returns the array {@code {a, b, + * c}}. + * + * @param arrays zero or more {@code double} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static double[] concat(double[]... arrays) { + int length = 0; + for (double[] array : arrays) { + length += array.length; + } + double[] result = new double[length]; + int pos = 0; + for (double[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + private static final class DoubleConverter extends Converter + implements Serializable { + static final DoubleConverter INSTANCE = new DoubleConverter(); + + @Override + protected Double doForward(String value) { + return Double.valueOf(value); + } + + @Override + protected String doBackward(Double value) { + return value.toString(); + } + + @Override + public String toString() { + return "Doubles.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + /** + * Returns a serializable converter object that converts between strings and doubles using {@link + * Double#valueOf} and {@link Double#toString()}. + * + * @since 16.0 + */ + public static Converter stringConverter() { + return DoubleConverter.INSTANCE; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static double[] ensureCapacity(double[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code double} values, converted to strings as + * specified by {@link Double#toString(double)}, and separated by {@code separator}. For example, + * {@code join("-", 1.0, 2.0, 3.0)} returns the string {@code "1.0-2.0-3.0"}. + * + *

Note that {@link Double#toString(double)} formats {@code double} differently in GWT + * sometimes. In the previous example, it returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code double} values, possibly empty + */ + public static String join(String separator, double... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 12); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code double} arrays lexicographically. That is, it + * compares, using {@link #compare(double, double)}), the first pair of values that follow any + * common prefix, or when one array is a prefix of the other, treats the shorter array as the + * lesser. For example, {@code [] < [1.0] < [1.0, 2.0] < [2.0]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(double[], + * double[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(double[] left, double[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Double.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Doubles.lexicographicalComparator()"; + } + } + + /** + * Sorts the elements of {@code array} in descending order. + * + *

Note that this method uses the total order imposed by {@link Double#compare}, which treats + * all NaN values as equal and 0.0 as greater than -0.0. + * + * @since 23.1 + */ + public static void sortDescending(double[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + *

Note that this method uses the total order imposed by {@link Double#compare}, which treats + * all NaN values as equal and 0.0 as greater than -0.0. + * + * @since 23.1 + */ + public static void sortDescending(double[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Doubles.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(double[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Doubles.asList(array).subList(fromIndex, toIndex))}, but is likely to be + * more efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(double[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + double tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Bytes.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(double[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Bytes.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(double[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Returns an array containing each value of {@code collection}, converted to a {@code double} + * value in the manner of {@link Number#doubleValue}. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Number} instances + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + * @since 1.0 (parameter was {@code Collection} before 12.0) + */ + public static double[] toArray(Collection collection) { + if (collection instanceof DoubleArrayAsList) { + return ((DoubleArrayAsList) collection).toDoubleArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + double[] array = new double[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = ((Number) checkNotNull(boxedArray[i])).doubleValue(); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Double} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list may have unexpected behavior if it contains {@code NaN}, or if {@code NaN} + * is used as a parameter to any of its methods. + * + *

The returned list is serializable. + * + *

Note: when possible, you should represent your data as an {@link + * ImmutableDoubleArray} instead, which has an {@link ImmutableDoubleArray#asList asList} view. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(double... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new DoubleArrayAsList(backingArray); + } + + @GwtCompatible + private static class DoubleArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final double[] array; + final int start; + final int end; + + DoubleArrayAsList(double[] array) { + this(array, 0, array.length); + } + + DoubleArrayAsList(double[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Double get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public Spliterator.OfDouble spliterator() { + return Spliterators.spliterator(array, start, end, 0); + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Double) + && Doubles.indexOf(array, (Double) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Double) { + int i = Doubles.indexOf(array, (Double) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Double) { + int i = Doubles.lastIndexOf(array, (Double) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Double set(int index, Double element) { + checkElementIndex(index, size()); + double oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new DoubleArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof DoubleArrayAsList) { + DoubleArrayAsList that = (DoubleArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Doubles.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 12); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + double[] toDoubleArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } + + /** + * This is adapted from the regex suggested by {@link Double#valueOf(String)} for prevalidating + * inputs. All valid inputs must pass this regex, but it's semantically fine if not all inputs + * that pass this regex are valid -- only a performance hit is incurred, not a semantics bug. + */ + @J2ktIncompatible @GwtIncompatible // regular expressions + static final java.util.regex.Pattern FLOATING_POINT_PATTERN = fpPattern(); + + @GwtIncompatible // regular expressions + private static java.util.regex.Pattern fpPattern() { + /* + * We use # instead of * for possessive quantifiers. This lets us strip them out when building + * the regex for RE2 (which doesn't support them) but leave them in when building it for + * java.util.regex (where we want them in order to avoid catastrophic backtracking). + */ + String decimal = "(?:\\d+#(?:\\.\\d*#)?|\\.\\d+#)"; + String completeDec = decimal + "(?:[eE][+-]?\\d+#)?[fFdD]?"; + String hex = "(?:[0-9a-fA-F]+#(?:\\.[0-9a-fA-F]*#)?|\\.[0-9a-fA-F]+#)"; + String completeHex = "0[xX]" + hex + "[pP][+-]?\\d+#[fFdD]?"; + String fpPattern = "[+-]?(?:NaN|Infinity|" + completeDec + "|" + completeHex + ")"; + fpPattern = fpPattern.replace("#", "+"); + return java.util.regex.Pattern.compile(fpPattern); + } + + /** + * Parses the specified string as a double-precision floating point value. The ASCII character + * {@code '-'} ('\u002D') is recognized as the minus sign. + * + *

Unlike {@link Double#parseDouble(String)}, this method returns {@code null} instead of + * throwing an exception if parsing fails. Valid inputs are exactly those accepted by {@link + * Double#valueOf(String)}, except that leading and trailing whitespace is not permitted. + * + *

This implementation is likely to be faster than {@code Double.parseDouble} if many failures + * are expected. + * + * @param string the string representation of a {@code double} value + * @return the floating point value represented by {@code string}, or {@code null} if {@code + * string} has a length of zero or cannot be parsed as a {@code double} value + * @throws NullPointerException if {@code string} is {@code null} + * @since 14.0 + */ + @J2ktIncompatible + @GwtIncompatible // regular expressions + public static Double tryParse(String string) { + if (FLOATING_POINT_PATTERN.matcher(string).matches()) { + // TODO(lowasser): could be potentially optimized, but only with + // extensive testing + try { + return Double.parseDouble(string); + } catch (NumberFormatException e) { + // Double.parseDouble has changed specs several times, so fall through + // gracefully + } + } + return null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/DoublesMethodsForWeb.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/DoublesMethodsForWeb.java new file mode 100644 index 0000000000000..2beb2572b0cf4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/DoublesMethodsForWeb.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Holder for web specializations of methods of {@code Doubles}. Intended to be empty for regular + * version. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class DoublesMethodsForWeb {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..a2f33538321c4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Floats.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Floats.java new file mode 100644 index 0000000000000..a1dcd5e4ddb65 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Floats.java @@ -0,0 +1,733 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Converter; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import static java.lang.Float.NEGATIVE_INFINITY; +import static java.lang.Float.POSITIVE_INFINITY; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; +import static org.apache.iotdb.google.common.base.Strings.lenientFormat; + +/** + * Static utility methods pertaining to {@code float} primitives, that are not already found in + * either {@link Float} or {@link Arrays}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Floats extends FloatsMethodsForWeb { + private Floats() {} + + /** + * The number of bytes required to represent a primitive {@code float} value. + * + *

Java 8 users: use {@link Float#BYTES} instead. + * + * @since 10.0 + */ + public static final int BYTES = Float.SIZE / Byte.SIZE; + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Float) + * value).hashCode()}. + * + *

Java 8 users: use {@link Float#hashCode(float)} instead. + * + * @param value a primitive {@code float} value + * @return a hash code for the value + */ + public static int hashCode(float value) { + // TODO(kevinb): is there a better way, that's still gwt-safe? + return ((Float) value).hashCode(); + } + + /** + * Compares the two specified {@code float} values using {@link Float#compare(float, float)}. You + * may prefer to invoke that method directly; this method exists only for consistency with the + * other utilities in this package. + * + *

Note: this method simply delegates to the JDK method {@link Float#compare}. It is + * provided for consistency with the other primitive types, whose compare methods were not added + * to the JDK until JDK 7. + * + * @param a the first {@code float} to compare + * @param b the second {@code float} to compare + * @return the result of invoking {@link Float#compare(float, float)} + */ + public static int compare(float a, float b) { + return Float.compare(a, b); + } + + /** + * Returns {@code true} if {@code value} represents a real number. This is equivalent to, but not + * necessarily implemented as, {@code !(Float.isInfinite(value) || Float.isNaN(value))}. + * + *

Java 8 users: use {@link Float#isFinite(float)} instead. + * + * @since 10.0 + */ + public static boolean isFinite(float value) { + return NEGATIVE_INFINITY < value && value < POSITIVE_INFINITY; + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. Note + * that this always returns {@code false} when {@code target} is {@code NaN}. + * + * @param array an array of {@code float} values, possibly empty + * @param target a primitive {@code float} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(float[] array, float target) { + for (float value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. Note + * that this always returns {@code -1} when {@code target} is {@code NaN}. + * + * @param array an array of {@code float} values, possibly empty + * @param target a primitive {@code float} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(float[] array, float target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(float[] array, float target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + *

Note that this always returns {@code -1} when {@code target} contains {@code NaN}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(float[] array, float[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. Note + * that this always returns {@code -1} when {@code target} is {@code NaN}. + * + * @param array an array of {@code float} values, possibly empty + * @param target a primitive {@code float} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(float[] array, float target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(float[] array, float target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the least value present in {@code array}, using the same rules of comparison as {@link + * Math#min(float, float)}. + * + * @param array a nonempty array of {@code float} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static float min(float... array) { + checkArgument(array.length > 0); + float min = array[0]; + for (int i = 1; i < array.length; i++) { + min = Math.min(min, array[i]); + } + return min; + } + + /** + * Returns the greatest value present in {@code array}, using the same rules of comparison as + * {@link Math#max(float, float)}. + * + * @param array a nonempty array of {@code float} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static float max(float... array) { + checkArgument(array.length > 0); + float max = array[0]; + for (int i = 1; i < array.length; i++) { + max = Math.max(max, array[i]); + } + return max; + } + + /** + * Returns the value nearest to {@code value} which is within the closed range {@code [min..max]}. + * + *

If {@code value} is within the range {@code [min..max]}, {@code value} is returned + * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code + * value} is greater than {@code max}, {@code max} is returned. + * + * @param value the {@code float} value to constrain + * @param min the lower bound (inclusive) of the range to constrain {@code value} to + * @param max the upper bound (inclusive) of the range to constrain {@code value} to + * @throws IllegalArgumentException if {@code min > max} + * @since 21.0 + */ + public static float constrainToRange(float value, float min, float max) { + // avoid auto-boxing by not using Preconditions.checkArgument(); see Guava issue 3984 + // Reject NaN by testing for the good case (min <= max) instead of the bad (min > max). + if (min <= max) { + return Math.min(Math.max(value, min), max); + } + throw new IllegalArgumentException( + lenientFormat("min (%s) must be less than or equal to max (%s)", min, max)); + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new float[] {a, b}, new float[] {}, new float[] {c}} returns the array {@code {a, b, + * c}}. + * + * @param arrays zero or more {@code float} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static float[] concat(float[]... arrays) { + int length = 0; + for (float[] array : arrays) { + length += array.length; + } + float[] result = new float[length]; + int pos = 0; + for (float[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + private static final class FloatConverter extends Converter + implements Serializable { + static final FloatConverter INSTANCE = new FloatConverter(); + + @Override + protected Float doForward(String value) { + return Float.valueOf(value); + } + + @Override + protected String doBackward(Float value) { + return value.toString(); + } + + @Override + public String toString() { + return "Floats.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + /** + * Returns a serializable converter object that converts between strings and floats using {@link + * Float#valueOf} and {@link Float#toString()}. + * + * @since 16.0 + */ + public static Converter stringConverter() { + return FloatConverter.INSTANCE; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static float[] ensureCapacity(float[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code float} values, converted to strings as + * specified by {@link Float#toString(float)}, and separated by {@code separator}. For example, + * {@code join("-", 1.0f, 2.0f, 3.0f)} returns the string {@code "1.0-2.0-3.0"}. + * + *

Note that {@link Float#toString(float)} formats {@code float} differently in GWT. In the + * previous example, it returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code float} values, possibly empty + */ + public static String join(String separator, float... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 12); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code float} arrays lexicographically. That is, it + * compares, using {@link #compare(float, float)}), the first pair of values that follow any + * common prefix, or when one array is a prefix of the other, treats the shorter array as the + * lesser. For example, {@code [] < [1.0f] < [1.0f, 2.0f] < [2.0f]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(float[], + * float[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(float[] left, float[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Float.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Floats.lexicographicalComparator()"; + } + } + + /** + * Sorts the elements of {@code array} in descending order. + * + *

Note that this method uses the total order imposed by {@link Float#compare}, which treats + * all NaN values as equal and 0.0 as greater than -0.0. + * + * @since 23.1 + */ + public static void sortDescending(float[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + *

Note that this method uses the total order imposed by {@link Float#compare}, which treats + * all NaN values as equal and 0.0 as greater than -0.0. + * + * @since 23.1 + */ + public static void sortDescending(float[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Floats.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(float[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Floats.asList(array).subList(fromIndex, toIndex))}, but is likely to be + * more efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(float[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + float tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Floats.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(float[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Floats.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(float[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Returns an array containing each value of {@code collection}, converted to a {@code float} + * value in the manner of {@link Number#floatValue}. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Number} instances + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + * @since 1.0 (parameter was {@code Collection} before 12.0) + */ + public static float[] toArray(Collection collection) { + if (collection instanceof FloatArrayAsList) { + return ((FloatArrayAsList) collection).toFloatArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + float[] array = new float[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = ((Number) checkNotNull(boxedArray[i])).floatValue(); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Float} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list may have unexpected behavior if it contains {@code NaN}, or if {@code NaN} + * is used as a parameter to any of its methods. + * + *

The returned list is serializable. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(float... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new FloatArrayAsList(backingArray); + } + + @GwtCompatible + private static class FloatArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final float[] array; + final int start; + final int end; + + FloatArrayAsList(float[] array) { + this(array, 0, array.length); + } + + FloatArrayAsList(float[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Float get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Float) && Floats.indexOf(array, (Float) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Float) { + int i = Floats.indexOf(array, (Float) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Float) { + int i = Floats.lastIndexOf(array, (Float) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Float set(int index, Float element) { + checkElementIndex(index, size()); + float oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new FloatArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof FloatArrayAsList) { + FloatArrayAsList that = (FloatArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Floats.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 12); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + float[] toFloatArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } + + /** + * Parses the specified string as a single-precision floating point value. The ASCII character + * {@code '-'} ('\u002D') is recognized as the minus sign. + * + *

Unlike {@link Float#parseFloat(String)}, this method returns {@code null} instead of + * throwing an exception if parsing fails. Valid inputs are exactly those accepted by {@link + * Float#valueOf(String)}, except that leading and trailing whitespace is not permitted. + * + *

This implementation is likely to be faster than {@code Float.parseFloat} if many failures + * are expected. + * + * @param string the string representation of a {@code float} value + * @return the floating point value represented by {@code string}, or {@code null} if {@code + * string} has a length of zero or cannot be parsed as a {@code float} value + * @throws NullPointerException if {@code string} is {@code null} + * @since 14.0 + */ + @J2ktIncompatible + @GwtIncompatible // regular expressions + public static Float tryParse(String string) { + if (Doubles.FLOATING_POINT_PATTERN.matcher(string).matches()) { + // TODO(lowasser): could be potentially optimized, but only with + // extensive testing + try { + return Float.parseFloat(string); + } catch (NumberFormatException e) { + // Float.parseFloat has changed specs several times, so fall through + // gracefully + } + } + return null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/FloatsMethodsForWeb.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/FloatsMethodsForWeb.java new file mode 100644 index 0000000000000..2f09713168d85 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/FloatsMethodsForWeb.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Holder for web specializations of methods of {@code Floats}. Intended to be empty for regular + * version. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class FloatsMethodsForWeb {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableDoubleArray.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableDoubleArray.java new file mode 100644 index 0000000000000..07937d7bf7b98 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableDoubleArray.java @@ -0,0 +1,625 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.DoubleConsumer; +import java.util.stream.DoubleStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An immutable array of {@code double} values, with an API resembling {@link List}. + * + *

Advantages compared to {@code double[]}: + * + *

    + *
  • All the many well-known advantages of immutability (read Effective Java, third + * edition, Item 17). + *
  • Has the value-based (not identity-based) {@link #equals}, {@link #hashCode}, and {@link + * #toString} behavior you expect. + *
  • Offers useful operations beyond just {@code get} and {@code length}, so you don't have to + * hunt through classes like {@link Arrays} and {@link Doubles} for them. + *
  • Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to + * add overloads that accept start and end indexes. + *
  • Can be streamed without "breaking the chain": {@code foo.getBarDoubles().stream()...}. + *
  • Access to all collection-based utilities via {@link #asList} (though at the cost of + * allocating garbage). + *
+ * + *

Disadvantages compared to {@code double[]}: + * + *

    + *
  • Memory footprint has a fixed overhead (about 24 bytes per instance). + *
  • Some construction use cases force the data to be copied (though several construction + * APIs are offered that don't). + *
  • Can't be passed directly to methods that expect {@code double[]} (though the most common + * utilities do have replacements here). + *
  • Dependency on {@code org.apache.iotdb.google.common} / Guava. + *
+ * + *

Advantages compared to {@link org.apache.iotdb.google.common.collect.ImmutableList + * ImmutableList}{@code }: + * + *

    + *
  • Improved memory compactness and locality. + *
  • Can be queried without allocating garbage. + *
  • Access to {@code DoubleStream} features (like {@link DoubleStream#sum}) using {@code + * stream()} instead of the awkward {@code stream().mapToDouble(v -> v)}. + *
+ * + *

Disadvantages compared to {@code ImmutableList}: + * + *

    + *
  • Can't be passed directly to methods that expect {@code Iterable}, {@code Collection}, or + * {@code List} (though the most common utilities do have replacements here, and there is a + * lazy {@link #asList} view). + *
+ * + * @since 22.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class ImmutableDoubleArray implements Serializable { + private static final ImmutableDoubleArray EMPTY = new ImmutableDoubleArray(new double[0]); + + /** Returns the empty array. */ + public static ImmutableDoubleArray of() { + return EMPTY; + } + + /** Returns an immutable array containing a single value. */ + public static ImmutableDoubleArray of(double e0) { + return new ImmutableDoubleArray(new double[] {e0}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray of(double e0, double e1) { + return new ImmutableDoubleArray(new double[] {e0, e1}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray of(double e0, double e1, double e2) { + return new ImmutableDoubleArray(new double[] {e0, e1, e2}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray of(double e0, double e1, double e2, double e3) { + return new ImmutableDoubleArray(new double[] {e0, e1, e2, e3}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray of(double e0, double e1, double e2, double e3, double e4) { + return new ImmutableDoubleArray(new double[] {e0, e1, e2, e3, e4}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray of( + double e0, double e1, double e2, double e3, double e4, double e5) { + return new ImmutableDoubleArray(new double[] {e0, e1, e2, e3, e4, e5}); + } + + // TODO(kevinb): go up to 11? + + /** + * Returns an immutable array containing the given values, in order. + * + *

The array {@code rest} must not be longer than {@code Integer.MAX_VALUE - 1}. + */ + // Use (first, rest) so that `of(someDoubleArray)` won't compile (they should use copyOf), which + // is okay since we have to copy the just-created array anyway. + public static ImmutableDoubleArray of(double first, double... rest) { + checkArgument( + rest.length <= Integer.MAX_VALUE - 1, "the total number of elements must fit in an int"); + double[] array = new double[rest.length + 1]; + array[0] = first; + System.arraycopy(rest, 0, array, 1, rest.length); + return new ImmutableDoubleArray(array); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray copyOf(double[] values) { + return values.length == 0 + ? EMPTY + : new ImmutableDoubleArray(Arrays.copyOf(values, values.length)); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableDoubleArray copyOf(Collection values) { + return values.isEmpty() ? EMPTY : new ImmutableDoubleArray(Doubles.toArray(values)); + } + + /** + * Returns an immutable array containing the given values, in order. + * + *

Performance note: this method delegates to {@link #copyOf(Collection)} if {@code + * values} is a {@link Collection}. Otherwise it creates a {@link #builder} and uses {@link + * Builder#addAll(Iterable)}, with all the performance implications associated with that. + */ + public static ImmutableDoubleArray copyOf(Iterable values) { + if (values instanceof Collection) { + return copyOf((Collection) values); + } + return builder().addAll(values).build(); + } + + /** Returns an immutable array containing all the values from {@code stream}, in order. */ + public static ImmutableDoubleArray copyOf(DoubleStream stream) { + // Note this uses very different growth behavior from copyOf(Iterable) and the builder. + double[] array = stream.toArray(); + return (array.length == 0) ? EMPTY : new ImmutableDoubleArray(array); + } + + /** + * Returns a new, empty builder for {@link ImmutableDoubleArray} instances, sized to hold up to + * {@code initialCapacity} values without resizing. The returned builder is not thread-safe. + * + *

Performance note: When feasible, {@code initialCapacity} should be the exact number + * of values that will be added, if that knowledge is readily available. It is better to guess a + * value slightly too high than slightly too low. If the value is not exact, the {@link + * ImmutableDoubleArray} that is built will very likely occupy more memory than strictly + * necessary; to trim memory usage, build using {@code builder.build().trimmed()}. + */ + public static Builder builder(int initialCapacity) { + checkArgument(initialCapacity >= 0, "Invalid initialCapacity: %s", initialCapacity); + return new Builder(initialCapacity); + } + + /** + * Returns a new, empty builder for {@link ImmutableDoubleArray} instances, with a default initial + * capacity. The returned builder is not thread-safe. + * + *

Performance note: The {@link ImmutableDoubleArray} that is built will very likely + * occupy more memory than necessary; to trim memory usage, build using {@code + * builder.build().trimmed()}. + */ + public static Builder builder() { + return new Builder(10); + } + + /** + * A builder for {@link ImmutableDoubleArray} instances; obtained using {@link + * ImmutableDoubleArray#builder}. + */ + public static final class Builder { + private double[] array; + private int count = 0; // <= array.length + + Builder(int initialCapacity) { + array = new double[initialCapacity]; + } + + /** + * Appends {@code value} to the end of the values the built {@link ImmutableDoubleArray} will + * contain. + */ + public Builder add(double value) { + ensureRoomFor(1); + array[count] = value; + count += 1; + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableDoubleArray} will contain. + */ + public Builder addAll(double[] values) { + ensureRoomFor(values.length); + System.arraycopy(values, 0, array, count, values.length); + count += values.length; + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableDoubleArray} will contain. + */ + public Builder addAll(Iterable values) { + if (values instanceof Collection) { + return addAll((Collection) values); + } + for (Double value : values) { + add(value); + } + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableDoubleArray} will contain. + */ + public Builder addAll(Collection values) { + ensureRoomFor(values.size()); + for (Double value : values) { + array[count++] = value; + } + return this; + } + + /** + * Appends all values from {@code stream}, in order, to the end of the values the built {@link + * ImmutableDoubleArray} will contain. + */ + public Builder addAll(DoubleStream stream) { + Spliterator.OfDouble spliterator = stream.spliterator(); + long size = spliterator.getExactSizeIfKnown(); + if (size > 0) { // known *and* nonempty + ensureRoomFor(Ints.saturatedCast(size)); + } + spliterator.forEachRemaining((DoubleConsumer) this::add); + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableDoubleArray} will contain. + */ + public Builder addAll(ImmutableDoubleArray values) { + ensureRoomFor(values.length()); + System.arraycopy(values.array, values.start, array, count, values.length()); + count += values.length(); + return this; + } + + private void ensureRoomFor(int numberToAdd) { + int newCount = count + numberToAdd; // TODO(kevinb): check overflow now? + if (newCount > array.length) { + array = Arrays.copyOf(array, expandedCapacity(array.length, newCount)); + } + } + + // Unfortunately this is pasted from ImmutableCollection.Builder. + private static int expandedCapacity(int oldCapacity, int minCapacity) { + if (minCapacity < 0) { + throw new AssertionError("cannot store more than MAX_VALUE elements"); + } + // careful of overflow! + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; // guaranteed to be >= newCapacity + } + return newCapacity; + } + + /** + * Returns a new immutable array. The builder can continue to be used after this call, to append + * more values and build again. + * + *

Performance note: the returned array is backed by the same array as the builder, so + * no data is copied as part of this step, but this may occupy more memory than strictly + * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}. + */ + public ImmutableDoubleArray build() { + return count == 0 ? EMPTY : new ImmutableDoubleArray(array, 0, count); + } + } + + // Instance stuff here + + // The array is never mutated after storing in this field and the construction strategies ensure + // it doesn't escape this class + @SuppressWarnings("Immutable") + private final double[] array; + + /* + * TODO(kevinb): evaluate the trade-offs of going bimorphic to save these two fields from most + * instances. Note that the instances that would get smaller are the right set to care about + * optimizing, because the rest have the option of calling `trimmed`. + */ + + private final transient int start; // it happens that we only serialize instances where this is 0 + private final int end; // exclusive + + private ImmutableDoubleArray(double[] array) { + this(array, 0, array.length); + } + + private ImmutableDoubleArray(double[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + /** Returns the number of values in this array. */ + public int length() { + return end - start; + } + + /** Returns {@code true} if there are no values in this array ({@link #length} is zero). */ + public boolean isEmpty() { + return end == start; + } + + /** + * Returns the {@code double} value present at the given index. + * + * @throws IndexOutOfBoundsException if {@code index} is negative, or greater than or equal to + * {@link #length} + */ + public double get(int index) { + Preconditions.checkElementIndex(index, length()); + return array[start + index]; + } + + /** + * Returns the smallest index for which {@link #get} returns {@code target}, or {@code -1} if no + * such index exists. Values are compared as if by {@link Double#equals}. Equivalent to {@code + * asList().indexOf(target)}. + */ + public int indexOf(double target) { + for (int i = start; i < end; i++) { + if (areEqual(array[i], target)) { + return i - start; + } + } + return -1; + } + + /** + * Returns the largest index for which {@link #get} returns {@code target}, or {@code -1} if no + * such index exists. Values are compared as if by {@link Double#equals}. Equivalent to {@code + * asList().lastIndexOf(target)}. + */ + public int lastIndexOf(double target) { + for (int i = end - 1; i >= start; i--) { + if (areEqual(array[i], target)) { + return i - start; + } + } + return -1; + } + + /** + * Returns {@code true} if {@code target} is present at any index in this array. Values are + * compared as if by {@link Double#equals}. Equivalent to {@code asList().contains(target)}. + */ + public boolean contains(double target) { + return indexOf(target) >= 0; + } + + /** Invokes {@code consumer} for each value contained in this array, in order. */ + public void forEach(DoubleConsumer consumer) { + checkNotNull(consumer); + for (int i = start; i < end; i++) { + consumer.accept(array[i]); + } + } + + /** Returns a stream over the values in this array, in order. */ + public DoubleStream stream() { + return Arrays.stream(array, start, end); + } + + /** Returns a new, mutable copy of this array's values, as a primitive {@code double[]}. */ + public double[] toArray() { + return Arrays.copyOfRange(array, start, end); + } + + /** + * Returns a new immutable array containing the values in the specified range. + * + *

Performance note: The returned array has the same full memory footprint as this one + * does (no actual copying is performed). To reduce memory usage, use {@code subArray(start, + * end).trimmed()}. + */ + public ImmutableDoubleArray subArray(int startIndex, int endIndex) { + Preconditions.checkPositionIndexes(startIndex, endIndex, length()); + return startIndex == endIndex + ? EMPTY + : new ImmutableDoubleArray(array, start + startIndex, start + endIndex); + } + + private Spliterator.OfDouble spliterator() { + return Spliterators.spliterator(array, start, end, Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + + /** + * Returns an immutable view of this array's values as a {@code List}; note that {@code + * double} values are boxed into {@link Double} instances on demand, which can be very expensive. + * The returned list should be used once and discarded. For any usages beyond that, pass the + * returned list to {@link org.apache.iotdb.google.common.collect.ImmutableList#copyOf(Collection) + * ImmutableList.copyOf} and use that list instead. + */ + public List asList() { + /* + * Typically we cache this kind of thing, but much repeated use of this view is a performance + * anti-pattern anyway. If we cache, then everyone pays a price in memory footprint even if + * they never use this method. + */ + return new AsList(this); + } + + static class AsList extends AbstractList implements RandomAccess, Serializable { + private final ImmutableDoubleArray parent; + + private AsList(ImmutableDoubleArray parent) { + this.parent = parent; + } + + // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, stream, forEach, mutations + + @Override + public int size() { + return parent.length(); + } + + @Override + public Double get(int index) { + return parent.get(index); + } + + @Override + public boolean contains(Object target) { + return indexOf(target) >= 0; + } + + @Override + public int indexOf(Object target) { + return target instanceof Double ? parent.indexOf((Double) target) : -1; + } + + @Override + public int lastIndexOf(Object target) { + return target instanceof Double ? parent.lastIndexOf((Double) target) : -1; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return parent.subArray(fromIndex, toIndex).asList(); + } + + // The default List spliterator is not efficiently splittable + @Override + public Spliterator spliterator() { + return parent.spliterator(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof AsList) { + AsList that = (AsList) object; + return this.parent.equals(that.parent); + } + // We could delegate to super now but it would still box too much + if (!(object instanceof List)) { + return false; + } + List that = (List) object; + if (this.size() != that.size()) { + return false; + } + int i = parent.start; + // Since `that` is very likely RandomAccess we could avoid allocating this iterator... + for (Object element : that) { + if (!(element instanceof Double) || !areEqual(parent.array[i++], (Double) element)) { + return false; + } + } + return true; + } + + // Because we happen to use the same formula. If that changes, just don't override this. + @Override + public int hashCode() { + return parent.hashCode(); + } + + @Override + public String toString() { + return parent.toString(); + } + } + + /** + * Returns {@code true} if {@code object} is an {@code ImmutableDoubleArray} containing the same + * values as this one, in the same order. Values are compared as if by {@link Double#equals}. + */ + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof ImmutableDoubleArray)) { + return false; + } + ImmutableDoubleArray that = (ImmutableDoubleArray) object; + if (this.length() != that.length()) { + return false; + } + for (int i = 0; i < length(); i++) { + if (!areEqual(this.get(i), that.get(i))) { + return false; + } + } + return true; + } + + // Match the behavior of Double.equals() + private static boolean areEqual(double a, double b) { + return Double.doubleToLongBits(a) == Double.doubleToLongBits(b); + } + + /** Returns an unspecified hash code for the contents of this immutable array. */ + @Override + public int hashCode() { + int hash = 1; + for (int i = start; i < end; i++) { + hash *= 31; + hash += Doubles.hashCode(array[i]); + } + return hash; + } + + /** + * Returns a string representation of this array in the same form as {@link + * Arrays#toString(double[])}, for example {@code "[1, 2, 3]"}. + */ + @Override + public String toString() { + if (isEmpty()) { + return "[]"; + } + StringBuilder builder = new StringBuilder(length() * 5); // rough estimate is fine + builder.append('[').append(array[start]); + + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + builder.append(']'); + return builder.toString(); + } + + /** + * Returns an immutable array containing the same values as {@code this} array. This is logically + * a no-op, and in some circumstances {@code this} itself is returned. However, if this instance + * is a {@link #subArray} view of a larger array, this method will copy only the appropriate range + * of values, resulting in an equivalent array with a smaller memory footprint. + */ + public ImmutableDoubleArray trimmed() { + return isPartialView() ? new ImmutableDoubleArray(toArray()) : this; + } + + private boolean isPartialView() { + return start > 0 || end < array.length; + } + + Object writeReplace() { + return trimmed(); + } + + Object readResolve() { + return isEmpty() ? EMPTY : this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableIntArray.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableIntArray.java new file mode 100644 index 0000000000000..8cbf1d07dc5d3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableIntArray.java @@ -0,0 +1,615 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An immutable array of {@code int} values, with an API resembling {@link List}. + * + *

Advantages compared to {@code int[]}: + * + *

    + *
  • All the many well-known advantages of immutability (read Effective Java, third + * edition, Item 17). + *
  • Has the value-based (not identity-based) {@link #equals}, {@link #hashCode}, and {@link + * #toString} behavior you expect. + *
  • Offers useful operations beyond just {@code get} and {@code length}, so you don't have to + * hunt through classes like {@link Arrays} and {@link Ints} for them. + *
  • Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to + * add overloads that accept start and end indexes. + *
  • Can be streamed without "breaking the chain": {@code foo.getBarInts().stream()...}. + *
  • Access to all collection-based utilities via {@link #asList} (though at the cost of + * allocating garbage). + *
+ * + *

Disadvantages compared to {@code int[]}: + * + *

    + *
  • Memory footprint has a fixed overhead (about 24 bytes per instance). + *
  • Some construction use cases force the data to be copied (though several construction + * APIs are offered that don't). + *
  • Can't be passed directly to methods that expect {@code int[]} (though the most common + * utilities do have replacements here). + *
  • Dependency on {@code org.apache.iotdb.google.common} / Guava. + *
+ * + *

Advantages compared to {@link org.apache.iotdb.google.common.collect.ImmutableList + * ImmutableList}{@code }: + * + *

    + *
  • Improved memory compactness and locality. + *
  • Can be queried without allocating garbage. + *
  • Access to {@code IntStream} features (like {@link IntStream#sum}) using {@code stream()} + * instead of the awkward {@code stream().mapToInt(v -> v)}. + *
+ * + *

Disadvantages compared to {@code ImmutableList}: + * + *

    + *
  • Can't be passed directly to methods that expect {@code Iterable}, {@code Collection}, or + * {@code List} (though the most common utilities do have replacements here, and there is a + * lazy {@link #asList} view). + *
+ * + * @since 22.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class ImmutableIntArray implements Serializable { + private static final ImmutableIntArray EMPTY = new ImmutableIntArray(new int[0]); + + /** Returns the empty array. */ + public static ImmutableIntArray of() { + return EMPTY; + } + + /** Returns an immutable array containing a single value. */ + public static ImmutableIntArray of(int e0) { + return new ImmutableIntArray(new int[] {e0}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray of(int e0, int e1) { + return new ImmutableIntArray(new int[] {e0, e1}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray of(int e0, int e1, int e2) { + return new ImmutableIntArray(new int[] {e0, e1, e2}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray of(int e0, int e1, int e2, int e3) { + return new ImmutableIntArray(new int[] {e0, e1, e2, e3}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray of(int e0, int e1, int e2, int e3, int e4) { + return new ImmutableIntArray(new int[] {e0, e1, e2, e3, e4}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray of(int e0, int e1, int e2, int e3, int e4, int e5) { + return new ImmutableIntArray(new int[] {e0, e1, e2, e3, e4, e5}); + } + + // TODO(kevinb): go up to 11? + + /** + * Returns an immutable array containing the given values, in order. + * + *

The array {@code rest} must not be longer than {@code Integer.MAX_VALUE - 1}. + */ + // Use (first, rest) so that `of(someIntArray)` won't compile (they should use copyOf), which is + // okay since we have to copy the just-created array anyway. + public static ImmutableIntArray of(int first, int... rest) { + checkArgument( + rest.length <= Integer.MAX_VALUE - 1, "the total number of elements must fit in an int"); + int[] array = new int[rest.length + 1]; + array[0] = first; + System.arraycopy(rest, 0, array, 1, rest.length); + return new ImmutableIntArray(array); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray copyOf(int[] values) { + return values.length == 0 ? EMPTY : new ImmutableIntArray(Arrays.copyOf(values, values.length)); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableIntArray copyOf(Collection values) { + return values.isEmpty() ? EMPTY : new ImmutableIntArray(Ints.toArray(values)); + } + + /** + * Returns an immutable array containing the given values, in order. + * + *

Performance note: this method delegates to {@link #copyOf(Collection)} if {@code + * values} is a {@link Collection}. Otherwise it creates a {@link #builder} and uses {@link + * Builder#addAll(Iterable)}, with all the performance implications associated with that. + */ + public static ImmutableIntArray copyOf(Iterable values) { + if (values instanceof Collection) { + return copyOf((Collection) values); + } + return builder().addAll(values).build(); + } + + /** Returns an immutable array containing all the values from {@code stream}, in order. */ + public static ImmutableIntArray copyOf(IntStream stream) { + // Note this uses very different growth behavior from copyOf(Iterable) and the builder. + int[] array = stream.toArray(); + return (array.length == 0) ? EMPTY : new ImmutableIntArray(array); + } + + /** + * Returns a new, empty builder for {@link ImmutableIntArray} instances, sized to hold up to + * {@code initialCapacity} values without resizing. The returned builder is not thread-safe. + * + *

Performance note: When feasible, {@code initialCapacity} should be the exact number + * of values that will be added, if that knowledge is readily available. It is better to guess a + * value slightly too high than slightly too low. If the value is not exact, the {@link + * ImmutableIntArray} that is built will very likely occupy more memory than strictly necessary; + * to trim memory usage, build using {@code builder.build().trimmed()}. + */ + public static Builder builder(int initialCapacity) { + checkArgument(initialCapacity >= 0, "Invalid initialCapacity: %s", initialCapacity); + return new Builder(initialCapacity); + } + + /** + * Returns a new, empty builder for {@link ImmutableIntArray} instances, with a default initial + * capacity. The returned builder is not thread-safe. + * + *

Performance note: The {@link ImmutableIntArray} that is built will very likely occupy + * more memory than necessary; to trim memory usage, build using {@code + * builder.build().trimmed()}. + */ + public static Builder builder() { + return new Builder(10); + } + + /** + * A builder for {@link ImmutableIntArray} instances; obtained using {@link + * ImmutableIntArray#builder}. + */ + public static final class Builder { + private int[] array; + private int count = 0; // <= array.length + + Builder(int initialCapacity) { + array = new int[initialCapacity]; + } + + /** + * Appends {@code value} to the end of the values the built {@link ImmutableIntArray} will + * contain. + */ + public Builder add(int value) { + ensureRoomFor(1); + array[count] = value; + count += 1; + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableIntArray} will contain. + */ + public Builder addAll(int[] values) { + ensureRoomFor(values.length); + System.arraycopy(values, 0, array, count, values.length); + count += values.length; + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableIntArray} will contain. + */ + public Builder addAll(Iterable values) { + if (values instanceof Collection) { + return addAll((Collection) values); + } + for (Integer value : values) { + add(value); + } + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableIntArray} will contain. + */ + public Builder addAll(Collection values) { + ensureRoomFor(values.size()); + for (Integer value : values) { + array[count++] = value; + } + return this; + } + + /** + * Appends all values from {@code stream}, in order, to the end of the values the built {@link + * ImmutableIntArray} will contain. + */ + public Builder addAll(IntStream stream) { + Spliterator.OfInt spliterator = stream.spliterator(); + long size = spliterator.getExactSizeIfKnown(); + if (size > 0) { // known *and* nonempty + ensureRoomFor(Ints.saturatedCast(size)); + } + spliterator.forEachRemaining((IntConsumer) this::add); + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableIntArray} will contain. + */ + public Builder addAll(ImmutableIntArray values) { + ensureRoomFor(values.length()); + System.arraycopy(values.array, values.start, array, count, values.length()); + count += values.length(); + return this; + } + + private void ensureRoomFor(int numberToAdd) { + int newCount = count + numberToAdd; // TODO(kevinb): check overflow now? + if (newCount > array.length) { + array = Arrays.copyOf(array, expandedCapacity(array.length, newCount)); + } + } + + // Unfortunately this is pasted from ImmutableCollection.Builder. + private static int expandedCapacity(int oldCapacity, int minCapacity) { + if (minCapacity < 0) { + throw new AssertionError("cannot store more than MAX_VALUE elements"); + } + // careful of overflow! + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; // guaranteed to be >= newCapacity + } + return newCapacity; + } + + /** + * Returns a new immutable array. The builder can continue to be used after this call, to append + * more values and build again. + * + *

Performance note: the returned array is backed by the same array as the builder, so + * no data is copied as part of this step, but this may occupy more memory than strictly + * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}. + */ + public ImmutableIntArray build() { + return count == 0 ? EMPTY : new ImmutableIntArray(array, 0, count); + } + } + + // Instance stuff here + + // The array is never mutated after storing in this field and the construction strategies ensure + // it doesn't escape this class + @SuppressWarnings("Immutable") + private final int[] array; + + /* + * TODO(kevinb): evaluate the trade-offs of going bimorphic to save these two fields from most + * instances. Note that the instances that would get smaller are the right set to care about + * optimizing, because the rest have the option of calling `trimmed`. + */ + + private final transient int start; // it happens that we only serialize instances where this is 0 + private final int end; // exclusive + + private ImmutableIntArray(int[] array) { + this(array, 0, array.length); + } + + private ImmutableIntArray(int[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + /** Returns the number of values in this array. */ + public int length() { + return end - start; + } + + /** Returns {@code true} if there are no values in this array ({@link #length} is zero). */ + public boolean isEmpty() { + return end == start; + } + + /** + * Returns the {@code int} value present at the given index. + * + * @throws IndexOutOfBoundsException if {@code index} is negative, or greater than or equal to + * {@link #length} + */ + public int get(int index) { + Preconditions.checkElementIndex(index, length()); + return array[start + index]; + } + + /** + * Returns the smallest index for which {@link #get} returns {@code target}, or {@code -1} if no + * such index exists. Equivalent to {@code asList().indexOf(target)}. + */ + public int indexOf(int target) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i - start; + } + } + return -1; + } + + /** + * Returns the largest index for which {@link #get} returns {@code target}, or {@code -1} if no + * such index exists. Equivalent to {@code asList().lastIndexOf(target)}. + */ + public int lastIndexOf(int target) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i - start; + } + } + return -1; + } + + /** + * Returns {@code true} if {@code target} is present at any index in this array. Equivalent to + * {@code asList().contains(target)}. + */ + public boolean contains(int target) { + return indexOf(target) >= 0; + } + + /** Invokes {@code consumer} for each value contained in this array, in order. */ + public void forEach(IntConsumer consumer) { + checkNotNull(consumer); + for (int i = start; i < end; i++) { + consumer.accept(array[i]); + } + } + + /** Returns a stream over the values in this array, in order. */ + public IntStream stream() { + return Arrays.stream(array, start, end); + } + + /** Returns a new, mutable copy of this array's values, as a primitive {@code int[]}. */ + public int[] toArray() { + return Arrays.copyOfRange(array, start, end); + } + + /** + * Returns a new immutable array containing the values in the specified range. + * + *

Performance note: The returned array has the same full memory footprint as this one + * does (no actual copying is performed). To reduce memory usage, use {@code subArray(start, + * end).trimmed()}. + */ + public ImmutableIntArray subArray(int startIndex, int endIndex) { + Preconditions.checkPositionIndexes(startIndex, endIndex, length()); + return startIndex == endIndex + ? EMPTY + : new ImmutableIntArray(array, start + startIndex, start + endIndex); + } + + private Spliterator.OfInt spliterator() { + return Spliterators.spliterator(array, start, end, Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + + /** + * Returns an immutable view of this array's values as a {@code List}; note that {@code + * int} values are boxed into {@link Integer} instances on demand, which can be very expensive. + * The returned list should be used once and discarded. For any usages beyond that, pass the + * returned list to {@link org.apache.iotdb.google.common.collect.ImmutableList#copyOf(Collection) + * ImmutableList.copyOf} and use that list instead. + */ + public List asList() { + /* + * Typically we cache this kind of thing, but much repeated use of this view is a performance + * anti-pattern anyway. If we cache, then everyone pays a price in memory footprint even if + * they never use this method. + */ + return new AsList(this); + } + + static class AsList extends AbstractList implements RandomAccess, Serializable { + private final ImmutableIntArray parent; + + private AsList(ImmutableIntArray parent) { + this.parent = parent; + } + + // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, stream, forEach, mutations + + @Override + public int size() { + return parent.length(); + } + + @Override + public Integer get(int index) { + return parent.get(index); + } + + @Override + public boolean contains(Object target) { + return indexOf(target) >= 0; + } + + @Override + public int indexOf(Object target) { + return target instanceof Integer ? parent.indexOf((Integer) target) : -1; + } + + @Override + public int lastIndexOf(Object target) { + return target instanceof Integer ? parent.lastIndexOf((Integer) target) : -1; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return parent.subArray(fromIndex, toIndex).asList(); + } + + // The default List spliterator is not efficiently splittable + @Override + public Spliterator spliterator() { + return parent.spliterator(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof AsList) { + AsList that = (AsList) object; + return this.parent.equals(that.parent); + } + // We could delegate to super now but it would still box too much + if (!(object instanceof List)) { + return false; + } + List that = (List) object; + if (this.size() != that.size()) { + return false; + } + int i = parent.start; + // Since `that` is very likely RandomAccess we could avoid allocating this iterator... + for (Object element : that) { + if (!(element instanceof Integer) || parent.array[i++] != (Integer) element) { + return false; + } + } + return true; + } + + // Because we happen to use the same formula. If that changes, just don't override this. + @Override + public int hashCode() { + return parent.hashCode(); + } + + @Override + public String toString() { + return parent.toString(); + } + } + + /** + * Returns {@code true} if {@code object} is an {@code ImmutableIntArray} containing the same + * values as this one, in the same order. + */ + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof ImmutableIntArray)) { + return false; + } + ImmutableIntArray that = (ImmutableIntArray) object; + if (this.length() != that.length()) { + return false; + } + for (int i = 0; i < length(); i++) { + if (this.get(i) != that.get(i)) { + return false; + } + } + return true; + } + + /** Returns an unspecified hash code for the contents of this immutable array. */ + @Override + public int hashCode() { + int hash = 1; + for (int i = start; i < end; i++) { + hash *= 31; + hash += Ints.hashCode(array[i]); + } + return hash; + } + + /** + * Returns a string representation of this array in the same form as {@link + * Arrays#toString(int[])}, for example {@code "[1, 2, 3]"}. + */ + @Override + public String toString() { + if (isEmpty()) { + return "[]"; + } + StringBuilder builder = new StringBuilder(length() * 5); // rough estimate is fine + builder.append('[').append(array[start]); + + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + builder.append(']'); + return builder.toString(); + } + + /** + * Returns an immutable array containing the same values as {@code this} array. This is logically + * a no-op, and in some circumstances {@code this} itself is returned. However, if this instance + * is a {@link #subArray} view of a larger array, this method will copy only the appropriate range + * of values, resulting in an equivalent array with a smaller memory footprint. + */ + public ImmutableIntArray trimmed() { + return isPartialView() ? new ImmutableIntArray(toArray()) : this; + } + + private boolean isPartialView() { + return start > 0 || end < array.length; + } + + Object writeReplace() { + return trimmed(); + } + + Object readResolve() { + return isEmpty() ? EMPTY : this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableLongArray.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableLongArray.java new file mode 100644 index 0000000000000..1fcc266a2b29c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ImmutableLongArray.java @@ -0,0 +1,617 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.LongConsumer; +import java.util.stream.LongStream; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * An immutable array of {@code long} values, with an API resembling {@link List}. + * + *

Advantages compared to {@code long[]}: + * + *

    + *
  • All the many well-known advantages of immutability (read Effective Java, third + * edition, Item 17). + *
  • Has the value-based (not identity-based) {@link #equals}, {@link #hashCode}, and {@link + * #toString} behavior you expect. + *
  • Offers useful operations beyond just {@code get} and {@code length}, so you don't have to + * hunt through classes like {@link Arrays} and {@link Longs} for them. + *
  • Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to + * add overloads that accept start and end indexes. + *
  • Can be streamed without "breaking the chain": {@code foo.getBarLongs().stream()...}. + *
  • Access to all collection-based utilities via {@link #asList} (though at the cost of + * allocating garbage). + *
+ * + *

Disadvantages compared to {@code long[]}: + * + *

    + *
  • Memory footprint has a fixed overhead (about 24 bytes per instance). + *
  • Some construction use cases force the data to be copied (though several construction + * APIs are offered that don't). + *
  • Can't be passed directly to methods that expect {@code long[]} (though the most common + * utilities do have replacements here). + *
  • Dependency on {@code org.apache.iotdb.google.common} / Guava. + *
+ * + *

Advantages compared to {@link org.apache.iotdb.google.common.collect.ImmutableList + * ImmutableList}{@code }: + * + *

    + *
  • Improved memory compactness and locality. + *
  • Can be queried without allocating garbage. + *
  • Access to {@code LongStream} features (like {@link LongStream#sum}) using {@code stream()} + * instead of the awkward {@code stream().mapToLong(v -> v)}. + *
+ * + *

Disadvantages compared to {@code ImmutableList}: + * + *

    + *
  • Can't be passed directly to methods that expect {@code Iterable}, {@code Collection}, or + * {@code List} (though the most common utilities do have replacements here, and there is a + * lazy {@link #asList} view). + *
+ * + * @since 22.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class ImmutableLongArray implements Serializable { + private static final ImmutableLongArray EMPTY = new ImmutableLongArray(new long[0]); + + /** Returns the empty array. */ + public static ImmutableLongArray of() { + return EMPTY; + } + + /** Returns an immutable array containing a single value. */ + public static ImmutableLongArray of(long e0) { + return new ImmutableLongArray(new long[] {e0}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray of(long e0, long e1) { + return new ImmutableLongArray(new long[] {e0, e1}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray of(long e0, long e1, long e2) { + return new ImmutableLongArray(new long[] {e0, e1, e2}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray of(long e0, long e1, long e2, long e3) { + return new ImmutableLongArray(new long[] {e0, e1, e2, e3}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray of(long e0, long e1, long e2, long e3, long e4) { + return new ImmutableLongArray(new long[] {e0, e1, e2, e3, e4}); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray of(long e0, long e1, long e2, long e3, long e4, long e5) { + return new ImmutableLongArray(new long[] {e0, e1, e2, e3, e4, e5}); + } + + // TODO(kevinb): go up to 11? + + /** + * Returns an immutable array containing the given values, in order. + * + *

The array {@code rest} must not be longer than {@code Integer.MAX_VALUE - 1}. + */ + // Use (first, rest) so that `of(someLongArray)` won't compile (they should use copyOf), which is + // okay since we have to copy the just-created array anyway. + public static ImmutableLongArray of(long first, long... rest) { + checkArgument( + rest.length <= Integer.MAX_VALUE - 1, "the total number of elements must fit in an int"); + long[] array = new long[rest.length + 1]; + array[0] = first; + System.arraycopy(rest, 0, array, 1, rest.length); + return new ImmutableLongArray(array); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray copyOf(long[] values) { + return values.length == 0 + ? EMPTY + : new ImmutableLongArray(Arrays.copyOf(values, values.length)); + } + + /** Returns an immutable array containing the given values, in order. */ + public static ImmutableLongArray copyOf(Collection values) { + return values.isEmpty() ? EMPTY : new ImmutableLongArray(Longs.toArray(values)); + } + + /** + * Returns an immutable array containing the given values, in order. + * + *

Performance note: this method delegates to {@link #copyOf(Collection)} if {@code + * values} is a {@link Collection}. Otherwise it creates a {@link #builder} and uses {@link + * Builder#addAll(Iterable)}, with all the performance implications associated with that. + */ + public static ImmutableLongArray copyOf(Iterable values) { + if (values instanceof Collection) { + return copyOf((Collection) values); + } + return builder().addAll(values).build(); + } + + /** Returns an immutable array containing all the values from {@code stream}, in order. */ + public static ImmutableLongArray copyOf(LongStream stream) { + // Note this uses very different growth behavior from copyOf(Iterable) and the builder. + long[] array = stream.toArray(); + return (array.length == 0) ? EMPTY : new ImmutableLongArray(array); + } + + /** + * Returns a new, empty builder for {@link ImmutableLongArray} instances, sized to hold up to + * {@code initialCapacity} values without resizing. The returned builder is not thread-safe. + * + *

Performance note: When feasible, {@code initialCapacity} should be the exact number + * of values that will be added, if that knowledge is readily available. It is better to guess a + * value slightly too high than slightly too low. If the value is not exact, the {@link + * ImmutableLongArray} that is built will very likely occupy more memory than strictly necessary; + * to trim memory usage, build using {@code builder.build().trimmed()}. + */ + public static Builder builder(int initialCapacity) { + checkArgument(initialCapacity >= 0, "Invalid initialCapacity: %s", initialCapacity); + return new Builder(initialCapacity); + } + + /** + * Returns a new, empty builder for {@link ImmutableLongArray} instances, with a default initial + * capacity. The returned builder is not thread-safe. + * + *

Performance note: The {@link ImmutableLongArray} that is built will very likely + * occupy more memory than necessary; to trim memory usage, build using {@code + * builder.build().trimmed()}. + */ + public static Builder builder() { + return new Builder(10); + } + + /** + * A builder for {@link ImmutableLongArray} instances; obtained using {@link + * ImmutableLongArray#builder}. + */ + public static final class Builder { + private long[] array; + private int count = 0; // <= array.length + + Builder(int initialCapacity) { + array = new long[initialCapacity]; + } + + /** + * Appends {@code value} to the end of the values the built {@link ImmutableLongArray} will + * contain. + */ + public Builder add(long value) { + ensureRoomFor(1); + array[count] = value; + count += 1; + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableLongArray} will contain. + */ + public Builder addAll(long[] values) { + ensureRoomFor(values.length); + System.arraycopy(values, 0, array, count, values.length); + count += values.length; + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableLongArray} will contain. + */ + public Builder addAll(Iterable values) { + if (values instanceof Collection) { + return addAll((Collection) values); + } + for (Long value : values) { + add(value); + } + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableLongArray} will contain. + */ + public Builder addAll(Collection values) { + ensureRoomFor(values.size()); + for (Long value : values) { + array[count++] = value; + } + return this; + } + + /** + * Appends all values from {@code stream}, in order, to the end of the values the built {@link + * ImmutableLongArray} will contain. + */ + public Builder addAll(LongStream stream) { + Spliterator.OfLong spliterator = stream.spliterator(); + long size = spliterator.getExactSizeIfKnown(); + if (size > 0) { // known *and* nonempty + ensureRoomFor(Ints.saturatedCast(size)); + } + spliterator.forEachRemaining((LongConsumer) this::add); + return this; + } + + /** + * Appends {@code values}, in order, to the end of the values the built {@link + * ImmutableLongArray} will contain. + */ + public Builder addAll(ImmutableLongArray values) { + ensureRoomFor(values.length()); + System.arraycopy(values.array, values.start, array, count, values.length()); + count += values.length(); + return this; + } + + private void ensureRoomFor(int numberToAdd) { + int newCount = count + numberToAdd; // TODO(kevinb): check overflow now? + if (newCount > array.length) { + array = Arrays.copyOf(array, expandedCapacity(array.length, newCount)); + } + } + + // Unfortunately this is pasted from ImmutableCollection.Builder. + private static int expandedCapacity(int oldCapacity, int minCapacity) { + if (minCapacity < 0) { + throw new AssertionError("cannot store more than MAX_VALUE elements"); + } + // careful of overflow! + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; // guaranteed to be >= newCapacity + } + return newCapacity; + } + + /** + * Returns a new immutable array. The builder can continue to be used after this call, to append + * more values and build again. + * + *

Performance note: the returned array is backed by the same array as the builder, so + * no data is copied as part of this step, but this may occupy more memory than strictly + * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}. + */ + public ImmutableLongArray build() { + return count == 0 ? EMPTY : new ImmutableLongArray(array, 0, count); + } + } + + // Instance stuff here + + // The array is never mutated after storing in this field and the construction strategies ensure + // it doesn't escape this class + @SuppressWarnings("Immutable") + private final long[] array; + + /* + * TODO(kevinb): evaluate the trade-offs of going bimorphic to save these two fields from most + * instances. Note that the instances that would get smaller are the right set to care about + * optimizing, because the rest have the option of calling `trimmed`. + */ + + private final transient int start; // it happens that we only serialize instances where this is 0 + private final int end; // exclusive + + private ImmutableLongArray(long[] array) { + this(array, 0, array.length); + } + + private ImmutableLongArray(long[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + /** Returns the number of values in this array. */ + public int length() { + return end - start; + } + + /** Returns {@code true} if there are no values in this array ({@link #length} is zero). */ + public boolean isEmpty() { + return end == start; + } + + /** + * Returns the {@code long} value present at the given index. + * + * @throws IndexOutOfBoundsException if {@code index} is negative, or greater than or equal to + * {@link #length} + */ + public long get(int index) { + Preconditions.checkElementIndex(index, length()); + return array[start + index]; + } + + /** + * Returns the smallest index for which {@link #get} returns {@code target}, or {@code -1} if no + * such index exists. Equivalent to {@code asList().indexOf(target)}. + */ + public int indexOf(long target) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i - start; + } + } + return -1; + } + + /** + * Returns the largest index for which {@link #get} returns {@code target}, or {@code -1} if no + * such index exists. Equivalent to {@code asList().lastIndexOf(target)}. + */ + public int lastIndexOf(long target) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i - start; + } + } + return -1; + } + + /** + * Returns {@code true} if {@code target} is present at any index in this array. Equivalent to + * {@code asList().contains(target)}. + */ + public boolean contains(long target) { + return indexOf(target) >= 0; + } + + /** Invokes {@code consumer} for each value contained in this array, in order. */ + public void forEach(LongConsumer consumer) { + checkNotNull(consumer); + for (int i = start; i < end; i++) { + consumer.accept(array[i]); + } + } + + /** Returns a stream over the values in this array, in order. */ + public LongStream stream() { + return Arrays.stream(array, start, end); + } + + /** Returns a new, mutable copy of this array's values, as a primitive {@code long[]}. */ + public long[] toArray() { + return Arrays.copyOfRange(array, start, end); + } + + /** + * Returns a new immutable array containing the values in the specified range. + * + *

Performance note: The returned array has the same full memory footprint as this one + * does (no actual copying is performed). To reduce memory usage, use {@code subArray(start, + * end).trimmed()}. + */ + public ImmutableLongArray subArray(int startIndex, int endIndex) { + Preconditions.checkPositionIndexes(startIndex, endIndex, length()); + return startIndex == endIndex + ? EMPTY + : new ImmutableLongArray(array, start + startIndex, start + endIndex); + } + + private Spliterator.OfLong spliterator() { + return Spliterators.spliterator(array, start, end, Spliterator.IMMUTABLE | Spliterator.ORDERED); + } + + /** + * Returns an immutable view of this array's values as a {@code List}; note that {@code + * long} values are boxed into {@link Long} instances on demand, which can be very expensive. The + * returned list should be used once and discarded. For any usages beyond that, pass the returned + * list to {@link org.apache.iotdb.google.common.collect.ImmutableList#copyOf(Collection) + * ImmutableList.copyOf} and use that list instead. + */ + public List asList() { + /* + * Typically we cache this kind of thing, but much repeated use of this view is a performance + * anti-pattern anyway. If we cache, then everyone pays a price in memory footprint even if + * they never use this method. + */ + return new AsList(this); + } + + static class AsList extends AbstractList implements RandomAccess, Serializable { + private final ImmutableLongArray parent; + + private AsList(ImmutableLongArray parent) { + this.parent = parent; + } + + // inherit: isEmpty, containsAll, toArray x2, iterator, listIterator, stream, forEach, mutations + + @Override + public int size() { + return parent.length(); + } + + @Override + public Long get(int index) { + return parent.get(index); + } + + @Override + public boolean contains(Object target) { + return indexOf(target) >= 0; + } + + @Override + public int indexOf(Object target) { + return target instanceof Long ? parent.indexOf((Long) target) : -1; + } + + @Override + public int lastIndexOf(Object target) { + return target instanceof Long ? parent.lastIndexOf((Long) target) : -1; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return parent.subArray(fromIndex, toIndex).asList(); + } + + // The default List spliterator is not efficiently splittable + @Override + public Spliterator spliterator() { + return parent.spliterator(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof AsList) { + AsList that = (AsList) object; + return this.parent.equals(that.parent); + } + // We could delegate to super now but it would still box too much + if (!(object instanceof List)) { + return false; + } + List that = (List) object; + if (this.size() != that.size()) { + return false; + } + int i = parent.start; + // Since `that` is very likely RandomAccess we could avoid allocating this iterator... + for (Object element : that) { + if (!(element instanceof Long) || parent.array[i++] != (Long) element) { + return false; + } + } + return true; + } + + // Because we happen to use the same formula. If that changes, just don't override this. + @Override + public int hashCode() { + return parent.hashCode(); + } + + @Override + public String toString() { + return parent.toString(); + } + } + + /** + * Returns {@code true} if {@code object} is an {@code ImmutableLongArray} containing the same + * values as this one, in the same order. + */ + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof ImmutableLongArray)) { + return false; + } + ImmutableLongArray that = (ImmutableLongArray) object; + if (this.length() != that.length()) { + return false; + } + for (int i = 0; i < length(); i++) { + if (this.get(i) != that.get(i)) { + return false; + } + } + return true; + } + + /** Returns an unspecified hash code for the contents of this immutable array. */ + @Override + public int hashCode() { + int hash = 1; + for (int i = start; i < end; i++) { + hash *= 31; + hash += Longs.hashCode(array[i]); + } + return hash; + } + + /** + * Returns a string representation of this array in the same form as {@link + * Arrays#toString(long[])}, for example {@code "[1, 2, 3]"}. + */ + @Override + public String toString() { + if (isEmpty()) { + return "[]"; + } + StringBuilder builder = new StringBuilder(length() * 5); // rough estimate is fine + builder.append('[').append(array[start]); + + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + builder.append(']'); + return builder.toString(); + } + + /** + * Returns an immutable array containing the same values as {@code this} array. This is logically + * a no-op, and in some circumstances {@code this} itself is returned. However, if this instance + * is a {@link #subArray} view of a larger array, this method will copy only the appropriate range + * of values, resulting in an equivalent array with a smaller memory footprint. + */ + public ImmutableLongArray trimmed() { + return isPartialView() ? new ImmutableLongArray(toArray()) : this; + } + + private boolean isPartialView() { + return start > 0 || end < array.length; + } + + Object writeReplace() { + return trimmed(); + } + + Object readResolve() { + return isEmpty() ? EMPTY : this; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Ints.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Ints.java new file mode 100644 index 0000000000000..640bd633137db --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Ints.java @@ -0,0 +1,828 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Converter; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.Spliterators; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code int} primitives, that are not already found in either + * {@link Integer} or {@link Arrays}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Ints extends IntsMethodsForWeb { + private Ints() {} + + /** + * The number of bytes required to represent a primitive {@code int} value. + * + *

Java 8 users: use {@link Integer#BYTES} instead. + */ + public static final int BYTES = Integer.SIZE / Byte.SIZE; + + /** + * The largest power of two that can be represented as an {@code int}. + * + * @since 10.0 + */ + public static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Integer) + * value).hashCode()}. + * + *

Java 8 users: use {@link Integer#hashCode(int)} instead. + * + * @param value a primitive {@code int} value + * @return a hash code for the value + */ + public static int hashCode(int value) { + return value; + } + + /** + * Returns the {@code int} value that is equal to {@code value}, if possible. + * + * @param value any value in the range of the {@code int} type + * @return the {@code int} value that equals {@code value} + * @throws IllegalArgumentException if {@code value} is greater than {@link Integer#MAX_VALUE} or + * less than {@link Integer#MIN_VALUE} + */ + public static int checkedCast(long value) { + int result = (int) value; + checkArgument(result == value, "Out of range: %s", value); + return result; + } + + /** + * Returns the {@code int} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code int} if it is in the range of the {@code int} type, + * {@link Integer#MAX_VALUE} if it is too large, or {@link Integer#MIN_VALUE} if it is too + * small + */ + public static int saturatedCast(long value) { + if (value > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + if (value < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + return (int) value; + } + + /** + * Compares the two specified {@code int} values. The sign of the value returned is the same as + * that of {@code ((Integer) a).compareTo(b)}. + * + *

Note for Java 7 and later: this method should be treated as deprecated; use the + * equivalent {@link Integer#compare} method instead. + * + * @param a the first {@code int} to compare + * @param b the second {@code int} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(int a, int b) { + return (a < b) ? -1 : ((a > b) ? 1 : 0); + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. + * + * @param array an array of {@code int} values, possibly empty + * @param target a primitive {@code int} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(int[] array, int target) { + for (int value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code int} values, possibly empty + * @param target a primitive {@code int} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(int[] array, int target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(int[] array, int target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(int[] array, int[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code int} values, possibly empty + * @param target a primitive {@code int} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(int[] array, int target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(int[] array, int target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the least value present in {@code array}. + * + * @param array a nonempty array of {@code int} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static int min(int... array) { + checkArgument(array.length > 0); + int min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } + + /** + * Returns the greatest value present in {@code array}. + * + * @param array a nonempty array of {@code int} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static int max(int... array) { + checkArgument(array.length > 0); + int max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } + + /** + * Returns the value nearest to {@code value} which is within the closed range {@code [min..max]}. + * + *

If {@code value} is within the range {@code [min..max]}, {@code value} is returned + * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code + * value} is greater than {@code max}, {@code max} is returned. + * + * @param value the {@code int} value to constrain + * @param min the lower bound (inclusive) of the range to constrain {@code value} to + * @param max the upper bound (inclusive) of the range to constrain {@code value} to + * @throws IllegalArgumentException if {@code min > max} + * @since 21.0 + */ + public static int constrainToRange(int value, int min, int max) { + checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); + return Math.min(Math.max(value, min), max); + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new int[] {a, b}, new int[] {}, new int[] {c}} returns the array {@code {a, b, c}}. + * + * @param arrays zero or more {@code int} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static int[] concat(int[]... arrays) { + int length = 0; + for (int[] array : arrays) { + length += array.length; + } + int[] result = new int[length]; + int pos = 0; + for (int[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + /** + * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to + * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code + * 0x12131415} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15}}. + * + *

If you need to convert and concatenate several values (possibly even of different types), + * use a shared {@link java.nio.ByteBuffer} instance, or use {@link + * org.apache.iotdb.google.common.io.ByteStreams#newDataOutput()} to get a growable buffer. + */ + public static byte[] toByteArray(int value) { + return new byte[] { + (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value + }; + } + + /** + * Returns the {@code int} value whose big-endian representation is stored in the first 4 bytes of + * {@code bytes}; equivalent to {@code ByteBuffer.wrap(bytes).getInt()}. For example, the input + * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x33}} would yield the {@code int} value {@code + * 0x12131415}. + * + *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that library exposes much more + * flexibility at little cost in readability. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than 4 elements + */ + public static int fromByteArray(byte[] bytes) { + checkArgument(bytes.length >= BYTES, "array too small: %s < %s", bytes.length, BYTES); + return fromBytes(bytes[0], bytes[1], bytes[2], bytes[3]); + } + + /** + * Returns the {@code int} value whose byte representation is the given 4 bytes, in big-endian + * order; equivalent to {@code Ints.fromByteArray(new byte[] {b1, b2, b3, b4})}. + * + * @since 7.0 + */ + public static int fromBytes(byte b1, byte b2, byte b3, byte b4) { + return b1 << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF); + } + + private static final class IntConverter extends Converter + implements Serializable { + static final IntConverter INSTANCE = new IntConverter(); + + @Override + protected Integer doForward(String value) { + return Integer.decode(value); + } + + @Override + protected String doBackward(Integer value) { + return value.toString(); + } + + @Override + public String toString() { + return "Ints.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + /** + * Returns a serializable converter object that converts between strings and integers using {@link + * Integer#decode} and {@link Integer#toString()}. The returned converter throws {@link + * NumberFormatException} if the input string is invalid. + * + *

Warning: please see {@link Integer#decode} to understand exactly how strings are + * parsed. For example, the string {@code "0123"} is treated as octal and converted to the + * value {@code 83}. + * + * @since 16.0 + */ + public static Converter stringConverter() { + return IntConverter.INSTANCE; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static int[] ensureCapacity(int[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code int} values separated by {@code separator}. For + * example, {@code join("-", 1, 2, 3)} returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code int} values, possibly empty + */ + public static String join(String separator, int... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code int} arrays lexicographically. That is, it + * compares, using {@link #compare(int, int)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [1] < [1, 2] < [2]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(int[], int[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(int[] left, int[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Ints.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Ints.lexicographicalComparator()"; + } + } + + /** + * Sorts the elements of {@code array} in descending order. + * + * @since 23.1 + */ + public static void sortDescending(int[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + * @since 23.1 + */ + public static void sortDescending(int[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Ints.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(int[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Ints.asList(array).subList(fromIndex, toIndex))}, but is likely to be more + * efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(int[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + int tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Ints.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(int[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Ints.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(int[] array, int distance, int fromIndex, int toIndex) { + // There are several well-known algorithms for rotating part of an array (or, equivalently, + // exchanging two blocks of memory). This classic text by Gries and Mills mentions several: + // https://ecommons.cornell.edu/bitstream/handle/1813/6292/81-452.pdf. + // (1) "Reversal", the one we have here. + // (2) "Dolphin". If we're rotating an array a of size n by a distance of d, then element a[0] + // ends up at a[d], which in turn ends up at a[2d], and so on until we get back to a[0]. + // (All indices taken mod n.) If d and n are mutually prime, all elements will have been + // moved at that point. Otherwise, we can rotate the cycle a[1], a[1 + d], a[1 + 2d], etc, + // then a[2] etc, and so on until we have rotated all elements. There are gcd(d, n) cycles + // in all. + // (3) "Successive". We can consider that we are exchanging a block of size d (a[0..d-1]) with a + // block of size n-d (a[d..n-1]), where in general these blocks have different sizes. If we + // imagine a line separating the first block from the second, we can proceed by exchanging + // the smaller of these blocks with the far end of the other one. That leaves us with a + // smaller version of the same problem. + // Say we are rotating abcdefgh by 5. We start with abcde|fgh. The smaller block is [fgh]: + // [abc]de|[fgh] -> [fgh]de|[abc]. Now [fgh] is in the right place, but we need to swap [de] + // with [abc]: fgh[de]|a[bc] -> fgh[bc]|a[de]. Now we need to swap [a] with [bc]: + // fgh[b]c|[a]de -> fgh[a]c|[b]de. Finally we need to swap [c] with [b]: + // fgha[c]|[b]de -> fgha[b]|[c]de. Because these two blocks are the same size, we are done. + // The Dolphin algorithm is attractive because it does the fewest array reads and writes: each + // array slot is read and written exactly once. However, it can have very poor memory locality: + // benchmarking shows it can take 7 times longer than the other two in some cases. The other two + // do n swaps, minus a delta (0 or 2 for Reversal, gcd(d, n) for Successive), so that's about + // twice as many reads and writes. But benchmarking shows that they usually perform better than + // Dolphin. Reversal is about as good as Successive on average, and it is much simpler, + // especially since we already have a `reverse` method. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Returns an array containing each value of {@code collection}, converted to a {@code int} value + * in the manner of {@link Number#intValue}. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Number} instances + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + * @since 1.0 (parameter was {@code Collection} before 12.0) + */ + public static int[] toArray(Collection collection) { + if (collection instanceof IntArrayAsList) { + return ((IntArrayAsList) collection).toIntArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + int[] array = new int[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = ((Number) checkNotNull(boxedArray[i])).intValue(); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Integer} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list is serializable. + * + *

Note: when possible, you should represent your data as an {@link ImmutableIntArray} + * instead, which has an {@link ImmutableIntArray#asList asList} view. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(int... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new IntArrayAsList(backingArray); + } + + @GwtCompatible + private static class IntArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final int[] array; + final int start; + final int end; + + IntArrayAsList(int[] array) { + this(array, 0, array.length); + } + + IntArrayAsList(int[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Integer get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public Spliterator.OfInt spliterator() { + return Spliterators.spliterator(array, start, end, 0); + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Integer) && Ints.indexOf(array, (Integer) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Integer) { + int i = Ints.indexOf(array, (Integer) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Integer) { + int i = Ints.lastIndexOf(array, (Integer) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Integer set(int index, Integer element) { + checkElementIndex(index, size()); + int oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new IntArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof IntArrayAsList) { + IntArrayAsList that = (IntArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Ints.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 5); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + int[] toIntArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } + + /** + * Parses the specified string as a signed decimal integer value. The ASCII character {@code '-'} + * ('\u002D') is recognized as the minus sign. + * + *

Unlike {@link Integer#parseInt(String)}, this method returns {@code null} instead of + * throwing an exception if parsing fails. Additionally, this method only accepts ASCII digits, + * and returns {@code null} if non-ASCII digits are present in the string. + * + *

Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite + * the change to {@link Integer#parseInt(String)} for that version. + * + * @param string the string representation of an integer value + * @return the integer value represented by {@code string}, or {@code null} if {@code string} has + * a length of zero or cannot be parsed as an integer value + * @throws NullPointerException if {@code string} is {@code null} + * @since 11.0 + */ + public static Integer tryParse(String string) { + return tryParse(string, 10); + } + + /** + * Parses the specified string as a signed integer value using the specified radix. The ASCII + * character {@code '-'} ('\u002D') is recognized as the minus sign. + * + *

Unlike {@link Integer#parseInt(String, int)}, this method returns {@code null} instead of + * throwing an exception if parsing fails. Additionally, this method only accepts ASCII digits, + * and returns {@code null} if non-ASCII digits are present in the string. + * + *

Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite + * the change to {@link Integer#parseInt(String, int)} for that version. + * + * @param string the string representation of an integer value + * @param radix the radix to use when parsing + * @return the integer value represented by {@code string} using {@code radix}, or {@code null} if + * {@code string} has a length of zero or cannot be parsed as an integer value + * @throws IllegalArgumentException if {@code radix < Character.MIN_RADIX} or {@code radix > + * Character.MAX_RADIX} + * @throws NullPointerException if {@code string} is {@code null} + * @since 19.0 + */ + public static Integer tryParse(String string, int radix) { + Long result = Longs.tryParse(string, radix); + if (result == null || result.longValue() != result.intValue()) { + return null; + } else { + return result.intValue(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/IntsMethodsForWeb.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/IntsMethodsForWeb.java new file mode 100644 index 0000000000000..5bf306973cb28 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/IntsMethodsForWeb.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Holder for web specializations of methods of {@code Ints}. Intended to be empty for regular + * version. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class IntsMethodsForWeb {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Longs.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Longs.java new file mode 100644 index 0000000000000..cf884258e3a7a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Longs.java @@ -0,0 +1,854 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Converter; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.Spliterators; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code long} primitives, that are not already found in + * either {@link Long} or {@link Arrays}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Longs { + private Longs() {} + + /** + * The number of bytes required to represent a primitive {@code long} value. + * + *

Java 8 users: use {@link Long#BYTES} instead. + */ + public static final int BYTES = Long.SIZE / Byte.SIZE; + + /** + * The largest power of two that can be represented as a {@code long}. + * + * @since 10.0 + */ + public static final long MAX_POWER_OF_TWO = 1L << (Long.SIZE - 2); + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Long) + * value).hashCode()}. + * + *

This method always return the value specified by {@link Long#hashCode()} in java, which + * might be different from {@code ((Long) value).hashCode()} in GWT because {@link + * Long#hashCode()} in GWT does not obey the JRE contract. + * + *

Java 8 users: use {@link Long#hashCode(long)} instead. + * + * @param value a primitive {@code long} value + * @return a hash code for the value + */ + public static int hashCode(long value) { + return (int) (value ^ (value >>> 32)); + } + + /** + * Compares the two specified {@code long} values. The sign of the value returned is the same as + * that of {@code ((Long) a).compareTo(b)}. + * + *

Note for Java 7 and later: this method should be treated as deprecated; use the + * equivalent {@link Long#compare} method instead. + * + * @param a the first {@code long} to compare + * @param b the second {@code long} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(long a, long b) { + return (a < b) ? -1 : ((a > b) ? 1 : 0); + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. + * + * @param array an array of {@code long} values, possibly empty + * @param target a primitive {@code long} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(long[] array, long target) { + for (long value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code long} values, possibly empty + * @param target a primitive {@code long} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(long[] array, long target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(long[] array, long target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(long[] array, long[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code long} values, possibly empty + * @param target a primitive {@code long} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(long[] array, long target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(long[] array, long target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the least value present in {@code array}. + * + * @param array a nonempty array of {@code long} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + public static long min(long... array) { + checkArgument(array.length > 0); + long min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } + + /** + * Returns the greatest value present in {@code array}. + * + * @param array a nonempty array of {@code long} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + public static long max(long... array) { + checkArgument(array.length > 0); + long max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } + + /** + * Returns the value nearest to {@code value} which is within the closed range {@code [min..max]}. + * + *

If {@code value} is within the range {@code [min..max]}, {@code value} is returned + * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code + * value} is greater than {@code max}, {@code max} is returned. + * + * @param value the {@code long} value to constrain + * @param min the lower bound (inclusive) of the range to constrain {@code value} to + * @param max the upper bound (inclusive) of the range to constrain {@code value} to + * @throws IllegalArgumentException if {@code min > max} + * @since 21.0 + */ + public static long constrainToRange(long value, long min, long max) { + checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); + return Math.min(Math.max(value, min), max); + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new long[] {a, b}, new long[] {}, new long[] {c}} returns the array {@code {a, b, c}}. + * + * @param arrays zero or more {@code long} arrays + * @return a single array containing all the values from the source arrays, in order + * @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit + * in an {@code int} + */ + public static long[] concat(long[]... arrays) { + long length = 0; + for (long[] array : arrays) { + length += array.length; + } + long[] result = new long[checkNoOverflow(length)]; + int pos = 0; + for (long[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + private static int checkNoOverflow(long result) { + checkArgument( + result == (int) result, + "the total number of elements (%s) in the arrays must fit in an int", + result); + return (int) result; + } + + /** + * Returns a big-endian representation of {@code value} in an 8-element byte array; equivalent to + * {@code ByteBuffer.allocate(8).putLong(value).array()}. For example, the input value {@code + * 0x1213141516171819L} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + * 0x18, 0x19}}. + * + *

If you need to convert and concatenate several values (possibly even of different types), + * use a shared {@link java.nio.ByteBuffer} instance, or use {@link + * org.apache.iotdb.google.common.io.ByteStreams#newDataOutput()} to get a growable buffer. + */ + public static byte[] toByteArray(long value) { + // Note that this code needs to stay compatible with GWT, which has known + // bugs when narrowing byte casts of long values occur. + byte[] result = new byte[8]; + for (int i = 7; i >= 0; i--) { + result[i] = (byte) (value & 0xffL); + value >>= 8; + } + return result; + } + + /** + * Returns the {@code long} value whose big-endian representation is stored in the first 8 bytes + * of {@code bytes}; equivalent to {@code ByteBuffer.wrap(bytes).getLong()}. For example, the + * input byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}} would yield the + * {@code long} value {@code 0x1213141516171819L}. + * + *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that library exposes much more + * flexibility at little cost in readability. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than 8 elements + */ + public static long fromByteArray(byte[] bytes) { + checkArgument(bytes.length >= BYTES, "array too small: %s < %s", bytes.length, BYTES); + return fromBytes( + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]); + } + + /** + * Returns the {@code long} value whose byte representation is the given 8 bytes, in big-endian + * order; equivalent to {@code Longs.fromByteArray(new byte[] {b1, b2, b3, b4, b5, b6, b7, b8})}. + * + * @since 7.0 + */ + public static long fromBytes( + byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8) { + return (b1 & 0xFFL) << 56 + | (b2 & 0xFFL) << 48 + | (b3 & 0xFFL) << 40 + | (b4 & 0xFFL) << 32 + | (b5 & 0xFFL) << 24 + | (b6 & 0xFFL) << 16 + | (b7 & 0xFFL) << 8 + | (b8 & 0xFFL); + } + + /* + * Moving asciiDigits into this static holder class lets ProGuard eliminate and inline the Longs + * class. + */ + static final class AsciiDigits { + private AsciiDigits() {} + + private static final byte[] asciiDigits; + + static { + byte[] result = new byte[128]; + Arrays.fill(result, (byte) -1); + for (int i = 0; i < 10; i++) { + result['0' + i] = (byte) i; + } + for (int i = 0; i < 26; i++) { + result['A' + i] = (byte) (10 + i); + result['a' + i] = (byte) (10 + i); + } + asciiDigits = result; + } + + static int digit(char c) { + return (c < 128) ? asciiDigits[c] : -1; + } + } + + /** + * Parses the specified string as a signed decimal long value. The ASCII character {@code '-'} ( + * '\u002D') is recognized as the minus sign. + * + *

Unlike {@link Long#parseLong(String)}, this method returns {@code null} instead of throwing + * an exception if parsing fails. Additionally, this method only accepts ASCII digits, and returns + * {@code null} if non-ASCII digits are present in the string. + * + *

Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite + * the change to {@link Long#parseLong(String)} for that version. + * + * @param string the string representation of a long value + * @return the long value represented by {@code string}, or {@code null} if {@code string} has a + * length of zero or cannot be parsed as a long value + * @throws NullPointerException if {@code string} is {@code null} + * @since 14.0 + */ + public static Long tryParse(String string) { + return tryParse(string, 10); + } + + /** + * Parses the specified string as a signed long value using the specified radix. The ASCII + * character {@code '-'} ('\u002D') is recognized as the minus sign. + * + *

Unlike {@link Long#parseLong(String, int)}, this method returns {@code null} instead of + * throwing an exception if parsing fails. Additionally, this method only accepts ASCII digits, + * and returns {@code null} if non-ASCII digits are present in the string. + * + *

Note that strings prefixed with ASCII {@code '+'} are rejected, even under JDK 7, despite + * the change to {@link Long#parseLong(String, int)} for that version. + * + * @param string the string representation of a long value + * @param radix the radix to use when parsing + * @return the long value represented by {@code string} using {@code radix}, or {@code null} if + * {@code string} has a length of zero or cannot be parsed as a long value + * @throws IllegalArgumentException if {@code radix < Character.MIN_RADIX} or {@code radix > + * Character.MAX_RADIX} + * @throws NullPointerException if {@code string} is {@code null} + * @since 19.0 + */ + public static Long tryParse(String string, int radix) { + if (checkNotNull(string).isEmpty()) { + return null; + } + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new IllegalArgumentException( + "radix must be between MIN_RADIX and MAX_RADIX but was " + radix); + } + boolean negative = string.charAt(0) == '-'; + int index = negative ? 1 : 0; + if (index == string.length()) { + return null; + } + int digit = AsciiDigits.digit(string.charAt(index++)); + if (digit < 0 || digit >= radix) { + return null; + } + long accum = -digit; + + long cap = Long.MIN_VALUE / radix; + + while (index < string.length()) { + digit = AsciiDigits.digit(string.charAt(index++)); + if (digit < 0 || digit >= radix || accum < cap) { + return null; + } + accum *= radix; + if (accum < Long.MIN_VALUE + digit) { + return null; + } + accum -= digit; + } + + if (negative) { + return accum; + } else if (accum == Long.MIN_VALUE) { + return null; + } else { + return -accum; + } + } + + private static final class LongConverter extends Converter implements Serializable { + static final LongConverter INSTANCE = new LongConverter(); + + @Override + protected Long doForward(String value) { + return Long.decode(value); + } + + @Override + protected String doBackward(Long value) { + return value.toString(); + } + + @Override + public String toString() { + return "Longs.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + /** + * Returns a serializable converter object that converts between strings and longs using {@link + * Long#decode} and {@link Long#toString()}. The returned converter throws {@link + * NumberFormatException} if the input string is invalid. + * + *

Warning: please see {@link Long#decode} to understand exactly how strings are parsed. + * For example, the string {@code "0123"} is treated as octal and converted to the value + * {@code 83L}. + * + * @since 16.0 + */ + public static Converter stringConverter() { + return LongConverter.INSTANCE; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static long[] ensureCapacity(long[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code long} values separated by {@code separator}. + * For example, {@code join("-", 1L, 2L, 3L)} returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code long} values, possibly empty + */ + public static String join(String separator, long... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 10); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code long} arrays lexicographically. That is, it + * compares, using {@link #compare(long, long)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [1L] < [1L, 2L] < [2L]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(long[], + * long[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(long[] left, long[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Longs.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Longs.lexicographicalComparator()"; + } + } + + /** + * Sorts the elements of {@code array} in descending order. + * + * @since 23.1 + */ + public static void sortDescending(long[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + * @since 23.1 + */ + public static void sortDescending(long[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Longs.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(long[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Longs.asList(array).subList(fromIndex, toIndex))}, but is likely to be more + * efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(long[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + long tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Longs.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(long[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Longs.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(long[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Returns an array containing each value of {@code collection}, converted to a {@code long} value + * in the manner of {@link Number#longValue}. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Number} instances + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + * @since 1.0 (parameter was {@code Collection} before 12.0) + */ + public static long[] toArray(Collection collection) { + if (collection instanceof LongArrayAsList) { + return ((LongArrayAsList) collection).toLongArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + long[] array = new long[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = ((Number) checkNotNull(boxedArray[i])).longValue(); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Long} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list is serializable. + * + *

Note: when possible, you should represent your data as an {@link ImmutableLongArray} + * instead, which has an {@link ImmutableLongArray#asList asList} view. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(long... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new LongArrayAsList(backingArray); + } + + @GwtCompatible + private static class LongArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final long[] array; + final int start; + final int end; + + LongArrayAsList(long[] array) { + this(array, 0, array.length); + } + + LongArrayAsList(long[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Long get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public Spliterator.OfLong spliterator() { + return Spliterators.spliterator(array, start, end, 0); + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Long) && Longs.indexOf(array, (Long) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Long) { + int i = Longs.indexOf(array, (Long) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Long) { + int i = Longs.lastIndexOf(array, (Long) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Long set(int index, Long element) { + checkElementIndex(index, size()); + long oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new LongArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof LongArrayAsList) { + LongArrayAsList that = (LongArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Longs.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 10); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + long[] toLongArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ParametricNullness.java new file mode 100644 index 0000000000000..3dcc9e7d472a1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ParseRequest.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ParseRequest.java new file mode 100644 index 0000000000000..529cc04e1d0ef --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ParseRequest.java @@ -0,0 +1,61 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** A string to be parsed as a number and the radix to interpret it in. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class ParseRequest { + final String rawValue; + final int radix; + + private ParseRequest(String rawValue, int radix) { + this.rawValue = rawValue; + this.radix = radix; + } + + static ParseRequest fromString(String stringValue) { + if (stringValue.length() == 0) { + throw new NumberFormatException("empty string"); + } + + // Handle radix specifier if present + String rawValue; + int radix; + char firstChar = stringValue.charAt(0); + if (stringValue.startsWith("0x") || stringValue.startsWith("0X")) { + rawValue = stringValue.substring(2); + radix = 16; + } else if (firstChar == '#') { + rawValue = stringValue.substring(1); + radix = 16; + } else if (firstChar == '0' && stringValue.length() > 1) { + rawValue = stringValue.substring(1); + radix = 8; + } else { + rawValue = stringValue; + radix = 10; + } + + return new ParseRequest(rawValue, radix); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Primitives.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Primitives.java new file mode 100644 index 0000000000000..a11e7a4b2ec36 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Primitives.java @@ -0,0 +1,145 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Contains static utility methods pertaining to primitive types and their corresponding wrapper + * types. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Primitives { + private Primitives() {} + + /** A map from primitive types to their corresponding wrapper types. */ + private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; + + /** A map from wrapper types to their corresponding primitive types. */ + private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; + + // Sad that we can't use a BiMap. :( + + static { + Map, Class> primToWrap = new LinkedHashMap<>(16); + Map, Class> wrapToPrim = new LinkedHashMap<>(16); + + add(primToWrap, wrapToPrim, boolean.class, Boolean.class); + add(primToWrap, wrapToPrim, byte.class, Byte.class); + add(primToWrap, wrapToPrim, char.class, Character.class); + add(primToWrap, wrapToPrim, double.class, Double.class); + add(primToWrap, wrapToPrim, float.class, Float.class); + add(primToWrap, wrapToPrim, int.class, Integer.class); + add(primToWrap, wrapToPrim, long.class, Long.class); + add(primToWrap, wrapToPrim, short.class, Short.class); + add(primToWrap, wrapToPrim, void.class, Void.class); + + PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); + WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); + } + + private static void add( + Map, Class> forward, + Map, Class> backward, + Class key, + Class value) { + forward.put(key, value); + backward.put(value, key); + } + + /** + * Returns an immutable set of all nine primitive types (including {@code void}). Note that a + * simpler way to test whether a {@code Class} instance is a member of this set is to call {@link + * Class#isPrimitive}. + * + * @since 3.0 + */ + public static Set> allPrimitiveTypes() { + return PRIMITIVE_TO_WRAPPER_TYPE.keySet(); + } + + /** + * Returns an immutable set of all nine primitive-wrapper types (including {@link Void}). + * + * @since 3.0 + */ + public static Set> allWrapperTypes() { + return WRAPPER_TO_PRIMITIVE_TYPE.keySet(); + } + + /** + * Returns {@code true} if {@code type} is one of the nine primitive-wrapper types, such as {@link + * Integer}. + * + * @see Class#isPrimitive + */ + public static boolean isWrapperType(Class type) { + return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(checkNotNull(type)); + } + + /** + * Returns the corresponding wrapper type of {@code type} if it is a primitive type; otherwise + * returns {@code type} itself. Idempotent. + * + *

+   *     wrap(int.class) == Integer.class
+   *     wrap(Integer.class) == Integer.class
+   *     wrap(String.class) == String.class
+   * 
+ */ + public static Class wrap(Class type) { + checkNotNull(type); + + // cast is safe: long.class and Long.class are both of type Class + @SuppressWarnings("unchecked") + Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get(type); + return (wrapped == null) ? type : wrapped; + } + + /** + * Returns the corresponding primitive type of {@code type} if it is a wrapper type; otherwise + * returns {@code type} itself. Idempotent. + * + *
+   *     unwrap(Integer.class) == int.class
+   *     unwrap(int.class) == int.class
+   *     unwrap(String.class) == String.class
+   * 
+ */ + public static Class unwrap(Class type) { + checkNotNull(type); + + // cast is safe: long.class and Long.class are both of type Class + @SuppressWarnings("unchecked") + Class unwrapped = (Class) WRAPPER_TO_PRIMITIVE_TYPE.get(type); + return (unwrapped == null) ? type : unwrapped; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Shorts.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Shorts.java new file mode 100644 index 0000000000000..1c754f05fbee2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/Shorts.java @@ -0,0 +1,744 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.base.Converter; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkElementIndex; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code short} primitives, that are not already found in + * either {@link Short} or {@link Arrays}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Shorts extends ShortsMethodsForWeb { + private Shorts() {} + + /** + * The number of bytes required to represent a primitive {@code short} value. + * + *

Java 8 users: use {@link Short#BYTES} instead. + */ + public static final int BYTES = Short.SIZE / Byte.SIZE; + + /** + * The largest power of two that can be represented as a {@code short}. + * + * @since 10.0 + */ + public static final short MAX_POWER_OF_TWO = 1 << (Short.SIZE - 2); + + /** + * Returns a hash code for {@code value}; equal to the result of invoking {@code ((Short) + * value).hashCode()}. + * + *

Java 8 users: use {@link Short#hashCode(short)} instead. + * + * @param value a primitive {@code short} value + * @return a hash code for the value + */ + public static int hashCode(short value) { + return value; + } + + /** + * Returns the {@code short} value that is equal to {@code value}, if possible. + * + * @param value any value in the range of the {@code short} type + * @return the {@code short} value that equals {@code value} + * @throws IllegalArgumentException if {@code value} is greater than {@link Short#MAX_VALUE} or + * less than {@link Short#MIN_VALUE} + */ + public static short checkedCast(long value) { + short result = (short) value; + checkArgument(result == value, "Out of range: %s", value); + return result; + } + + /** + * Returns the {@code short} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code short} if it is in the range of the {@code short} type, + * {@link Short#MAX_VALUE} if it is too large, or {@link Short#MIN_VALUE} if it is too small + */ + public static short saturatedCast(long value) { + if (value > Short.MAX_VALUE) { + return Short.MAX_VALUE; + } + if (value < Short.MIN_VALUE) { + return Short.MIN_VALUE; + } + return (short) value; + } + + /** + * Compares the two specified {@code short} values. The sign of the value returned is the same as + * that of {@code ((Short) a).compareTo(b)}. + * + *

Note for Java 7 and later: this method should be treated as deprecated; use the + * equivalent {@link Short#compare} method instead. + * + * @param a the first {@code short} to compare + * @param b the second {@code short} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(short a, short b) { + return a - b; // safe due to restricted range + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in {@code array}. + * + * @param array an array of {@code short} values, possibly empty + * @param target a primitive {@code short} value + * @return {@code true} if {@code array[i] == target} for some value of {@code i} + */ + public static boolean contains(short[] array, short target) { + for (short value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code short} values, possibly empty + * @param target a primitive {@code short} value + * @return the least index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int indexOf(short[] array, short target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf(short[] array, short target, int start, int end) { + for (int i = start; i < end; i++) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the start position of the first occurrence of the specified {@code target} within + * {@code array}, or {@code -1} if there is no such occurrence. + * + *

More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array, + * i, i + target.length)} contains exactly the same elements as {@code target}. + * + * @param array the array to search for the sequence {@code target} + * @param target the array to search for as a sub-sequence of {@code array} + */ + public static int indexOf(short[] array, short[] target) { + checkNotNull(array, "array"); + checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } + + outer: + for (int i = 0; i < array.length - target.length + 1; i++) { + for (int j = 0; j < target.length; j++) { + if (array[i + j] != target[j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Returns the index of the last appearance of the value {@code target} in {@code array}. + * + * @param array an array of {@code short} values, possibly empty + * @param target a primitive {@code short} value + * @return the greatest index {@code i} for which {@code array[i] == target}, or {@code -1} if no + * such index exists. + */ + public static int lastIndexOf(short[] array, short target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf(short[] array, short target, int start, int end) { + for (int i = end - 1; i >= start; i--) { + if (array[i] == target) { + return i; + } + } + return -1; + } + + /** + * Returns the least value present in {@code array}. + * + * @param array a nonempty array of {@code short} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static short min(short... array) { + checkArgument(array.length > 0); + short min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } + + /** + * Returns the greatest value present in {@code array}. + * + * @param array a nonempty array of {@code short} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + @GwtIncompatible( + "Available in GWT! Annotation is to avoid conflict with GWT specialization of base class.") + public static short max(short... array) { + checkArgument(array.length > 0); + short max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } + + /** + * Returns the value nearest to {@code value} which is within the closed range {@code [min..max]}. + * + *

If {@code value} is within the range {@code [min..max]}, {@code value} is returned + * unchanged. If {@code value} is less than {@code min}, {@code min} is returned, and if {@code + * value} is greater than {@code max}, {@code max} is returned. + * + * @param value the {@code short} value to constrain + * @param min the lower bound (inclusive) of the range to constrain {@code value} to + * @param max the upper bound (inclusive) of the range to constrain {@code value} to + * @throws IllegalArgumentException if {@code min > max} + * @since 21.0 + */ + public static short constrainToRange(short value, short min, short max) { + checkArgument(min <= max, "min (%s) must be less than or equal to max (%s)", min, max); + return value < min ? min : value < max ? value : max; + } + + /** + * Returns the values from each provided array combined into a single array. For example, {@code + * concat(new short[] {a, b}, new short[] {}, new short[] {c}} returns the array {@code {a, b, + * c}}. + * + * @param arrays zero or more {@code short} arrays + * @return a single array containing all the values from the source arrays, in order + */ + public static short[] concat(short[]... arrays) { + int length = 0; + for (short[] array : arrays) { + length += array.length; + } + short[] result = new short[length]; + int pos = 0; + for (short[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + /** + * Returns a big-endian representation of {@code value} in a 2-element byte array; equivalent to + * {@code ByteBuffer.allocate(2).putShort(value).array()}. For example, the input value {@code + * (short) 0x1234} would yield the byte array {@code {0x12, 0x34}}. + * + *

If you need to convert and concatenate several values (possibly even of different types), + * use a shared {@link java.nio.ByteBuffer} instance, or use {@link + * org.apache.iotdb.google.common.io.ByteStreams#newDataOutput()} to get a growable buffer. + */ + @GwtIncompatible // doesn't work + public static byte[] toByteArray(short value) { + return new byte[] {(byte) (value >> 8), (byte) value}; + } + + /** + * Returns the {@code short} value whose big-endian representation is stored in the first 2 bytes + * of {@code bytes}; equivalent to {@code ByteBuffer.wrap(bytes).getShort()}. For example, the + * input byte array {@code {0x54, 0x32}} would yield the {@code short} value {@code 0x5432}. + * + *

Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that library exposes much more + * flexibility at little cost in readability. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than 2 elements + */ + @GwtIncompatible // doesn't work + public static short fromByteArray(byte[] bytes) { + checkArgument(bytes.length >= BYTES, "array too small: %s < %s", bytes.length, BYTES); + return fromBytes(bytes[0], bytes[1]); + } + + /** + * Returns the {@code short} value whose byte representation is the given 2 bytes, in big-endian + * order; equivalent to {@code Shorts.fromByteArray(new byte[] {b1, b2})}. + * + * @since 7.0 + */ + @GwtIncompatible // doesn't work + public static short fromBytes(byte b1, byte b2) { + return (short) ((b1 << 8) | (b2 & 0xFF)); + } + + private static final class ShortConverter extends Converter + implements Serializable { + static final ShortConverter INSTANCE = new ShortConverter(); + + @Override + protected Short doForward(String value) { + return Short.decode(value); + } + + @Override + protected String doBackward(Short value) { + return value.toString(); + } + + @Override + public String toString() { + return "Shorts.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 1; + } + + /** + * Returns a serializable converter object that converts between strings and shorts using {@link + * Short#decode} and {@link Short#toString()}. The returned converter throws {@link + * NumberFormatException} if the input string is invalid. + * + *

Warning: please see {@link Short#decode} to understand exactly how strings are + * parsed. For example, the string {@code "0123"} is treated as octal and converted to the + * value {@code 83}. + * + * @since 16.0 + */ + public static Converter stringConverter() { + return ShortConverter.INSTANCE; + } + + /** + * Returns an array containing the same values as {@code array}, but guaranteed to be of a + * specified minimum length. If {@code array} already has a length of at least {@code minLength}, + * it is returned directly. Otherwise, a new array of size {@code minLength + padding} is + * returned, containing the values of {@code array}, and zeroes in the remaining places. + * + * @param array the source array + * @param minLength the minimum length the returned array must guarantee + * @param padding an extra amount to "grow" the array by if growth is necessary + * @throws IllegalArgumentException if {@code minLength} or {@code padding} is negative + * @return an array containing the values of {@code array}, with guaranteed minimum length {@code + * minLength} + */ + public static short[] ensureCapacity(short[] array, int minLength, int padding) { + checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + checkArgument(padding >= 0, "Invalid padding: %s", padding); + return (array.length < minLength) ? Arrays.copyOf(array, minLength + padding) : array; + } + + /** + * Returns a string containing the supplied {@code short} values separated by {@code separator}. + * For example, {@code join("-", (short) 1, (short) 2, (short) 3)} returns the string {@code + * "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code short} values, possibly empty + */ + public static String join(String separator, short... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 6); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code short} arrays lexicographically. That is, it + * compares, using {@link #compare(short, short)}), the first pair of values that follow any + * common prefix, or when one array is a prefix of the other, treats the shorter array as the + * lesser. For example, {@code [] < [(short) 1] < [(short) 1, (short) 2] < [(short) 2]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(short[], + * short[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(short[] left, short[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = Shorts.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "Shorts.lexicographicalComparator()"; + } + } + + /** + * Sorts the elements of {@code array} in descending order. + * + * @since 23.1 + */ + public static void sortDescending(short[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + * @since 23.1 + */ + public static void sortDescending(short[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Reverses the elements of {@code array}. This is equivalent to {@code + * Collections.reverse(Shorts.asList(array))}, but is likely to be more efficient. + * + * @since 23.1 + */ + public static void reverse(short[] array) { + checkNotNull(array); + reverse(array, 0, array.length); + } + + /** + * Reverses the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive. This is equivalent to {@code + * Collections.reverse(Shorts.asList(array).subList(fromIndex, toIndex))}, but is likely to be + * more efficient. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 23.1 + */ + public static void reverse(short[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex, j = toIndex - 1; i < j; i++, j--) { + short tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + + /** + * Performs a right rotation of {@code array} of "distance" places, so that the first element is + * moved to index "distance", and the element at index {@code i} ends up at index {@code (distance + * + i) mod array.length}. This is equivalent to {@code Collections.rotate(Shorts.asList(array), + * distance)}, but is considerably faster and avoids allocation and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @since 32.0.0 + */ + public static void rotate(short[] array, int distance) { + rotate(array, distance, 0, array.length); + } + + /** + * Performs a right rotation of {@code array} between {@code fromIndex} inclusive and {@code + * toIndex} exclusive. This is equivalent to {@code + * Collections.rotate(Shorts.asList(array).subList(fromIndex, toIndex), distance)}, but is + * considerably faster and avoids allocations and garbage collection. + * + *

The provided "distance" may be negative, which will rotate left. + * + * @throws IndexOutOfBoundsException if {@code fromIndex < 0}, {@code toIndex > array.length}, or + * {@code toIndex > fromIndex} + * @since 32.0.0 + */ + public static void rotate(short[] array, int distance, int fromIndex, int toIndex) { + // See Ints.rotate for more details about possible algorithms here. + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + if (array.length <= 1) { + return; + } + + int length = toIndex - fromIndex; + // Obtain m = (-distance mod length), a non-negative value less than "length". This is how many + // places left to rotate. + int m = -distance % length; + m = (m < 0) ? m + length : m; + // The current index of what will become the first element of the rotated section. + int newFirstIndex = m + fromIndex; + if (newFirstIndex == fromIndex) { + return; + } + + reverse(array, fromIndex, newFirstIndex); + reverse(array, newFirstIndex, toIndex); + reverse(array, fromIndex, toIndex); + } + + /** + * Returns an array containing each value of {@code collection}, converted to a {@code short} + * value in the manner of {@link Number#shortValue}. + * + *

Elements are copied from the argument collection as if by {@code collection.toArray()}. + * Calling this method is as thread-safe as calling that method. + * + * @param collection a collection of {@code Number} instances + * @return an array containing the same values as {@code collection}, in the same order, converted + * to primitives + * @throws NullPointerException if {@code collection} or any of its elements is null + * @since 1.0 (parameter was {@code Collection} before 12.0) + */ + public static short[] toArray(Collection collection) { + if (collection instanceof ShortArrayAsList) { + return ((ShortArrayAsList) collection).toShortArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + short[] array = new short[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = ((Number) checkNotNull(boxedArray[i])).shortValue(); + } + return array; + } + + /** + * Returns a fixed-size list backed by the specified array, similar to {@link + * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)}, but any attempt to + * set a value to {@code null} will result in a {@link NullPointerException}. + * + *

The returned list maintains the values, but not the identities, of {@code Short} objects + * written to or read from it. For example, whether {@code list.get(0) == list.get(0)} is true for + * the returned list is unspecified. + * + *

The returned list is serializable. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List asList(short... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new ShortArrayAsList(backingArray); + } + + @GwtCompatible + private static class ShortArrayAsList extends AbstractList + implements RandomAccess, Serializable { + final short[] array; + final int start; + final int end; + + ShortArrayAsList(short[] array) { + this(array, 0, array.length); + } + + ShortArrayAsList(short[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + @Override + public int size() { + return end - start; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Short get(int index) { + checkElementIndex(index, size()); + return array[start + index]; + } + + @Override + public boolean contains(Object target) { + // Overridden to prevent a ton of boxing + return (target instanceof Short) && Shorts.indexOf(array, (Short) target, start, end) != -1; + } + + @Override + public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Short) { + int i = Shorts.indexOf(array, (Short) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof Short) { + int i = Shorts.lastIndexOf(array, (Short) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override + public Short set(int index, Short element) { + checkElementIndex(index, size()); + short oldValue = array[start + index]; + // checkNotNull for GWT (do not optimize) + array[start + index] = checkNotNull(element); + return oldValue; + } + + @Override + public List subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new ShortArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ShortArrayAsList) { + ShortArrayAsList that = (ShortArrayAsList) object; + int size = size(); + if (that.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (array[start + i] != that.array[that.start + i]) { + return false; + } + } + return true; + } + return super.equals(object); + } + + @Override + public int hashCode() { + int result = 1; + for (int i = start; i < end; i++) { + result = 31 * result + Shorts.hashCode(array[i]); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(size() * 6); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + short[] toShortArray() { + return Arrays.copyOfRange(array, start, end); + } + + private static final long serialVersionUID = 0; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ShortsMethodsForWeb.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ShortsMethodsForWeb.java new file mode 100644 index 0000000000000..2f186264a0a10 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/ShortsMethodsForWeb.java @@ -0,0 +1,30 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Holder for web specializations of methods of {@code Shorts}. Intended to be empty for regular + * version. + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class ShortsMethodsForWeb {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/SignedBytes.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/SignedBytes.java new file mode 100644 index 0000000000000..81faec9938531 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/SignedBytes.java @@ -0,0 +1,226 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Arrays; +import java.util.Comparator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code byte} primitives that interpret values as signed. The + * corresponding methods that treat the values as unsigned are found in {@link UnsignedBytes}, and + * the methods for which signedness is not an issue are in {@link Bytes}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +// TODO(kevinb): how to prevent warning on UnsignedBytes when building GWT +// javadoc? +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class SignedBytes { + private SignedBytes() {} + + /** + * The largest power of two that can be represented as a signed {@code byte}. + * + * @since 10.0 + */ + public static final byte MAX_POWER_OF_TWO = 1 << 6; + + /** + * Returns the {@code byte} value that is equal to {@code value}, if possible. + * + * @param value any value in the range of the {@code byte} type + * @return the {@code byte} value that equals {@code value} + * @throws IllegalArgumentException if {@code value} is greater than {@link Byte#MAX_VALUE} or + * less than {@link Byte#MIN_VALUE} + */ + public static byte checkedCast(long value) { + byte result = (byte) value; + checkArgument(result == value, "Out of range: %s", value); + return result; + } + + /** + * Returns the {@code byte} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code byte} if it is in the range of the {@code byte} type, + * {@link Byte#MAX_VALUE} if it is too large, or {@link Byte#MIN_VALUE} if it is too small + */ + public static byte saturatedCast(long value) { + if (value > Byte.MAX_VALUE) { + return Byte.MAX_VALUE; + } + if (value < Byte.MIN_VALUE) { + return Byte.MIN_VALUE; + } + return (byte) value; + } + + /** + * Compares the two specified {@code byte} values. The sign of the value returned is the same as + * that of {@code ((Byte) a).compareTo(b)}. + * + *

Note: this method behaves identically to the JDK 7 method {@link Byte#compare}. + * + * @param a the first {@code byte} to compare + * @param b the second {@code byte} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + // TODO(kevinb): if Ints.compare etc. are ever removed, *maybe* remove this + // one too, which would leave compare methods only on the Unsigned* classes. + public static int compare(byte a, byte b) { + return a - b; // safe due to restricted range + } + + /** + * Returns the least value present in {@code array}. + * + * @param array a nonempty array of {@code byte} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array + * @throws IllegalArgumentException if {@code array} is empty + */ + public static byte min(byte... array) { + checkArgument(array.length > 0); + byte min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } + + /** + * Returns the greatest value present in {@code array}. + * + * @param array a nonempty array of {@code byte} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array + * @throws IllegalArgumentException if {@code array} is empty + */ + public static byte max(byte... array) { + checkArgument(array.length > 0); + byte max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + return max; + } + + /** + * Returns a string containing the supplied {@code byte} values separated by {@code separator}. + * For example, {@code join(":", 0x01, 0x02, -0x01)} returns the string {@code "1:2:-1"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code byte} values, possibly empty + */ + public static String join(String separator, byte... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(array[0]); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(array[i]); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code byte} arrays lexicographically. That is, it + * compares, using {@link #compare(byte, byte)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [0x01] < [0x01, 0x80] < [0x01, 0x7F] < [0x02]}. Values are treated as + * signed. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link + * java.util.Arrays#equals(byte[], byte[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(byte[] left, byte[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = SignedBytes.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "SignedBytes.lexicographicalComparator()"; + } + } + + /** + * Sorts the elements of {@code array} in descending order. + * + * @since 23.1 + */ + public static void sortDescending(byte[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order. + * + * @since 23.1 + */ + public static void sortDescending(byte[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + Arrays.sort(array, fromIndex, toIndex); + Bytes.reverse(array, fromIndex, toIndex); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedBytes.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedBytes.java new file mode 100644 index 0000000000000..3c325973b5969 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedBytes.java @@ -0,0 +1,515 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import sun.misc.Unsafe; + +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.Comparator; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code byte} primitives that interpret values as + * unsigned (that is, any negative value {@code b} is treated as the positive value {@code + * 256 + b}). The corresponding methods that treat the values as signed are found in {@link + * SignedBytes}, and the methods for which signedness is not an issue are in {@link Bytes}. + * + *

See the Guava User Guide article on primitive utilities. + * + * @author Kevin Bourrillion + * @author Martin Buchholz + * @author Hiroshi Yamauchi + * @author Louis Wasserman + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class UnsignedBytes { + private UnsignedBytes() {} + + /** + * The largest power of two that can be represented as an unsigned {@code byte}. + * + * @since 10.0 + */ + public static final byte MAX_POWER_OF_TWO = (byte) 0x80; + + /** + * The largest value that fits into an unsigned byte. + * + * @since 13.0 + */ + public static final byte MAX_VALUE = (byte) 0xFF; + + private static final int UNSIGNED_MASK = 0xFF; + + /** + * Returns the value of the given byte as an integer, when treated as unsigned. That is, returns + * {@code value + 256} if {@code value} is negative; {@code value} itself otherwise. + * + *

Java 8 users: use {@link Byte#toUnsignedInt(byte)} instead. + * + * @since 6.0 + */ + public static int toInt(byte value) { + return value & UNSIGNED_MASK; + } + + /** + * Returns the {@code byte} value that, when treated as unsigned, is equal to {@code value}, if + * possible. + * + * @param value a value between 0 and 255 inclusive + * @return the {@code byte} value that, when treated as unsigned, equals {@code value} + * @throws IllegalArgumentException if {@code value} is negative or greater than 255 + */ + public static byte checkedCast(long value) { + checkArgument(value >> Byte.SIZE == 0, "out of range: %s", value); + return (byte) value; + } + + /** + * Returns the {@code byte} value that, when treated as unsigned, is nearest in value to {@code + * value}. + * + * @param value any {@code long} value + * @return {@code (byte) 255} if {@code value >= 255}, {@code (byte) 0} if {@code value <= 0}, and + * {@code value} cast to {@code byte} otherwise + */ + public static byte saturatedCast(long value) { + if (value > toInt(MAX_VALUE)) { + return MAX_VALUE; // -1 + } + if (value < 0) { + return (byte) 0; + } + return (byte) value; + } + + /** + * Compares the two specified {@code byte} values, treating them as unsigned values between 0 and + * 255 inclusive. For example, {@code (byte) -127} is considered greater than {@code (byte) 127} + * because it is seen as having the value of positive {@code 129}. + * + * @param a the first {@code byte} to compare + * @param b the second {@code byte} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(byte a, byte b) { + return toInt(a) - toInt(b); + } + + /** + * Returns the least value present in {@code array}, treating values as unsigned. + * + * @param array a nonempty array of {@code byte} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array according to {@link #compare} + * @throws IllegalArgumentException if {@code array} is empty + */ + public static byte min(byte... array) { + checkArgument(array.length > 0); + int min = toInt(array[0]); + for (int i = 1; i < array.length; i++) { + int next = toInt(array[i]); + if (next < min) { + min = next; + } + } + return (byte) min; + } + + /** + * Returns the greatest value present in {@code array}, treating values as unsigned. + * + * @param array a nonempty array of {@code byte} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array according to {@link #compare} + * @throws IllegalArgumentException if {@code array} is empty + */ + public static byte max(byte... array) { + checkArgument(array.length > 0); + int max = toInt(array[0]); + for (int i = 1; i < array.length; i++) { + int next = toInt(array[i]); + if (next > max) { + max = next; + } + } + return (byte) max; + } + + /** + * Returns a string representation of x, where x is treated as unsigned. + * + * @since 13.0 + */ + public static String toString(byte x) { + return toString(x, 10); + } + + /** + * Returns a string representation of {@code x} for the given radix, where {@code x} is treated as + * unsigned. + * + * @param x the value to convert to a string. + * @param radix the radix to use while working with {@code x} + * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} + * and {@link Character#MAX_RADIX}. + * @since 13.0 + */ + public static String toString(byte x, int radix) { + checkArgument( + radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX, + "radix (%s) must be between Character.MIN_RADIX and Character.MAX_RADIX", + radix); + // Benchmarks indicate this is probably not worth optimizing. + return Integer.toString(toInt(x), radix); + } + + /** + * Returns the unsigned {@code byte} value represented by the given decimal string. + * + * @throws NumberFormatException if the string does not contain a valid unsigned {@code byte} + * value + * @throws NullPointerException if {@code string} is null (in contrast to {@link + * Byte#parseByte(String)}) + * @since 13.0 + */ + public static byte parseUnsignedByte(String string) { + return parseUnsignedByte(string, 10); + } + + /** + * Returns the unsigned {@code byte} value represented by a string with the given radix. + * + * @param string the string containing the unsigned {@code byte} representation to be parsed. + * @param radix the radix to use while parsing {@code string} + * @throws NumberFormatException if the string does not contain a valid unsigned {@code byte} with + * the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} and {@link + * Character#MAX_RADIX}. + * @throws NullPointerException if {@code string} is null (in contrast to {@link + * Byte#parseByte(String)}) + * @since 13.0 + */ + public static byte parseUnsignedByte(String string, int radix) { + int parse = Integer.parseInt(checkNotNull(string), radix); + // We need to throw a NumberFormatException, so we have to duplicate checkedCast. =( + if (parse >> Byte.SIZE == 0) { + return (byte) parse; + } else { + throw new NumberFormatException("out of range: " + parse); + } + } + + /** + * Returns a string containing the supplied {@code byte} values separated by {@code separator}. + * For example, {@code join(":", (byte) 1, (byte) 2, (byte) 255)} returns the string {@code + * "1:2:255"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of {@code byte} values, possibly empty + */ + public static String join(String separator, byte... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * (3 + separator.length())); + builder.append(toInt(array[0])); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(toString(array[i])); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two {@code byte} arrays lexicographically. That is, it + * compares, using {@link #compare(byte, byte)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [0x01] < [0x01, 0x7F] < [0x01, 0x80] < [0x02]}. Values are treated as + * unsigned. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link + * java.util.Arrays#equals(byte[], byte[])}. + * + * @since 2.0 + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparatorHolder.BEST_COMPARATOR; + } + + @VisibleForTesting + static Comparator lexicographicalComparatorJavaImpl() { + return LexicographicalComparatorHolder.PureJavaComparator.INSTANCE; + } + + /** + * Provides a lexicographical comparator implementation; either a Java implementation or a faster + * implementation based on {@link Unsafe}. + * + *

Uses reflection to gracefully fall back to the Java implementation if {@code Unsafe} isn't + * available. + */ + @VisibleForTesting + static class LexicographicalComparatorHolder { + static final String UNSAFE_COMPARATOR_NAME = + LexicographicalComparatorHolder.class.getName() + "$UnsafeComparator"; + + static final Comparator BEST_COMPARATOR = getBestComparator(); + + @VisibleForTesting + enum UnsafeComparator implements Comparator { + INSTANCE; + + static final boolean BIG_ENDIAN = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN); + + /* + * The following static final fields exist for performance reasons. + * + * In UnsignedBytesBenchmark, accessing the following objects via static final fields is the + * fastest (more than twice as fast as the Java implementation, vs ~1.5x with non-final static + * fields, on x86_32) under the Hotspot server compiler. The reason is obviously that the + * non-final fields need to be reloaded inside the loop. + * + * And, no, defining (final or not) local variables out of the loop still isn't as good + * because the null check on the theUnsafe object remains inside the loop and + * BYTE_ARRAY_BASE_OFFSET doesn't get constant-folded. + * + * The compiler can treat static final fields as compile-time constants and can constant-fold + * them while (final or not) local variables are run time values. + */ + + static final Unsafe theUnsafe = getUnsafe(); + + /** The offset to the first element in a byte array. */ + static final int BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + static { + // fall back to the safer pure java implementation unless we're in + // a 64-bit JVM with an 8-byte aligned field offset. + if (!("64".equals(System.getProperty("sun.arch.data.model")) + && (BYTE_ARRAY_BASE_OFFSET % 8) == 0 + // sanity check - this should never fail + && theUnsafe.arrayIndexScale(byte[].class) == 1)) { + throw new Error(); // force fallback to PureJavaComparator + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. Replace with a simple + * call to Unsafe.getUnsafe when integrating into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException e) { + // that's okay; try reflection instead + } + try { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction() { + @Override + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", e.getCause()); + } + } + + @Override + public int compare(byte[] left, byte[] right) { + int stride = 8; + int minLength = Math.min(left.length, right.length); + int strideLimit = minLength & ~(stride - 1); + int i; + + /* + * Compare 8 bytes at a time. Benchmarking on x86 shows a stride of 8 bytes is no slower + * than 4 bytes even on 32-bit. On the other hand, it is substantially faster on 64-bit. + */ + for (i = 0; i < strideLimit; i += stride) { + long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); + long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); + if (lw != rw) { + if (BIG_ENDIAN) { + return UnsignedLongs.compare(lw, rw); + } + + /* + * We want to compare only the first index where left[index] != right[index]. This + * corresponds to the least significant nonzero byte in lw ^ rw, since lw and rw are + * little-endian. Long.numberOfTrailingZeros(diff) tells us the least significant + * nonzero bit, and zeroing out the first three bits of L.nTZ gives us the shift to get + * that least significant nonzero byte. + */ + int n = Long.numberOfTrailingZeros(lw ^ rw) & ~0x7; + return ((int) ((lw >>> n) & UNSIGNED_MASK)) - ((int) ((rw >>> n) & UNSIGNED_MASK)); + } + } + + // The epilogue to cover the last (minLength % stride) elements. + for (; i < minLength; i++) { + int result = UnsignedBytes.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "UnsignedBytes.lexicographicalComparator() (sun.misc.Unsafe version)"; + } + } + + enum PureJavaComparator implements Comparator { + INSTANCE; + + @Override + public int compare(byte[] left, byte[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = UnsignedBytes.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "UnsignedBytes.lexicographicalComparator() (pure Java version)"; + } + } + + /** + * Returns the Unsafe-using Comparator, or falls back to the pure-Java implementation if unable + * to do so. + */ + static Comparator getBestComparator() { + try { + Class theClass = Class.forName(UNSAFE_COMPARATOR_NAME); + + // requireNonNull is safe because the class is an enum. + Object[] constants = requireNonNull(theClass.getEnumConstants()); + + // yes, UnsafeComparator does implement Comparator + @SuppressWarnings("unchecked") + Comparator comparator = (Comparator) constants[0]; + return comparator; + } catch (Throwable t) { // ensure we really catch *everything* + return lexicographicalComparatorJavaImpl(); + } + } + } + + private static byte flip(byte b) { + return (byte) (b ^ 0x80); + } + + /** + * Sorts the array, treating its elements as unsigned bytes. + * + * @since 23.1 + */ + public static void sort(byte[] array) { + checkNotNull(array); + sort(array, 0, array.length); + } + + /** + * Sorts the array between {@code fromIndex} inclusive and {@code toIndex} exclusive, treating its + * elements as unsigned bytes. + * + * @since 23.1 + */ + public static void sort(byte[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex; i < toIndex; i++) { + array[i] = flip(array[i]); + } + Arrays.sort(array, fromIndex, toIndex); + for (int i = fromIndex; i < toIndex; i++) { + array[i] = flip(array[i]); + } + } + + /** + * Sorts the elements of {@code array} in descending order, interpreting them as unsigned 8-bit + * integers. + * + * @since 23.1 + */ + public static void sortDescending(byte[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order, interpreting them as unsigned 8-bit integers. + * + * @since 23.1 + */ + public static void sortDescending(byte[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex; i < toIndex; i++) { + array[i] ^= Byte.MAX_VALUE; + } + Arrays.sort(array, fromIndex, toIndex); + for (int i = fromIndex; i < toIndex; i++) { + array[i] ^= Byte.MAX_VALUE; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedInteger.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedInteger.java new file mode 100644 index 0000000000000..c046ada63c1bf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedInteger.java @@ -0,0 +1,259 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.math.BigInteger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.primitives.UnsignedInts.INT_MASK; +import static org.apache.iotdb.google.common.primitives.UnsignedInts.compare; +import static org.apache.iotdb.google.common.primitives.UnsignedInts.toLong; + +/** + * A wrapper class for unsigned {@code int} values, supporting arithmetic operations. + * + *

In some cases, when speed is more important than code readability, it may be faster simply to + * treat primitive {@code int} values as unsigned, using the methods from {@link UnsignedInts}. + * + *

See the Guava User Guide article on unsigned + * primitive utilities. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class UnsignedInteger extends Number implements Comparable { + public static final UnsignedInteger ZERO = fromIntBits(0); + public static final UnsignedInteger ONE = fromIntBits(1); + public static final UnsignedInteger MAX_VALUE = fromIntBits(-1); + + private final int value; + + private UnsignedInteger(int value) { + // GWT doesn't consistently overflow values to make them 32-bit, so we need to force it. + this.value = value & 0xffffffff; + } + + /** + * Returns an {@code UnsignedInteger} corresponding to a given bit representation. The argument is + * interpreted as an unsigned 32-bit value. Specifically, the sign bit of {@code bits} is + * interpreted as a normal bit, and all other bits are treated as usual. + * + *

If the argument is nonnegative, the returned result will be equal to {@code bits}, + * otherwise, the result will be equal to {@code 2^32 + bits}. + * + *

To represent unsigned decimal constants, consider {@link #valueOf(long)} instead. + * + * @since 14.0 + */ + public static UnsignedInteger fromIntBits(int bits) { + return new UnsignedInteger(bits); + } + + /** + * Returns an {@code UnsignedInteger} that is equal to {@code value}, if possible. The inverse + * operation of {@link #longValue()}. + */ + public static UnsignedInteger valueOf(long value) { + checkArgument( + (value & INT_MASK) == value, + "value (%s) is outside the range for an unsigned integer value", + value); + return fromIntBits((int) value); + } + + /** + * Returns a {@code UnsignedInteger} representing the same value as the specified {@link + * BigInteger}. This is the inverse operation of {@link #bigIntegerValue()}. + * + * @throws IllegalArgumentException if {@code value} is negative or {@code value >= 2^32} + */ + public static UnsignedInteger valueOf(BigInteger value) { + checkNotNull(value); + checkArgument( + value.signum() >= 0 && value.bitLength() <= Integer.SIZE, + "value (%s) is outside the range for an unsigned integer value", + value); + return fromIntBits(value.intValue()); + } + + /** + * Returns an {@code UnsignedInteger} holding the value of the specified {@code String}, parsed as + * an unsigned {@code int} value. + * + * @throws NumberFormatException if the string does not contain a parsable unsigned {@code int} + * value + */ + public static UnsignedInteger valueOf(String string) { + return valueOf(string, 10); + } + + /** + * Returns an {@code UnsignedInteger} holding the value of the specified {@code String}, parsed as + * an unsigned {@code int} value in the specified radix. + * + * @throws NumberFormatException if the string does not contain a parsable unsigned {@code int} + * value + */ + public static UnsignedInteger valueOf(String string, int radix) { + return fromIntBits(UnsignedInts.parseUnsignedInt(string, radix)); + } + + /** + * Returns the result of adding this and {@code val}. If the result would have more than 32 bits, + * returns the low 32 bits of the result. + * + * @since 14.0 + */ + public UnsignedInteger plus(UnsignedInteger val) { + return fromIntBits(this.value + checkNotNull(val).value); + } + + /** + * Returns the result of subtracting this and {@code val}. If the result would be negative, + * returns the low 32 bits of the result. + * + * @since 14.0 + */ + public UnsignedInteger minus(UnsignedInteger val) { + return fromIntBits(value - checkNotNull(val).value); + } + + /** + * Returns the result of multiplying this and {@code val}. If the result would have more than 32 + * bits, returns the low 32 bits of the result. + * + * @since 14.0 + */ + @J2ktIncompatible + @GwtIncompatible // Does not truncate correctly + public UnsignedInteger times(UnsignedInteger val) { + // TODO(lowasser): make this GWT-compatible + return fromIntBits(value * checkNotNull(val).value); + } + + /** + * Returns the result of dividing this by {@code val}. + * + * @throws ArithmeticException if {@code val} is zero + * @since 14.0 + */ + public UnsignedInteger dividedBy(UnsignedInteger val) { + return fromIntBits(UnsignedInts.divide(value, checkNotNull(val).value)); + } + + /** + * Returns this mod {@code val}. + * + * @throws ArithmeticException if {@code val} is zero + * @since 14.0 + */ + public UnsignedInteger mod(UnsignedInteger val) { + return fromIntBits(UnsignedInts.remainder(value, checkNotNull(val).value)); + } + + /** + * Returns the value of this {@code UnsignedInteger} as an {@code int}. This is an inverse + * operation to {@link #fromIntBits}. + * + *

Note that if this {@code UnsignedInteger} holds a value {@code >= 2^31}, the returned value + * will be equal to {@code this - 2^32}. + */ + @Override + public int intValue() { + return value; + } + + /** Returns the value of this {@code UnsignedInteger} as a {@code long}. */ + @Override + public long longValue() { + return toLong(value); + } + + /** + * Returns the value of this {@code UnsignedInteger} as a {@code float}, analogous to a widening + * primitive conversion from {@code int} to {@code float}, and correctly rounded. + */ + @Override + public float floatValue() { + return longValue(); + } + + /** + * Returns the value of this {@code UnsignedInteger} as a {@code float}, analogous to a widening + * primitive conversion from {@code int} to {@code double}, and correctly rounded. + */ + @Override + public double doubleValue() { + return longValue(); + } + + /** Returns the value of this {@code UnsignedInteger} as a {@link BigInteger}. */ + public BigInteger bigIntegerValue() { + return BigInteger.valueOf(longValue()); + } + + /** + * Compares this unsigned integer to another unsigned integer. Returns {@code 0} if they are + * equal, a negative number if {@code this < other}, and a positive number if {@code this > + * other}. + */ + @Override + public int compareTo(UnsignedInteger other) { + checkNotNull(other); + return compare(value, other.value); + } + + @Override + public int hashCode() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UnsignedInteger) { + UnsignedInteger other = (UnsignedInteger) obj; + return value == other.value; + } + return false; + } + + /** Returns a string representation of the {@code UnsignedInteger} value, in base 10. */ + @Override + public String toString() { + return toString(10); + } + + /** + * Returns a string representation of the {@code UnsignedInteger} value, in base {@code radix}. If + * {@code radix < Character.MIN_RADIX} or {@code radix > Character.MAX_RADIX}, the radix {@code + * 10} is used. + */ + public String toString(int radix) { + return UnsignedInts.toString(value, radix); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedInts.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedInts.java new file mode 100644 index 0000000000000..498269d9f65c0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedInts.java @@ -0,0 +1,391 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Arrays; +import java.util.Comparator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code int} primitives that interpret values as + * unsigned (that is, any negative value {@code x} is treated as the positive value {@code + * 2^32 + x}). The methods for which signedness is not an issue are in {@link Ints}, as well as + * signed versions of methods for which signedness is an issue. + * + *

In addition, this class provides several static methods for converting an {@code int} to a + * {@code String} and a {@code String} to an {@code int} that treat the {@code int} as an unsigned + * number. + * + *

Users of these utilities must be extremely careful not to mix up signed and unsigned + * {@code int} values. When possible, it is recommended that the {@link UnsignedInteger} wrapper + * class be used, at a small efficiency penalty, to enforce the distinction in the type system. + * + *

See the Guava User Guide article on unsigned + * primitive utilities. + * + * @author Louis Wasserman + * @since 11.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class UnsignedInts { + static final long INT_MASK = 0xffffffffL; + + private UnsignedInts() {} + + static int flip(int value) { + return value ^ Integer.MIN_VALUE; + } + + /** + * Compares the two specified {@code int} values, treating them as unsigned values between {@code + * 0} and {@code 2^32 - 1} inclusive. + * + *

Java 8 users: use {@link Integer#compareUnsigned(int, int)} instead. + * + * @param a the first unsigned {@code int} to compare + * @param b the second unsigned {@code int} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(int a, int b) { + return Ints.compare(flip(a), flip(b)); + } + + /** + * Returns the value of the given {@code int} as a {@code long}, when treated as unsigned. + * + *

Java 8 users: use {@link Integer#toUnsignedLong(int)} instead. + */ + public static long toLong(int value) { + return value & INT_MASK; + } + + /** + * Returns the {@code int} value that, when treated as unsigned, is equal to {@code value}, if + * possible. + * + * @param value a value between 0 and 232-1 inclusive + * @return the {@code int} value that, when treated as unsigned, equals {@code value} + * @throws IllegalArgumentException if {@code value} is negative or greater than or equal to + * 232 + * @since 21.0 + */ + public static int checkedCast(long value) { + checkArgument((value >> Integer.SIZE) == 0, "out of range: %s", value); + return (int) value; + } + + /** + * Returns the {@code int} value that, when treated as unsigned, is nearest in value to {@code + * value}. + * + * @param value any {@code long} value + * @return {@code 2^32 - 1} if {@code value >= 2^32}, {@code 0} if {@code value <= 0}, and {@code + * value} cast to {@code int} otherwise + * @since 21.0 + */ + public static int saturatedCast(long value) { + if (value <= 0) { + return 0; + } else if (value >= (1L << 32)) { + return -1; + } else { + return (int) value; + } + } + + /** + * Returns the least value present in {@code array}, treating values as unsigned. + * + * @param array a nonempty array of unsigned {@code int} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array according to {@link #compare} + * @throws IllegalArgumentException if {@code array} is empty + */ + public static int min(int... array) { + checkArgument(array.length > 0); + int min = flip(array[0]); + for (int i = 1; i < array.length; i++) { + int next = flip(array[i]); + if (next < min) { + min = next; + } + } + return flip(min); + } + + /** + * Returns the greatest value present in {@code array}, treating values as unsigned. + * + * @param array a nonempty array of unsigned {@code int} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array according to {@link #compare} + * @throws IllegalArgumentException if {@code array} is empty + */ + public static int max(int... array) { + checkArgument(array.length > 0); + int max = flip(array[0]); + for (int i = 1; i < array.length; i++) { + int next = flip(array[i]); + if (next > max) { + max = next; + } + } + return flip(max); + } + + /** + * Returns a string containing the supplied unsigned {@code int} values separated by {@code + * separator}. For example, {@code join("-", 1, 2, 3)} returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of unsigned {@code int} values, possibly empty + */ + public static String join(String separator, int... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(toString(array[0])); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(toString(array[i])); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two arrays of unsigned {@code int} values lexicographically. That is, it + * compares, using {@link #compare(int, int)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [1] < [1, 2] < [2] < [1 << 31]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(int[], int[])}. + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(int[] left, int[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + if (left[i] != right[i]) { + return UnsignedInts.compare(left[i], right[i]); + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "UnsignedInts.lexicographicalComparator()"; + } + } + + /** + * Sorts the array, treating its elements as unsigned 32-bit integers. + * + * @since 23.1 + */ + public static void sort(int[] array) { + checkNotNull(array); + sort(array, 0, array.length); + } + + /** + * Sorts the array between {@code fromIndex} inclusive and {@code toIndex} exclusive, treating its + * elements as unsigned 32-bit integers. + * + * @since 23.1 + */ + public static void sort(int[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex; i < toIndex; i++) { + array[i] = flip(array[i]); + } + Arrays.sort(array, fromIndex, toIndex); + for (int i = fromIndex; i < toIndex; i++) { + array[i] = flip(array[i]); + } + } + + /** + * Sorts the elements of {@code array} in descending order, interpreting them as unsigned 32-bit + * integers. + * + * @since 23.1 + */ + public static void sortDescending(int[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order, interpreting them as unsigned 32-bit integers. + * + * @since 23.1 + */ + public static void sortDescending(int[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex; i < toIndex; i++) { + array[i] ^= Integer.MAX_VALUE; + } + Arrays.sort(array, fromIndex, toIndex); + for (int i = fromIndex; i < toIndex; i++) { + array[i] ^= Integer.MAX_VALUE; + } + } + + /** + * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 32-bit + * quantities. + * + *

Java 8 users: use {@link Integer#divideUnsigned(int, int)} instead. + * + * @param dividend the dividend (numerator) + * @param divisor the divisor (denominator) + * @throws ArithmeticException if divisor is 0 + */ + public static int divide(int dividend, int divisor) { + return (int) (toLong(dividend) / toLong(divisor)); + } + + /** + * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 32-bit + * quantities. + * + *

Java 8 users: use {@link Integer#remainderUnsigned(int, int)} instead. + * + * @param dividend the dividend (numerator) + * @param divisor the divisor (denominator) + * @throws ArithmeticException if divisor is 0 + */ + public static int remainder(int dividend, int divisor) { + return (int) (toLong(dividend) % toLong(divisor)); + } + + /** + * Returns the unsigned {@code int} value represented by the given string. + * + *

Accepts a decimal, hexadecimal, or octal number given by specifying the following prefix: + * + *

    + *
  • {@code 0x}HexDigits + *
  • {@code 0X}HexDigits + *
  • {@code #}HexDigits + *
  • {@code 0}OctalDigits + *
+ * + * @throws NumberFormatException if the string does not contain a valid unsigned {@code int} value + * @since 13.0 + */ + public static int decode(String stringValue) { + ParseRequest request = ParseRequest.fromString(stringValue); + + try { + return parseUnsignedInt(request.rawValue, request.radix); + } catch (NumberFormatException e) { + NumberFormatException decodeException = + new NumberFormatException("Error parsing value: " + stringValue); + decodeException.initCause(e); + throw decodeException; + } + } + + /** + * Returns the unsigned {@code int} value represented by the given decimal string. + * + *

Java 8 users: use {@link Integer#parseUnsignedInt(String)} instead. + * + * @throws NumberFormatException if the string does not contain a valid unsigned {@code int} value + * @throws NullPointerException if {@code s} is null (in contrast to {@link + * Integer#parseInt(String)}) + */ + public static int parseUnsignedInt(String s) { + return parseUnsignedInt(s, 10); + } + + /** + * Returns the unsigned {@code int} value represented by a string with the given radix. + * + *

Java 8 users: use {@link Integer#parseUnsignedInt(String, int)} instead. + * + * @param string the string containing the unsigned integer representation to be parsed. + * @param radix the radix to use while parsing {@code s}; must be between {@link + * Character#MIN_RADIX} and {@link Character#MAX_RADIX}. + * @throws NumberFormatException if the string does not contain a valid unsigned {@code int}, or + * if supplied radix is invalid. + * @throws NullPointerException if {@code s} is null (in contrast to {@link + * Integer#parseInt(String)}) + */ + public static int parseUnsignedInt(String string, int radix) { + checkNotNull(string); + long result = Long.parseLong(string, radix); + if ((result & INT_MASK) != result) { + throw new NumberFormatException( + "Input " + string + " in base " + radix + " is not in the range of an unsigned integer"); + } + return (int) result; + } + + /** + * Returns a string representation of x, where x is treated as unsigned. + * + *

Java 8 users: use {@link Integer#toUnsignedString(int)} instead. + */ + public static String toString(int x) { + return toString(x, 10); + } + + /** + * Returns a string representation of {@code x} for the given radix, where {@code x} is treated as + * unsigned. + * + *

Java 8 users: use {@link Integer#toUnsignedString(int, int)} instead. + * + * @param x the value to convert to a string. + * @param radix the radix to use while working with {@code x} + * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} + * and {@link Character#MAX_RADIX}. + */ + public static String toString(int x, int radix) { + long asLong = x & INT_MASK; + return Long.toString(asLong, radix); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedLong.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedLong.java new file mode 100644 index 0000000000000..f5ad1b229eda5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedLong.java @@ -0,0 +1,267 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.io.Serializable; +import java.math.BigInteger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A wrapper class for unsigned {@code long} values, supporting arithmetic operations. + * + *

In some cases, when speed is more important than code readability, it may be faster simply to + * treat primitive {@code long} values as unsigned, using the methods from {@link UnsignedLongs}. + * + *

See the Guava User Guide article on unsigned + * primitive utilities. + * + * @author Louis Wasserman + * @author Colin Evans + * @since 11.0 + */ +@GwtCompatible(serializable = true) +@ElementTypesAreNonnullByDefault +public final class UnsignedLong extends Number implements Comparable, Serializable { + + private static final long UNSIGNED_MASK = 0x7fffffffffffffffL; + + public static final UnsignedLong ZERO = new UnsignedLong(0); + public static final UnsignedLong ONE = new UnsignedLong(1); + public static final UnsignedLong MAX_VALUE = new UnsignedLong(-1L); + + private final long value; + + private UnsignedLong(long value) { + this.value = value; + } + + /** + * Returns an {@code UnsignedLong} corresponding to a given bit representation. The argument is + * interpreted as an unsigned 64-bit value. Specifically, the sign bit of {@code bits} is + * interpreted as a normal bit, and all other bits are treated as usual. + * + *

If the argument is nonnegative, the returned result will be equal to {@code bits}, + * otherwise, the result will be equal to {@code 2^64 + bits}. + * + *

To represent decimal constants less than {@code 2^63}, consider {@link #valueOf(long)} + * instead. + * + * @since 14.0 + */ + public static UnsignedLong fromLongBits(long bits) { + // TODO(lowasser): consider caching small values, like Long.valueOf + return new UnsignedLong(bits); + } + + /** + * Returns an {@code UnsignedLong} representing the same value as the specified {@code long}. + * + * @throws IllegalArgumentException if {@code value} is negative + * @since 14.0 + */ + public static UnsignedLong valueOf(long value) { + checkArgument(value >= 0, "value (%s) is outside the range for an unsigned long value", value); + return fromLongBits(value); + } + + /** + * Returns a {@code UnsignedLong} representing the same value as the specified {@code BigInteger}. + * This is the inverse operation of {@link #bigIntegerValue()}. + * + * @throws IllegalArgumentException if {@code value} is negative or {@code value >= 2^64} + */ + public static UnsignedLong valueOf(BigInteger value) { + checkNotNull(value); + checkArgument( + value.signum() >= 0 && value.bitLength() <= Long.SIZE, + "value (%s) is outside the range for an unsigned long value", + value); + return fromLongBits(value.longValue()); + } + + /** + * Returns an {@code UnsignedLong} holding the value of the specified {@code String}, parsed as an + * unsigned {@code long} value. + * + * @throws NumberFormatException if the string does not contain a parsable unsigned {@code long} + * value + */ + public static UnsignedLong valueOf(String string) { + return valueOf(string, 10); + } + + /** + * Returns an {@code UnsignedLong} holding the value of the specified {@code String}, parsed as an + * unsigned {@code long} value in the specified radix. + * + * @throws NumberFormatException if the string does not contain a parsable unsigned {@code long} + * value, or {@code radix} is not between {@link Character#MIN_RADIX} and {@link + * Character#MAX_RADIX} + */ + public static UnsignedLong valueOf(String string, int radix) { + return fromLongBits(UnsignedLongs.parseUnsignedLong(string, radix)); + } + + /** + * Returns the result of adding this and {@code val}. If the result would have more than 64 bits, + * returns the low 64 bits of the result. + * + * @since 14.0 + */ + public UnsignedLong plus(UnsignedLong val) { + return fromLongBits(this.value + checkNotNull(val).value); + } + + /** + * Returns the result of subtracting this and {@code val}. If the result would have more than 64 + * bits, returns the low 64 bits of the result. + * + * @since 14.0 + */ + public UnsignedLong minus(UnsignedLong val) { + return fromLongBits(this.value - checkNotNull(val).value); + } + + /** + * Returns the result of multiplying this and {@code val}. If the result would have more than 64 + * bits, returns the low 64 bits of the result. + * + * @since 14.0 + */ + public UnsignedLong times(UnsignedLong val) { + return fromLongBits(value * checkNotNull(val).value); + } + + /** + * Returns the result of dividing this by {@code val}. + * + * @since 14.0 + */ + public UnsignedLong dividedBy(UnsignedLong val) { + return fromLongBits(UnsignedLongs.divide(value, checkNotNull(val).value)); + } + + /** + * Returns this modulo {@code val}. + * + * @since 14.0 + */ + public UnsignedLong mod(UnsignedLong val) { + return fromLongBits(UnsignedLongs.remainder(value, checkNotNull(val).value)); + } + + /** Returns the value of this {@code UnsignedLong} as an {@code int}. */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this {@code UnsignedLong} as a {@code long}. This is an inverse operation + * to {@link #fromLongBits}. + * + *

Note that if this {@code UnsignedLong} holds a value {@code >= 2^63}, the returned value + * will be equal to {@code this - 2^64}. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this {@code UnsignedLong} as a {@code float}, analogous to a widening + * primitive conversion from {@code long} to {@code float}, and correctly rounded. + */ + @Override + public float floatValue() { + if (value >= 0) { + return (float) value; + } + // The top bit is set, which means that the float value is going to come from the top 24 bits. + // So we can ignore the bottom 8, except for rounding. See doubleValue() for more. + return (float) ((value >>> 1) | (value & 1)) * 2f; + } + + /** + * Returns the value of this {@code UnsignedLong} as a {@code double}, analogous to a widening + * primitive conversion from {@code long} to {@code double}, and correctly rounded. + */ + @Override + public double doubleValue() { + if (value >= 0) { + return (double) value; + } + // The top bit is set, which means that the double value is going to come from the top 53 bits. + // So we can ignore the bottom 11, except for rounding. We can unsigned-shift right 1, aka + // unsigned-divide by 2, and convert that. Then we'll get exactly half of the desired double + // value. But in the specific case where the bottom two bits of the original number are 01, we + // want to replace that with 1 in the shifted value for correct rounding. + return (double) ((value >>> 1) | (value & 1)) * 2.0; + } + + /** Returns the value of this {@code UnsignedLong} as a {@link BigInteger}. */ + public BigInteger bigIntegerValue() { + BigInteger bigInt = BigInteger.valueOf(value & UNSIGNED_MASK); + if (value < 0) { + bigInt = bigInt.setBit(Long.SIZE - 1); + } + return bigInt; + } + + @Override + public int compareTo(UnsignedLong o) { + checkNotNull(o); + return UnsignedLongs.compare(value, o.value); + } + + @Override + public int hashCode() { + return Longs.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UnsignedLong) { + UnsignedLong other = (UnsignedLong) obj; + return value == other.value; + } + return false; + } + + /** Returns a string representation of the {@code UnsignedLong} value, in base 10. */ + @Override + public String toString() { + return UnsignedLongs.toString(value); + } + + /** + * Returns a string representation of the {@code UnsignedLong} value, in base {@code radix}. If + * {@code radix < Character.MIN_RADIX} or {@code radix > Character.MAX_RADIX}, the radix {@code + * 10} is used. + */ + public String toString(int radix) { + return UnsignedLongs.toString(value, radix); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedLongs.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedLongs.java new file mode 100644 index 0000000000000..ff4663e8d63ac --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/UnsignedLongs.java @@ -0,0 +1,501 @@ +/* + * 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.iotdb.google.common.primitives; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Comparator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkPositionIndexes; + +/** + * Static utility methods pertaining to {@code long} primitives that interpret values as + * unsigned (that is, any negative value {@code x} is treated as the positive value {@code + * 2^64 + x}). The methods for which signedness is not an issue are in {@link Longs}, as well as + * signed versions of methods for which signedness is an issue. + * + *

In addition, this class provides several static methods for converting a {@code long} to a + * {@code String} and a {@code String} to a {@code long} that treat the {@code long} as an unsigned + * number. + * + *

Users of these utilities must be extremely careful not to mix up signed and unsigned + * {@code long} values. When possible, it is recommended that the {@link UnsignedLong} wrapper class + * be used, at a small efficiency penalty, to enforce the distinction in the type system. + * + *

See the Guava User Guide article on unsigned + * primitive utilities. + * + * @author Louis Wasserman + * @author Brian Milch + * @author Colin Evans + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class UnsignedLongs { + private UnsignedLongs() {} + + public static final long MAX_VALUE = -1L; // Equivalent to 2^64 - 1 + + /** + * A (self-inverse) bijection which converts the ordering on unsigned longs to the ordering on + * longs, that is, {@code a <= b} as unsigned longs if and only if {@code flip(a) <= flip(b)} as + * signed longs. + */ + private static long flip(long a) { + return a ^ Long.MIN_VALUE; + } + + /** + * Compares the two specified {@code long} values, treating them as unsigned values between {@code + * 0} and {@code 2^64 - 1} inclusive. + * + *

Java 8 users: use {@link Long#compareUnsigned(long, long)} instead. + * + * @param a the first unsigned {@code long} to compare + * @param b the second unsigned {@code long} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + public static int compare(long a, long b) { + return Longs.compare(flip(a), flip(b)); + } + + /** + * Returns the least value present in {@code array}, treating values as unsigned. + * + * @param array a nonempty array of unsigned {@code long} values + * @return the value present in {@code array} that is less than or equal to every other value in + * the array according to {@link #compare} + * @throws IllegalArgumentException if {@code array} is empty + */ + public static long min(long... array) { + checkArgument(array.length > 0); + long min = flip(array[0]); + for (int i = 1; i < array.length; i++) { + long next = flip(array[i]); + if (next < min) { + min = next; + } + } + return flip(min); + } + + /** + * Returns the greatest value present in {@code array}, treating values as unsigned. + * + * @param array a nonempty array of unsigned {@code long} values + * @return the value present in {@code array} that is greater than or equal to every other value + * in the array according to {@link #compare} + * @throws IllegalArgumentException if {@code array} is empty + */ + public static long max(long... array) { + checkArgument(array.length > 0); + long max = flip(array[0]); + for (int i = 1; i < array.length; i++) { + long next = flip(array[i]); + if (next > max) { + max = next; + } + } + return flip(max); + } + + /** + * Returns a string containing the supplied unsigned {@code long} values separated by {@code + * separator}. For example, {@code join("-", 1, 2, 3)} returns the string {@code "1-2-3"}. + * + * @param separator the text that should appear between consecutive values in the resulting string + * (but not at the start or end) + * @param array an array of unsigned {@code long} values, possibly empty + */ + public static String join(String separator, long... array) { + checkNotNull(separator); + if (array.length == 0) { + return ""; + } + + // For pre-sizing a builder, just get the right order of magnitude + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(toString(array[0])); + for (int i = 1; i < array.length; i++) { + builder.append(separator).append(toString(array[i])); + } + return builder.toString(); + } + + /** + * Returns a comparator that compares two arrays of unsigned {@code long} values lexicographically. That is, it + * compares, using {@link #compare(long, long)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [1L] < [1L, 2L] < [2L] < [1L << 63]}. + * + *

The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays + * support only identity equality), but it is consistent with {@link Arrays#equals(long[], + * long[])}. + */ + public static Comparator lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + enum LexicographicalComparator implements Comparator { + INSTANCE; + + @Override + public int compare(long[] left, long[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + if (left[i] != right[i]) { + return UnsignedLongs.compare(left[i], right[i]); + } + } + return left.length - right.length; + } + + @Override + public String toString() { + return "UnsignedLongs.lexicographicalComparator()"; + } + } + + /** + * Sorts the array, treating its elements as unsigned 64-bit integers. + * + * @since 23.1 + */ + public static void sort(long[] array) { + checkNotNull(array); + sort(array, 0, array.length); + } + + /** + * Sorts the array between {@code fromIndex} inclusive and {@code toIndex} exclusive, treating its + * elements as unsigned 64-bit integers. + * + * @since 23.1 + */ + public static void sort(long[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex; i < toIndex; i++) { + array[i] = flip(array[i]); + } + Arrays.sort(array, fromIndex, toIndex); + for (int i = fromIndex; i < toIndex; i++) { + array[i] = flip(array[i]); + } + } + + /** + * Sorts the elements of {@code array} in descending order, interpreting them as unsigned 64-bit + * integers. + * + * @since 23.1 + */ + public static void sortDescending(long[] array) { + checkNotNull(array); + sortDescending(array, 0, array.length); + } + + /** + * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex} + * exclusive in descending order, interpreting them as unsigned 64-bit integers. + * + * @since 23.1 + */ + public static void sortDescending(long[] array, int fromIndex, int toIndex) { + checkNotNull(array); + checkPositionIndexes(fromIndex, toIndex, array.length); + for (int i = fromIndex; i < toIndex; i++) { + array[i] ^= Long.MAX_VALUE; + } + Arrays.sort(array, fromIndex, toIndex); + for (int i = fromIndex; i < toIndex; i++) { + array[i] ^= Long.MAX_VALUE; + } + } + + /** + * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 64-bit + * quantities. + * + *

Java 8 users: use {@link Long#divideUnsigned(long, long)} instead. + * + * @param dividend the dividend (numerator) + * @param divisor the divisor (denominator) + * @throws ArithmeticException if divisor is 0 + */ + public static long divide(long dividend, long divisor) { + if (divisor < 0) { // i.e., divisor >= 2^63: + if (compare(dividend, divisor) < 0) { + return 0; // dividend < divisor + } else { + return 1; // dividend >= divisor + } + } + + // Optimization - use signed division if dividend < 2^63 + if (dividend >= 0) { + return dividend / divisor; + } + + /* + * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is + * guaranteed to be either exact or one less than the correct value. This follows from fact that + * floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not quite + * trivial. + */ + long quotient = ((dividend >>> 1) / divisor) << 1; + long rem = dividend - quotient * divisor; + return quotient + (compare(rem, divisor) >= 0 ? 1 : 0); + } + + /** + * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 64-bit + * quantities. + * + *

Java 8 users: use {@link Long#remainderUnsigned(long, long)} instead. + * + * @param dividend the dividend (numerator) + * @param divisor the divisor (denominator) + * @throws ArithmeticException if divisor is 0 + * @since 11.0 + */ + public static long remainder(long dividend, long divisor) { + if (divisor < 0) { // i.e., divisor >= 2^63: + if (compare(dividend, divisor) < 0) { + return dividend; // dividend < divisor + } else { + return dividend - divisor; // dividend >= divisor + } + } + + // Optimization - use signed modulus if dividend < 2^63 + if (dividend >= 0) { + return dividend % divisor; + } + + /* + * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is + * guaranteed to be either exact or one less than the correct value. This follows from the fact + * that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not + * quite trivial. + */ + long quotient = ((dividend >>> 1) / divisor) << 1; + long rem = dividend - quotient * divisor; + return rem - (compare(rem, divisor) >= 0 ? divisor : 0); + } + + /** + * Returns the unsigned {@code long} value represented by the given decimal string. + * + *

Java 8 users: use {@link Long#parseUnsignedLong(String)} instead. + * + * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} + * value + * @throws NullPointerException if {@code string} is null (in contrast to {@link + * Long#parseLong(String)}) + */ + public static long parseUnsignedLong(String string) { + return parseUnsignedLong(string, 10); + } + + /** + * Returns the unsigned {@code long} value represented by a string with the given radix. + * + *

Java 8 users: use {@link Long#parseUnsignedLong(String, int)} instead. + * + * @param string the string containing the unsigned {@code long} representation to be parsed. + * @param radix the radix to use while parsing {@code string} + * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} with + * the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} and {@link + * Character#MAX_RADIX}. + * @throws NullPointerException if {@code string} is null (in contrast to {@link + * Long#parseLong(String)}) + */ + public static long parseUnsignedLong(String string, int radix) { + checkNotNull(string); + if (string.length() == 0) { + throw new NumberFormatException("empty string"); + } + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { + throw new NumberFormatException("illegal radix: " + radix); + } + + int maxSafePos = ParseOverflowDetection.maxSafeDigits[radix] - 1; + long value = 0; + for (int pos = 0; pos < string.length(); pos++) { + int digit = Character.digit(string.charAt(pos), radix); + if (digit == -1) { + throw new NumberFormatException(string); + } + if (pos > maxSafePos && ParseOverflowDetection.overflowInParse(value, digit, radix)) { + throw new NumberFormatException("Too large for unsigned long: " + string); + } + value = (value * radix) + digit; + } + + return value; + } + + /** + * Returns the unsigned {@code long} value represented by the given string. + * + *

Accepts a decimal, hexadecimal, or octal number given by specifying the following prefix: + * + *

    + *
  • {@code 0x}HexDigits + *
  • {@code 0X}HexDigits + *
  • {@code #}HexDigits + *
  • {@code 0}OctalDigits + *
+ * + * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} + * value + * @since 13.0 + */ + public static long decode(String stringValue) { + ParseRequest request = ParseRequest.fromString(stringValue); + + try { + return parseUnsignedLong(request.rawValue, request.radix); + } catch (NumberFormatException e) { + NumberFormatException decodeException = + new NumberFormatException("Error parsing value: " + stringValue); + decodeException.initCause(e); + throw decodeException; + } + } + + /* + * We move the static constants into this class so ProGuard can inline UnsignedLongs entirely + * unless the user is actually calling a parse method. + */ + private static final class ParseOverflowDetection { + private ParseOverflowDetection() {} + + // calculated as 0xffffffffffffffff / radix + static final long[] maxValueDivs = new long[Character.MAX_RADIX + 1]; + static final int[] maxValueMods = new int[Character.MAX_RADIX + 1]; + static final int[] maxSafeDigits = new int[Character.MAX_RADIX + 1]; + + static { + BigInteger overflow = new BigInteger("10000000000000000", 16); + for (int i = Character.MIN_RADIX; i <= Character.MAX_RADIX; i++) { + maxValueDivs[i] = divide(MAX_VALUE, i); + maxValueMods[i] = (int) remainder(MAX_VALUE, i); + maxSafeDigits[i] = overflow.toString(i).length() - 1; + } + } + + /** + * Returns true if (current * radix) + digit is a number too large to be represented by an + * unsigned long. This is useful for detecting overflow while parsing a string representation of + * a number. Does not verify whether supplied radix is valid, passing an invalid radix will give + * undefined results or an ArrayIndexOutOfBoundsException. + */ + static boolean overflowInParse(long current, int digit, int radix) { + if (current >= 0) { + if (current < maxValueDivs[radix]) { + return false; + } + if (current > maxValueDivs[radix]) { + return true; + } + // current == maxValueDivs[radix] + return (digit > maxValueMods[radix]); + } + + // current < 0: high bit is set + return true; + } + } + + /** + * Returns a string representation of x, where x is treated as unsigned. + * + *

Java 8 users: use {@link Long#toUnsignedString(long)} instead. + */ + public static String toString(long x) { + return toString(x, 10); + } + + /** + * Returns a string representation of {@code x} for the given radix, where {@code x} is treated as + * unsigned. + * + *

Java 8 users: use {@link Long#toUnsignedString(long, int)} instead. + * + * @param x the value to convert to a string. + * @param radix the radix to use while working with {@code x} + * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} + * and {@link Character#MAX_RADIX}. + */ + public static String toString(long x, int radix) { + checkArgument( + radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX, + "radix (%s) must be between Character.MIN_RADIX and Character.MAX_RADIX", + radix); + if (x == 0) { + // Simply return "0" + return "0"; + } else if (x > 0) { + return Long.toString(x, radix); + } else { + char[] buf = new char[64]; + int i = buf.length; + if ((radix & (radix - 1)) == 0) { + // Radix is a power of two so we can avoid division. + int shift = Integer.numberOfTrailingZeros(radix); + int mask = radix - 1; + do { + buf[--i] = Character.forDigit(((int) x) & mask, radix); + x >>>= shift; + } while (x != 0); + } else { + // Separate off the last digit using unsigned division. That will leave + // a number that is nonnegative as a signed integer. + long quotient; + if ((radix & 1) == 0) { + // Fast path for the usual case where the radix is even. + quotient = (x >>> 1) / (radix >>> 1); + } else { + quotient = divide(x, radix); + } + long rem = x - quotient * radix; + buf[--i] = Character.forDigit((int) rem, radix); + x = quotient; + // Simple modulo/division approach + while (x > 0) { + buf[--i] = Character.forDigit((int) (x % radix), radix); + x /= radix; + } + } + // Generate string + return new String(buf, i, buf.length - i); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/package-info.java new file mode 100644 index 0000000000000..9c2d2bec11a8f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/primitives/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.primitives; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/AbstractInvocationHandler.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/AbstractInvocationHandler.java new file mode 100644 index 0000000000000..1affbe1966a93 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/AbstractInvocationHandler.java @@ -0,0 +1,147 @@ +/* + * 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.iotdb.google.common.reflect; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; + +/** + * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, {@link + * Object#hashCode} and {@link Object#toString}. For example: + * + *

+ * class Unsupported extends AbstractInvocationHandler {
+ *   protected Object handleInvocation(Object proxy, Method method, Object[] args) {
+ *     throw new UnsupportedOperationException();
+ *   }
+ * }
+ *
+ * CharSequence unsupported = Reflection.newProxy(CharSequence.class, new Unsupported());
+ * 
+ * + * @author Ben Yu + * @since 12.0 + */ +@ElementTypesAreNonnullByDefault +public abstract class AbstractInvocationHandler implements InvocationHandler { + + private static final Object[] NO_ARGS = {}; + + /** + * {@inheritDoc} + * + *
    + *
  • {@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode} + *
  • {@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString} + *
  • {@code proxy.equals(argument)} returns true if: + *
      + *
    • {@code proxy} and {@code argument} are of the same type + *
    • and {@link AbstractInvocationHandler#equals} returns true for the {@link + * InvocationHandler} of {@code argument} + *
    + *
  • other method calls are dispatched to {@link #handleInvocation}. + *
+ */ + @Override + public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (args == null) { + args = NO_ARGS; + } + if (args.length == 0 && method.getName().equals("hashCode")) { + return hashCode(); + } + if (args.length == 1 + && method.getName().equals("equals") + && method.getParameterTypes()[0] == Object.class) { + Object arg = args[0]; + if (arg == null) { + return false; + } + if (proxy == arg) { + return true; + } + return isProxyOfSameInterfaces(arg, proxy.getClass()) + && equals(Proxy.getInvocationHandler(arg)); + } + if (args.length == 0 && method.getName().equals("toString")) { + return toString(); + } + return handleInvocation(proxy, method, args); + } + + /** + * {@link #invoke} delegates to this method upon any method invocation on the proxy instance, + * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result + * will be returned as the proxied method's return value. + * + *

Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, + * an empty array is passed in. + */ + protected abstract Object handleInvocation(Object proxy, Method method, Object[] args) + throws Throwable; + + /** + * By default delegates to {@link Object#equals} so instances are only equal if they are + * identical. {@code proxy.equals(argument)} returns true if: + * + *

    + *
  • {@code proxy} and {@code argument} are of the same type + *
  • and this method returns true for the {@link InvocationHandler} of {@code argument} + *
+ * + *

Subclasses can override this method to provide custom equality. + */ + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * By default delegates to {@link Object#hashCode}. The dynamic proxies' {@code hashCode()} will + * delegate to this method. Subclasses can override this method to provide custom equality. + */ + @Override + public int hashCode() { + return super.hashCode(); + } + + /** + * By default delegates to {@link Object#toString}. The dynamic proxies' {@code toString()} will + * delegate to this method. Subclasses can override this method to provide custom string + * representation for the proxies. + */ + @Override + public String toString() { + return super.toString(); + } + + private static boolean isProxyOfSameInterfaces(Object arg, Class proxyClass) { + return proxyClass.isInstance(arg) + // Equal proxy instances should mostly be instance of proxyClass + // Under some edge cases (such as the proxy of JDK types serialized and then deserialized) + // the proxy type may not be the same. + // We first check isProxyClass() so that the common case of comparing with non-proxy objects + // is efficient. + || (Proxy.isProxyClass(arg.getClass()) + && Arrays.equals(arg.getClass().getInterfaces(), proxyClass.getInterfaces())); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ClassPath.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ClassPath.java new file mode 100644 index 0000000000000..d733c19f18c8d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ClassPath.java @@ -0,0 +1,687 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.CharMatcher; +import org.apache.iotdb.google.common.base.Splitter; +import org.apache.iotdb.google.common.collect.FluentIterable; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.io.ByteSource; +import org.apache.iotdb.google.common.io.CharSource; +import org.apache.iotdb.google.common.io.Resources; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.Charset; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; +import static org.apache.iotdb.google.common.base.StandardSystemProperty.PATH_SEPARATOR; + +/** + * Scans the source of a {@link ClassLoader} and finds all loadable classes and resources. + * + *

Prefer ClassGraph over {@code + * ClassPath}

+ * + *

We recommend using ClassGraph + * instead of {@code ClassPath}. ClassGraph improves upon {@code ClassPath} in several ways, + * including addressing many of its limitations. Limitations of {@code ClassPath} include: + * + *

+ * + *

{@code ClassPath} and symlinks

+ * + *

In the case of directory classloaders, symlinks are supported but cycles are not traversed. + * This guarantees discovery of each unique loadable resource. However, not all possible + * aliases for resources on cyclic paths will be listed. + * + * @author Ben Yu + * @since 14.0 + */ +@ElementTypesAreNonnullByDefault +public final class ClassPath { + private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); + + /** Separator for the Class-Path manifest attribute value in jar files. */ + private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = + Splitter.on(" ").omitEmptyStrings(); + + private static final String CLASS_FILE_NAME_EXTENSION = ".class"; + + private final ImmutableSet resources; + + private ClassPath(ImmutableSet resources) { + this.resources = resources; + } + + /** + * Returns a {@code ClassPath} representing all classes and resources loadable from {@code + * classloader} and its ancestor class loaders. + * + *

Warning: {@code ClassPath} can find classes and resources only from: + * + *

    + *
  • {@link URLClassLoader} instances' {@code file:} URLs + *
  • the {@linkplain ClassLoader#getSystemClassLoader() system class loader}. To search the + * system class loader even when it is not a {@link URLClassLoader} (as in Java 9), {@code + * ClassPath} searches the files from the {@code java.class.path} system property. + *
+ * + * @throws IOException if the attempt to read class path resources (jar files or directories) + * failed. + */ + public static ClassPath from(ClassLoader classloader) throws IOException { + ImmutableSet locations = locationsFrom(classloader); + + // Add all locations to the scanned set so that in a classpath [jar1, jar2], where jar1 has a + // manifest with Class-Path pointing to jar2, we won't scan jar2 twice. + Set scanned = new HashSet<>(); + for (LocationInfo location : locations) { + scanned.add(location.file()); + } + + // Scan all locations + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (LocationInfo location : locations) { + builder.addAll(location.scanResources(scanned)); + } + return new ClassPath(builder.build()); + } + + /** + * Returns all resources loadable from the current class path, including the class files of all + * loadable classes but excluding the "META-INF/MANIFEST.MF" file. + */ + public ImmutableSet getResources() { + return resources; + } + + /** + * Returns all classes loadable from the current class path. + * + * @since 16.0 + */ + public ImmutableSet getAllClasses() { + return FluentIterable.from(resources).filter(ClassInfo.class).toSet(); + } + + /** + * Returns all top level classes loadable from the current class path. Note that "top-level-ness" + * is determined heuristically by class name (see {@link ClassInfo#isTopLevel}). + */ + public ImmutableSet getTopLevelClasses() { + return FluentIterable.from(resources) + .filter(ClassInfo.class) + .filter(ClassInfo::isTopLevel) + .toSet(); + } + + /** Returns all top level classes whose package name is {@code packageName}. */ + public ImmutableSet getTopLevelClasses(String packageName) { + checkNotNull(packageName); + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (ClassInfo classInfo : getTopLevelClasses()) { + if (classInfo.getPackageName().equals(packageName)) { + builder.add(classInfo); + } + } + return builder.build(); + } + + /** + * Returns all top level classes whose package name is {@code packageName} or starts with {@code + * packageName} followed by a '.'. + */ + public ImmutableSet getTopLevelClassesRecursive(String packageName) { + checkNotNull(packageName); + String packagePrefix = packageName + '.'; + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (ClassInfo classInfo : getTopLevelClasses()) { + if (classInfo.getName().startsWith(packagePrefix)) { + builder.add(classInfo); + } + } + return builder.build(); + } + + /** + * Represents a class path resource that can be either a class file or any other resource file + * loadable from the class path. + * + * @since 14.0 + */ + public static class ResourceInfo { + private final File file; + private final String resourceName; + + final ClassLoader loader; + + static ResourceInfo of(File file, String resourceName, ClassLoader loader) { + if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) { + return new ClassInfo(file, resourceName, loader); + } else { + return new ResourceInfo(file, resourceName, loader); + } + } + + ResourceInfo(File file, String resourceName, ClassLoader loader) { + this.file = checkNotNull(file); + this.resourceName = checkNotNull(resourceName); + this.loader = checkNotNull(loader); + } + + /** + * Returns the url identifying the resource. + * + *

See {@link ClassLoader#getResource} + * + * @throws NoSuchElementException if the resource cannot be loaded through the class loader, + * despite physically existing in the class path. + */ + public final URL url() { + URL url = loader.getResource(resourceName); + if (url == null) { + throw new NoSuchElementException(resourceName); + } + return url; + } + + /** + * Returns a {@link ByteSource} view of the resource from which its bytes can be read. + * + * @throws NoSuchElementException if the resource cannot be loaded through the class loader, + * despite physically existing in the class path. + * @since 20.0 + */ + public final ByteSource asByteSource() { + return Resources.asByteSource(url()); + } + + /** + * Returns a {@link CharSource} view of the resource from which its bytes can be read as + * characters decoded with the given {@code charset}. + * + * @throws NoSuchElementException if the resource cannot be loaded through the class loader, + * despite physically existing in the class path. + * @since 20.0 + */ + public final CharSource asCharSource(Charset charset) { + return Resources.asCharSource(url(), charset); + } + + /** Returns the fully qualified name of the resource. Such as "com/mycomp/foo/bar.txt". */ + public final String getResourceName() { + return resourceName; + } + + /** Returns the file that includes this resource. */ + final File getFile() { + return file; + } + + @Override + public int hashCode() { + return resourceName.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ResourceInfo) { + ResourceInfo that = (ResourceInfo) obj; + return resourceName.equals(that.resourceName) && loader == that.loader; + } + return false; + } + + // Do not change this arbitrarily. We rely on it for sorting ResourceInfo. + @Override + public String toString() { + return resourceName; + } + } + + /** + * Represents a class that can be loaded through {@link #load}. + * + * @since 14.0 + */ + public static final class ClassInfo extends ResourceInfo { + private final String className; + + ClassInfo(File file, String resourceName, ClassLoader loader) { + super(file, resourceName, loader); + this.className = getClassName(resourceName); + } + + /** + * Returns the package name of the class, without attempting to load the class. + * + *

Behaves similarly to {@code class.getPackage().}{@link Package#getName() getName()} but + * does not require the class (or package) to be loaded. + * + *

But note that this method may behave differently for a class in the default package: For + * such classes, this method always returns an empty string. But under some version of Java, + * {@code class.getPackage().getName()} produces a {@code NullPointerException} because {@code + * class.getPackage()} returns {@code null}. + */ + public String getPackageName() { + return Reflection.getPackageName(className); + } + + /** + * Returns the simple name of the underlying class as given in the source code. + * + *

Behaves similarly to {@link Class#getSimpleName()} but does not require the class to be + * loaded. + * + *

But note that this class uses heuristics to identify the simple name. See a related + * discussion in issue 3349. + */ + public String getSimpleName() { + int lastDollarSign = className.lastIndexOf('$'); + if (lastDollarSign != -1) { + String innerClassName = className.substring(lastDollarSign + 1); + // local and anonymous classes are prefixed with number (1,2,3...), anonymous classes are + // entirely numeric whereas local classes have the user supplied name as a suffix + return CharMatcher.inRange('0', '9').trimLeadingFrom(innerClassName); + } + String packageName = getPackageName(); + if (packageName.isEmpty()) { + return className; + } + + // Since this is a top level class, its simple name is always the part after package name. + return className.substring(packageName.length() + 1); + } + + /** + * Returns the fully qualified name of the class. + * + *

Behaves identically to {@link Class#getName()} but does not require the class to be + * loaded. + */ + public String getName() { + return className; + } + + /** + * Returns true if the class name "looks to be" top level (not nested), that is, it includes no + * '$' in the name. This method may return false for a top-level class that's intentionally + * named with the '$' character. If this is a concern, you could use {@link #load} and then + * check on the loaded {@link Class} object instead. + * + * @since 30.1 + */ + public boolean isTopLevel() { + return className.indexOf('$') == -1; + } + + /** + * Loads (but doesn't link or initialize) the class. + * + * @throws LinkageError when there were errors in loading classes that this class depends on. + * For example, {@link NoClassDefFoundError}. + */ + public Class load() { + try { + return loader.loadClass(className); + } catch (ClassNotFoundException e) { + // Shouldn't happen, since the class name is read from the class path. + throw new IllegalStateException(e); + } + } + + @Override + public String toString() { + return className; + } + } + + /** + * Returns all locations that {@code classloader} and parent loaders load classes and resources + * from. Callers can {@linkplain LocationInfo#scanResources scan} individual locations selectively + * or even in parallel. + */ + static ImmutableSet locationsFrom(ClassLoader classloader) { + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (Map.Entry entry : getClassPathEntries(classloader).entrySet()) { + builder.add(new LocationInfo(entry.getKey(), entry.getValue())); + } + return builder.build(); + } + + /** + * Represents a single location (a directory or a jar file) in the class path and is responsible + * for scanning resources from this location. + */ + static final class LocationInfo { + final File home; + private final ClassLoader classloader; + + LocationInfo(File home, ClassLoader classloader) { + this.home = checkNotNull(home); + this.classloader = checkNotNull(classloader); + } + + /** Returns the file this location is from. */ + public final File file() { + return home; + } + + /** Scans this location and returns all scanned resources. */ + public ImmutableSet scanResources() throws IOException { + return scanResources(new HashSet()); + } + + /** + * Scans this location and returns all scanned resources. + * + *

This file and jar files from "Class-Path" entry in the scanned manifest files will be + * added to {@code scannedFiles}. + * + *

A file will be scanned at most once even if specified multiple times by one or multiple + * jar files' "Class-Path" manifest entries. Particularly, if a jar file from the "Class-Path" + * manifest entry is already in {@code scannedFiles}, either because it was scanned earlier, or + * it was intentionally added to the set by the caller, it will not be scanned again. + * + *

Note that when you call {@code location.scanResources(scannedFiles)}, the location will + * always be scanned even if {@code scannedFiles} already contains it. + */ + public ImmutableSet scanResources(Set scannedFiles) throws IOException { + ImmutableSet.Builder builder = ImmutableSet.builder(); + scannedFiles.add(home); + scan(home, scannedFiles, builder); + return builder.build(); + } + + private void scan(File file, Set scannedUris, ImmutableSet.Builder builder) + throws IOException { + try { + if (!file.exists()) { + return; + } + } catch (SecurityException e) { + logger.warning("Cannot access " + file + ": " + e); + // TODO(emcmanus): consider whether to log other failure cases too. + return; + } + if (file.isDirectory()) { + scanDirectory(file, builder); + } else { + scanJar(file, scannedUris, builder); + } + } + + private void scanJar( + File file, Set scannedUris, ImmutableSet.Builder builder) + throws IOException { + JarFile jarFile; + try { + jarFile = new JarFile(file); + } catch (IOException e) { + // Not a jar file + return; + } + try { + for (File path : getClassPathFromManifest(file, jarFile.getManifest())) { + // We only scan each file once independent of the classloader that file might be + // associated with. + if (scannedUris.add(path.getCanonicalFile())) { + scan(path, scannedUris, builder); + } + } + scanJarFile(jarFile, builder); + } finally { + try { + jarFile.close(); + } catch (IOException ignored) { // similar to try-with-resources, but don't fail scanning + } + } + } + + private void scanJarFile(JarFile file, ImmutableSet.Builder builder) { + Enumeration entries = file.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) { + continue; + } + builder.add(ResourceInfo.of(new File(file.getName()), entry.getName(), classloader)); + } + } + + private void scanDirectory(File directory, ImmutableSet.Builder builder) + throws IOException { + Set currentPath = new HashSet<>(); + currentPath.add(directory.getCanonicalFile()); + scanDirectory(directory, "", currentPath, builder); + } + + /** + * Recursively scan the given directory, adding resources for each file encountered. Symlinks + * which have already been traversed in the current tree path will be skipped to eliminate + * cycles; otherwise symlinks are traversed. + * + * @param directory the root of the directory to scan + * @param packagePrefix resource path prefix inside {@code classloader} for any files found + * under {@code directory} + * @param currentPath canonical files already visited in the current directory tree path, for + * cycle elimination + */ + private void scanDirectory( + File directory, + String packagePrefix, + Set currentPath, + ImmutableSet.Builder builder) + throws IOException { + File[] files = directory.listFiles(); + if (files == null) { + logger.warning("Cannot read directory " + directory); + // IO error, just skip the directory + return; + } + for (File f : files) { + String name = f.getName(); + if (f.isDirectory()) { + File deref = f.getCanonicalFile(); + if (currentPath.add(deref)) { + scanDirectory(deref, packagePrefix + name + "/", currentPath, builder); + currentPath.remove(deref); + } + } else { + String resourceName = packagePrefix + name; + if (!resourceName.equals(JarFile.MANIFEST_NAME)) { + builder.add(ResourceInfo.of(f, resourceName, classloader)); + } + } + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof LocationInfo) { + LocationInfo that = (LocationInfo) obj; + return home.equals(that.home) && classloader.equals(that.classloader); + } + return false; + } + + @Override + public int hashCode() { + return home.hashCode(); + } + + @Override + public String toString() { + return home.toString(); + } + } + + /** + * Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according + * to JAR + * File Specification. If {@code manifest} is null, it means the jar file has no manifest, and + * an empty set will be returned. + */ + @VisibleForTesting + static ImmutableSet getClassPathFromManifest(File jarFile, Manifest manifest) { + if (manifest == null) { + return ImmutableSet.of(); + } + ImmutableSet.Builder builder = ImmutableSet.builder(); + String classpathAttribute = + manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString()); + if (classpathAttribute != null) { + for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) { + URL url; + try { + url = getClassPathEntry(jarFile, path); + } catch (MalformedURLException e) { + // Ignore bad entry + logger.warning("Invalid Class-Path entry: " + path); + continue; + } + if (url.getProtocol().equals("file")) { + builder.add(toFile(url)); + } + } + } + return builder.build(); + } + + @VisibleForTesting + static ImmutableMap getClassPathEntries(ClassLoader classloader) { + LinkedHashMap entries = Maps.newLinkedHashMap(); + // Search parent first, since it's the order ClassLoader#loadClass() uses. + ClassLoader parent = classloader.getParent(); + if (parent != null) { + entries.putAll(getClassPathEntries(parent)); + } + for (URL url : getClassLoaderUrls(classloader)) { + if (url.getProtocol().equals("file")) { + File file = toFile(url); + if (!entries.containsKey(file)) { + entries.put(file, classloader); + } + } + } + return ImmutableMap.copyOf(entries); + } + + private static ImmutableList getClassLoaderUrls(ClassLoader classloader) { + if (classloader instanceof URLClassLoader) { + return ImmutableList.copyOf(((URLClassLoader) classloader).getURLs()); + } + if (classloader.equals(ClassLoader.getSystemClassLoader())) { + return parseJavaClassPath(); + } + return ImmutableList.of(); + } + + /** + * Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain + * System#getProperty system property}. + */ + @VisibleForTesting // TODO(b/65488446): Make this a public API. + static ImmutableList parseJavaClassPath() { + ImmutableList.Builder urls = ImmutableList.builder(); + for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) { + try { + try { + urls.add(new File(entry).toURI().toURL()); + } catch (SecurityException e) { // File.toURI checks to see if the file is a directory + urls.add(new URL("file", null, new File(entry).getAbsolutePath())); + } + } catch (MalformedURLException e) { + logger.log(WARNING, "malformed classpath entry: " + entry, e); + } + } + return urls.build(); + } + + /** + * Returns the absolute uri of the Class-Path entry value as specified in JAR + * File Specification. Even though the specification only talks about relative urls, absolute + * urls are actually supported too (for example, in Maven surefire plugin). + */ + @VisibleForTesting + static URL getClassPathEntry(File jarFile, String path) throws MalformedURLException { + return new URL(jarFile.toURI().toURL(), path); + } + + @VisibleForTesting + static String getClassName(String filename) { + int classNameEnd = filename.length() - CLASS_FILE_NAME_EXTENSION.length(); + return filename.substring(0, classNameEnd).replace('/', '.'); + } + + // TODO(benyu): Try java.nio.file.Paths#get() when Guava drops JDK 6 support. + @VisibleForTesting + static File toFile(URL url) { + checkArgument(url.getProtocol().equals("file")); + try { + return new File(url.toURI()); // Accepts escaped characters like %20. + } catch (URISyntaxException e) { // URL.toURI() doesn't escape chars. + return new File(url.getPath()); // Accepts non-escaped chars like space. + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..8bfe6602d1da8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/IgnoreJRERequirement.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/IgnoreJRERequirement.java new file mode 100644 index 0000000000000..c7f5c9076edcb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/IgnoreJRERequirement.java @@ -0,0 +1,35 @@ +/* + * 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.iotdb.google.common.reflect; + +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +/** + * Disables Animal Sniffer's checking of compatibility with older versions of Java/Android. + * + *

Each package's copy of this annotation needs to be listed in our {@code pom.xml}. + */ +@Target({METHOD, CONSTRUCTOR, TYPE}) +@ElementTypesAreNonnullByDefault +@interface IgnoreJRERequirement {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ImmutableTypeToInstanceMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ImmutableTypeToInstanceMap.java new file mode 100644 index 0000000000000..c260503e965f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ImmutableTypeToInstanceMap.java @@ -0,0 +1,171 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.collect.ForwardingMap; +import org.apache.iotdb.google.common.collect.ImmutableMap; + +import java.util.Map; + +/** + * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link + * MutableTypeToInstanceMap}. + * + * @author Ben Yu + * @since 13.0 + */ +@ElementTypesAreNonnullByDefault +public final class ImmutableTypeToInstanceMap extends ForwardingMap, B> + implements TypeToInstanceMap { + + /** Returns an empty type to instance map. */ + public static ImmutableTypeToInstanceMap of() { + return new ImmutableTypeToInstanceMap<>(ImmutableMap., B>of()); + } + + /** Returns a new builder. */ + public static Builder builder() { + return new Builder<>(); + } + + /** + * A builder for creating immutable type-to-instance maps. Example: + * + *

{@code
+   * static final ImmutableTypeToInstanceMap> HANDLERS =
+   *     ImmutableTypeToInstanceMap.>builder()
+   *         .put(new TypeToken>() {}, new FooHandler())
+   *         .put(new TypeToken>() {}, new SubBarHandler())
+   *         .build();
+   * }
+ * + *

After invoking {@link #build()} it is still possible to add more entries and build again. + * Thus each map generated by this builder will be a superset of any map generated before it. + * + * @since 13.0 + */ + public static final class Builder { + private final ImmutableMap.Builder, B> mapBuilder = + ImmutableMap.builder(); + + private Builder() {} + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed, + * and will cause {@link #build} to fail. + */ + public Builder put(Class key, T value) { + mapBuilder.put(TypeToken.of(key), value); + return this; + } + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate keys are not allowed, + * and will cause {@link #build} to fail. + */ + public Builder put(TypeToken key, T value) { + mapBuilder.put(key.rejectTypeVariables(), value); + return this; + } + + /** + * Returns a new immutable type-to-instance map containing the entries provided to this builder. + * + * @throws IllegalArgumentException if duplicate keys were added + */ + public ImmutableTypeToInstanceMap build() { + return new ImmutableTypeToInstanceMap<>(mapBuilder.buildOrThrow()); + } + } + + private final ImmutableMap, B> delegate; + + private ImmutableTypeToInstanceMap(ImmutableMap, B> delegate) { + this.delegate = delegate; + } + + @Override + public T getInstance(TypeToken type) { + return trustedGet(type.rejectTypeVariables()); + } + + @Override + public T getInstance(Class type) { + return trustedGet(TypeToken.of(type)); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @deprecated unsupported operation + * @throws UnsupportedOperationException always + */ + @Deprecated + @Override + public T putInstance(TypeToken type, T value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @deprecated unsupported operation + * @throws UnsupportedOperationException always + */ + @Deprecated + @Override + public T putInstance(Class type, T value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @deprecated unsupported operation + * @throws UnsupportedOperationException always + */ + @Deprecated + @Override + public B put(TypeToken key, B value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @deprecated unsupported operation + * @throws UnsupportedOperationException always + */ + @Deprecated + @Override + public void putAll(Map, ? extends B> map) { + throw new UnsupportedOperationException(); + } + + @Override + protected Map, B> delegate() { + return delegate; + } + + @SuppressWarnings("unchecked") // value could not get in if not a T + private T trustedGet(TypeToken type) { + return (T) delegate.get(type); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Invokable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Invokable.java new file mode 100644 index 0000000000000..1c60596f3d83c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Invokable.java @@ -0,0 +1,569 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.collect.ImmutableList; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Wrapper around either a {@link Method} or a {@link Constructor}. Convenience API is provided to + * make common reflective operation easier to deal with, such as {@link #isPublic}, {@link + * #getParameters} etc. + * + *

In addition to convenience methods, {@link TypeToken#method} and {@link TypeToken#constructor} + * will resolve the type parameters of the method or constructor in the context of the owner type, + * which may be a subtype of the declaring class. For example: + * + *

{@code
+ * Method getMethod = List.class.getMethod("get", int.class);
+ * Invokable, ?> invokable = new TypeToken>() {}.method(getMethod);
+ * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class!
+ * assertEquals(new TypeToken>() {}, invokable.getOwnerType());
+ * }
+ * + *

Note: earlier versions of this class inherited from {@link + * java.lang.reflect.AccessibleObject AccessibleObject} and {@link + * java.lang.reflect.GenericDeclaration GenericDeclaration}. Since version 31.0 that is no longer + * the case. However, most methods from those types are present with the same signature in this + * class. + * + * @param the type that owns this method or constructor. + * @param the return type of (or supertype thereof) the method or the declaring type of the + * constructor. + * @author Ben Yu + * @since 14.0 (no longer implements {@link AccessibleObject} or {@code GenericDeclaration} since + * 31.0) + */ +@ElementTypesAreNonnullByDefault +public abstract class Invokable implements AnnotatedElement, Member { + private final AccessibleObject accessibleObject; + private final Member member; + + Invokable(M member) { + checkNotNull(member); + this.accessibleObject = member; + this.member = member; + } + + /** Returns {@link Invokable} of {@code method}. */ + public static Invokable from(Method method) { + return new MethodInvokable<>(method); + } + + /** Returns {@link Invokable} of {@code constructor}. */ + public static Invokable from(Constructor constructor) { + return new ConstructorInvokable(constructor); + } + + @Override + public final boolean isAnnotationPresent(Class annotationClass) { + return accessibleObject.isAnnotationPresent(annotationClass); + } + + @Override + public final A getAnnotation(Class annotationClass) { + return accessibleObject.getAnnotation(annotationClass); + } + + @Override + public final Annotation[] getAnnotations() { + return accessibleObject.getAnnotations(); + } + + @Override + public final Annotation[] getDeclaredAnnotations() { + return accessibleObject.getDeclaredAnnotations(); + } + + // We ought to be able to implement GenericDeclaration instead its parent AnnotatedElement. + // That would give us this method declaration. But for some reason, implementing + // GenericDeclaration leads to weird errors in Android tests: + // IncompatibleClassChangeError: interface not implemented + /** See {@link java.lang.reflect.GenericDeclaration#getTypeParameters()}. */ + public abstract TypeVariable[] getTypeParameters(); + + /** See {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}. */ + public final void setAccessible(boolean flag) { + accessibleObject.setAccessible(flag); + } + + /** See {@link java.lang.reflect.AccessibleObject#trySetAccessible()}. */ + public final boolean trySetAccessible() { + // We can't call accessibleObject.trySetAccessible since that was added in Java 9 and this code + // should work on Java 8. So we emulate it this way. + try { + accessibleObject.setAccessible(true); + return true; + } catch (RuntimeException e) { + return false; + } + } + + /** See {@link java.lang.reflect.AccessibleObject#isAccessible()}. */ + public final boolean isAccessible() { + return accessibleObject.isAccessible(); + } + + @Override + public final String getName() { + return member.getName(); + } + + @Override + public final int getModifiers() { + return member.getModifiers(); + } + + @Override + public final boolean isSynthetic() { + return member.isSynthetic(); + } + + /** Returns true if the element is public. */ + public final boolean isPublic() { + return Modifier.isPublic(getModifiers()); + } + + /** Returns true if the element is protected. */ + public final boolean isProtected() { + return Modifier.isProtected(getModifiers()); + } + + /** Returns true if the element is package-private. */ + public final boolean isPackagePrivate() { + return !isPrivate() && !isPublic() && !isProtected(); + } + + /** Returns true if the element is private. */ + public final boolean isPrivate() { + return Modifier.isPrivate(getModifiers()); + } + + /** Returns true if the element is static. */ + public final boolean isStatic() { + return Modifier.isStatic(getModifiers()); + } + + /** + * Returns {@code true} if this method is final, per {@code Modifier.isFinal(getModifiers())}. + * + *

Note that a method may still be effectively "final", or non-overridable when it has no + * {@code final} keyword. For example, it could be private, or it could be declared by a final + * class. To tell whether a method is overridable, use {@link Invokable#isOverridable}. + */ + public final boolean isFinal() { + return Modifier.isFinal(getModifiers()); + } + + /** Returns true if the method is abstract. */ + public final boolean isAbstract() { + return Modifier.isAbstract(getModifiers()); + } + + /** Returns true if the element is native. */ + public final boolean isNative() { + return Modifier.isNative(getModifiers()); + } + + /** Returns true if the method is synchronized. */ + public final boolean isSynchronized() { + return Modifier.isSynchronized(getModifiers()); + } + + /** Returns true if the field is volatile. */ + final boolean isVolatile() { + return Modifier.isVolatile(getModifiers()); + } + + /** Returns true if the field is transient. */ + final boolean isTransient() { + return Modifier.isTransient(getModifiers()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Invokable) { + Invokable that = (Invokable) obj; + return getOwnerType().equals(that.getOwnerType()) && member.equals(that.member); + } + return false; + } + + @Override + public int hashCode() { + return member.hashCode(); + } + + @Override + public String toString() { + return member.toString(); + } + + /** + * Returns {@code true} if this is an overridable method. Constructors, private, static or final + * methods, or methods declared by final classes are not overridable. + */ + public abstract boolean isOverridable(); + + /** Returns {@code true} if this was declared to take a variable number of arguments. */ + public abstract boolean isVarArgs(); + + /** + * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method and + * returns the return value; or calls the underlying constructor with {@code args} and returns the + * constructed instance. + * + * @throws IllegalAccessException if this {@code Constructor} object enforces Java language access + * control and the underlying method or constructor is inaccessible. + * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an + * unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a + * parameter value cannot be converted to the corresponding formal parameter type by a method + * invocation conversion. + * @throws InvocationTargetException if the underlying method or constructor throws an exception. + */ + // All subclasses are owned by us and we'll make sure to get the R type right, including nullness. + @SuppressWarnings({"unchecked", "nullness"}) + public final R invoke(T receiver, Object... args) + throws InvocationTargetException, IllegalAccessException { + return (R) invokeInternal(receiver, checkNotNull(args)); + } + + /** Returns the return type of this {@code Invokable}. */ + // All subclasses are owned by us and we'll make sure to get the R type right. + @SuppressWarnings("unchecked") + public final TypeToken getReturnType() { + return (TypeToken) TypeToken.of(getGenericReturnType()); + } + + /** + * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor + * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden {@code + * this} parameter of the enclosing class is excluded from the returned parameters. + */ + @IgnoreJRERequirement + public final ImmutableList getParameters() { + Type[] parameterTypes = getGenericParameterTypes(); + Annotation[][] annotations = getParameterAnnotations(); + Object[] annotatedTypes = + ANNOTATED_TYPE_EXISTS ? getAnnotatedParameterTypes() : new Object[parameterTypes.length]; + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < parameterTypes.length; i++) { + builder.add( + new Parameter( + this, i, TypeToken.of(parameterTypes[i]), annotations[i], annotatedTypes[i])); + } + return builder.build(); + } + + /** Returns all declared exception types of this {@code Invokable}. */ + public final ImmutableList> getExceptionTypes() { + ImmutableList.Builder> builder = ImmutableList.builder(); + for (Type type : getGenericExceptionTypes()) { + // getGenericExceptionTypes() will never return a type that's not exception + @SuppressWarnings("unchecked") + TypeToken exceptionType = + (TypeToken) TypeToken.of(type); + builder.add(exceptionType); + } + return builder.build(); + } + + /** + * Explicitly specifies the return type of this {@code Invokable}. For example: + * + *

{@code
+   * Method factoryMethod = Person.class.getMethod("create");
+   * Invokable factory = Invokable.of(getNameMethod).returning(Person.class);
+   * }
+ */ + public final Invokable returning(Class returnType) { + return returning(TypeToken.of(returnType)); + } + + /** Explicitly specifies the return type of this {@code Invokable}. */ + public final Invokable returning(TypeToken returnType) { + if (!returnType.isSupertypeOf(getReturnType())) { + throw new IllegalArgumentException( + "Invokable is known to return " + getReturnType() + ", not " + returnType); + } + @SuppressWarnings("unchecked") // guarded by previous check + Invokable specialized = (Invokable) this; + return specialized; + } + + @SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes. + @Override + public final Class getDeclaringClass() { + return (Class) member.getDeclaringClass(); + } + + /** Returns the type of {@code T}. */ + // Overridden in TypeToken#method() and TypeToken#constructor() + @SuppressWarnings("unchecked") // The declaring class is T. + public TypeToken getOwnerType() { + return (TypeToken) TypeToken.of(getDeclaringClass()); + } + + abstract Object invokeInternal(Object receiver, Object[] args) + throws InvocationTargetException, IllegalAccessException; + + abstract Type[] getGenericParameterTypes(); + + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker"}) + @IgnoreJRERequirement + abstract AnnotatedType[] getAnnotatedParameterTypes(); + + /** This should never return a type that's not a subtype of Throwable. */ + abstract Type[] getGenericExceptionTypes(); + + abstract Annotation[][] getParameterAnnotations(); + + abstract Type getGenericReturnType(); + + /** + * Returns the {@link AnnotatedType} for the return type. + * + *

This method will fail if run under an Android VM. + * + * @since 14.0 for guava-jre (available since 32.0.0 in guava-android) + */ + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker"}) + @IgnoreJRERequirement + public abstract AnnotatedType getAnnotatedReturnType(); + + static class MethodInvokable extends Invokable { + + final Method method; + + MethodInvokable(Method method) { + super(method); + this.method = method; + } + + @Override + final Object invokeInternal(Object receiver, Object[] args) + throws InvocationTargetException, IllegalAccessException { + return method.invoke(receiver, args); + } + + @Override + Type getGenericReturnType() { + return method.getGenericReturnType(); + } + + @Override + Type[] getGenericParameterTypes() { + return method.getGenericParameterTypes(); + } + + @Override + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker"}) + @IgnoreJRERequirement + AnnotatedType[] getAnnotatedParameterTypes() { + return method.getAnnotatedParameterTypes(); + } + + @Override + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker", "DoNotCall"}) + @IgnoreJRERequirement + public AnnotatedType getAnnotatedReturnType() { + return method.getAnnotatedReturnType(); + } + + @Override + Type[] getGenericExceptionTypes() { + return method.getGenericExceptionTypes(); + } + + @Override + final Annotation[][] getParameterAnnotations() { + return method.getParameterAnnotations(); + } + + @Override + public final TypeVariable[] getTypeParameters() { + return method.getTypeParameters(); + } + + @Override + public final boolean isOverridable() { + return !(isFinal() + || isPrivate() + || isStatic() + || Modifier.isFinal(getDeclaringClass().getModifiers())); + } + + @Override + public final boolean isVarArgs() { + return method.isVarArgs(); + } + } + + static class ConstructorInvokable extends Invokable { + + final Constructor constructor; + + ConstructorInvokable(Constructor constructor) { + super(constructor); + this.constructor = constructor; + } + + @Override + final Object invokeInternal(Object receiver, Object[] args) + throws InvocationTargetException, IllegalAccessException { + try { + return constructor.newInstance(args); + } catch (InstantiationException e) { + throw new RuntimeException(constructor + " failed.", e); + } + } + + /** + * If the class is parameterized, such as {@link java.util.ArrayList ArrayList}, this returns + * {@code ArrayList}. + */ + @Override + Type getGenericReturnType() { + Class declaringClass = getDeclaringClass(); + TypeVariable[] typeParams = declaringClass.getTypeParameters(); + if (typeParams.length > 0) { + return Types.newParameterizedType(declaringClass, typeParams); + } else { + return declaringClass; + } + } + + @Override + Type[] getGenericParameterTypes() { + Type[] types = constructor.getGenericParameterTypes(); + if (types.length > 0 && mayNeedHiddenThis()) { + Class[] rawParamTypes = constructor.getParameterTypes(); + if (types.length == rawParamTypes.length + && rawParamTypes[0] == getDeclaringClass().getEnclosingClass()) { + // first parameter is the hidden 'this' + return Arrays.copyOfRange(types, 1, types.length); + } + } + return types; + } + + @Override + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker"}) + @IgnoreJRERequirement + AnnotatedType[] getAnnotatedParameterTypes() { + return constructor.getAnnotatedParameterTypes(); + } + + @Override + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker", "DoNotCall"}) + @IgnoreJRERequirement + public AnnotatedType getAnnotatedReturnType() { + return constructor.getAnnotatedReturnType(); + } + + @Override + Type[] getGenericExceptionTypes() { + return constructor.getGenericExceptionTypes(); + } + + @Override + final Annotation[][] getParameterAnnotations() { + return constructor.getParameterAnnotations(); + } + + /** + * {@inheritDoc} + * + *

{@code []} will be returned for ArrayList's constructor. When both the class and the + * constructor have type parameters, the class parameters are prepended before those of the + * constructor's. This is an arbitrary rule since no existing language spec mandates one way or + * the other. From the declaration syntax, the class type parameter appears first, but the call + * syntax may show up in opposite order such as {@code new Foo()}. + */ + @Override + public final TypeVariable[] getTypeParameters() { + TypeVariable[] declaredByClass = getDeclaringClass().getTypeParameters(); + TypeVariable[] declaredByConstructor = constructor.getTypeParameters(); + TypeVariable[] result = + new TypeVariable[declaredByClass.length + declaredByConstructor.length]; + System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length); + System.arraycopy( + declaredByConstructor, 0, result, declaredByClass.length, declaredByConstructor.length); + return result; + } + + @Override + public final boolean isOverridable() { + return false; + } + + @Override + public final boolean isVarArgs() { + return constructor.isVarArgs(); + } + + private boolean mayNeedHiddenThis() { + Class declaringClass = constructor.getDeclaringClass(); + if (declaringClass.getEnclosingConstructor() != null) { + // Enclosed in a constructor, needs hidden this + return true; + } + Method enclosingMethod = declaringClass.getEnclosingMethod(); + if (enclosingMethod != null) { + // Enclosed in a method, if it's not static, must need hidden this. + return !Modifier.isStatic(enclosingMethod.getModifiers()); + } else { + // Strictly, this doesn't necessarily indicate a hidden 'this' in the case of + // static initializer. But there seems no way to tell in that case. :( + // This may cause issues when an anonymous class is created inside a static initializer, + // and the class's constructor's first parameter happens to be the enclosing class. + // In such case, we may mistakenly think that the class is within a non-static context + // and the first parameter is the hidden 'this'. + return declaringClass.getEnclosingClass() != null + && !Modifier.isStatic(declaringClass.getModifiers()); + } + } + } + + private static final boolean ANNOTATED_TYPE_EXISTS = initAnnotatedTypeExists(); + + private static boolean initAnnotatedTypeExists() { + try { + Class.forName("java.lang.reflect.AnnotatedType"); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/MutableTypeToInstanceMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/MutableTypeToInstanceMap.java new file mode 100644 index 0000000000000..5a7e825d9a87c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/MutableTypeToInstanceMap.java @@ -0,0 +1,168 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.collect.ForwardingMap; +import org.apache.iotdb.google.common.collect.ForwardingMapEntry; +import org.apache.iotdb.google.common.collect.ForwardingSet; +import org.apache.iotdb.google.common.collect.Iterators; +import org.apache.iotdb.google.common.collect.Maps; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A mutable type-to-instance map. See also {@link ImmutableTypeToInstanceMap}. + * + * @author Ben Yu + * @since 13.0 + */ +@ElementTypesAreNonnullByDefault +public final class MutableTypeToInstanceMap + extends ForwardingMap, B> implements TypeToInstanceMap { + + private final Map, B> backingMap = Maps.newHashMap(); + + @Override + public T getInstance(Class type) { + return trustedGet(TypeToken.of(type)); + } + + @Override + public T getInstance(TypeToken type) { + return trustedGet(type.rejectTypeVariables()); + } + + @Override + public T putInstance(Class type, @ParametricNullness T value) { + return trustedPut(TypeToken.of(type), value); + } + + @Override + public T putInstance(TypeToken type, @ParametricNullness T value) { + return this.trustedPut(type.rejectTypeVariables(), value); + } + + /** + * Not supported. Use {@link #putInstance} instead. + * + * @deprecated unsupported operation + * @throws UnsupportedOperationException always + */ + @Deprecated + @Override + public B put(TypeToken key, @ParametricNullness B value) { + throw new UnsupportedOperationException("Please use putInstance() instead."); + } + + /** + * Not supported. Use {@link #putInstance} instead. + * + * @deprecated unsupported operation + * @throws UnsupportedOperationException always + */ + @Deprecated + @Override + public void putAll(Map, ? extends B> map) { + throw new UnsupportedOperationException("Please use putInstance() instead."); + } + + @Override + public Set, B>> entrySet() { + return UnmodifiableEntry.transformEntries(super.entrySet()); + } + + @Override + protected Map, B> delegate() { + return backingMap; + } + + @SuppressWarnings("unchecked") // value could not get in if not a T + private T trustedPut(TypeToken type, @ParametricNullness T value) { + return (T) backingMap.put(type, value); + } + + @SuppressWarnings("unchecked") // value could not get in if not a T + private T trustedGet(TypeToken type) { + return (T) backingMap.get(type); + } + + private static final class UnmodifiableEntry + extends ForwardingMapEntry { + + private final Entry delegate; + + static Set> transformEntries(Set> entries) { + return new ForwardingSet>() { + @Override + protected Set> delegate() { + return entries; + } + + @Override + public Iterator> iterator() { + return UnmodifiableEntry.transformEntries(super.iterator()); + } + + @Override + public Object[] toArray() { + /* + * standardToArray returns `@Nullable Object[]` rather than `Object[]` but only because it + * can be used with collections that may contain null. This collection is a collection of + * non-null Entry objects (Entry objects that might contain null values but are not + * themselves null), so we can treat it as a plain `Object[]`. + */ + @SuppressWarnings("nullness") + Object[] result = standardToArray(); + return result; + } + + @Override + @SuppressWarnings("nullness") // b/192354773 in our checker affects toArray declarations + public T[] toArray(T[] array) { + return standardToArray(array); + } + }; + } + + private static Iterator> transformEntries( + Iterator> entries) { + return Iterators.transform(entries, UnmodifiableEntry::new); + } + + private UnmodifiableEntry(java.util.Map.Entry delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + protected Entry delegate() { + return delegate; + } + + @Override + @ParametricNullness + public V setValue(@ParametricNullness V value) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Parameter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Parameter.java new file mode 100644 index 0000000000000..c0d49f826c22d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Parameter.java @@ -0,0 +1,166 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.collect.FluentIterable; +import org.apache.iotdb.google.common.collect.ImmutableList; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.AnnotatedType; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a method or constructor parameter. + * + * @author Ben Yu + * @since 14.0 + */ +@ElementTypesAreNonnullByDefault +public final class Parameter implements AnnotatedElement { + + private final Invokable declaration; + private final int position; + private final TypeToken type; + private final ImmutableList annotations; + + /** + * An {@link AnnotatedType} instance, or {@code null} under Android VMs (possible only when using + * the Android flavor of Guava). The field is declared with a type of {@code Object} to avoid + * compatibility problems on Android VMs. The corresponding accessor method, however, can have the + * more specific return type as long as users are careful to guard calls to it with version checks + * or reflection: Android VMs ignore the types of elements that aren't used. + */ + private final Object annotatedType; + + Parameter( + Invokable declaration, + int position, + TypeToken type, + Annotation[] annotations, + Object annotatedType) { + this.declaration = declaration; + this.position = position; + this.type = type; + this.annotations = ImmutableList.copyOf(annotations); + this.annotatedType = annotatedType; + } + + /** Returns the type of the parameter. */ + public TypeToken getType() { + return type; + } + + /** Returns the {@link Invokable} that declares this parameter. */ + public Invokable getDeclaringInvokable() { + return declaration; + } + + @Override + public boolean isAnnotationPresent(Class annotationType) { + return getAnnotation(annotationType) != null; + } + + @Override + public A getAnnotation(Class annotationType) { + checkNotNull(annotationType); + for (Annotation annotation : annotations) { + if (annotationType.isInstance(annotation)) { + return annotationType.cast(annotation); + } + } + return null; + } + + @Override + public Annotation[] getAnnotations() { + return getDeclaredAnnotations(); + } + + /** + * @since 18.0 + */ + @Override + public A[] getAnnotationsByType(Class annotationType) { + return getDeclaredAnnotationsByType(annotationType); + } + + /** + * @since 18.0 + */ + @Override + public Annotation[] getDeclaredAnnotations() { + return annotations.toArray(new Annotation[0]); + } + + /** + * @since 18.0 + */ + @Override + public A getDeclaredAnnotation(Class annotationType) { + checkNotNull(annotationType); + return FluentIterable.from(annotations).filter(annotationType).first().orNull(); + } + + /** + * @since 18.0 + */ + @Override + public A[] getDeclaredAnnotationsByType(Class annotationType) { + A[] result = FluentIterable.from(annotations).filter(annotationType).toArray(annotationType); + @SuppressWarnings("nullness") // safe because the input list contains no nulls + A[] cast = (A[]) result; + return cast; + } + + /** + * Returns the {@link AnnotatedType} of the parameter. + * + *

This method will fail if run under an Android VM. + * + * @since 25.1 for guava-jre (available since 32.0.0 in guava-android) + */ + @SuppressWarnings({"Java7ApiChecker", "AndroidJdkLibsChecker"}) + @IgnoreJRERequirement + public AnnotatedType getAnnotatedType() { + return requireNonNull((AnnotatedType) annotatedType); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Parameter) { + Parameter that = (Parameter) obj; + return position == that.position && declaration.equals(that.declaration); + } + return false; + } + + @Override + public int hashCode() { + return position; + } + + @Override + public String toString() { + return type + " arg" + position; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ParametricNullness.java new file mode 100644 index 0000000000000..cd4fa777fdd09 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Reflection.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Reflection.java new file mode 100644 index 0000000000000..486c5691cc8e9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Reflection.java @@ -0,0 +1,95 @@ +/* + * 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.iotdb.google.common.reflect; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utilities relating to Java reflection. + * + * @since 12.0 + */ +@ElementTypesAreNonnullByDefault +public final class Reflection { + + /** + * Returns the package name of {@code clazz} according to the Java Language Specification (section + * 6.7). Unlike {@link Class#getPackage}, this method only parses the class name, without + * attempting to define the {@link Package} and hence load files. + */ + public static String getPackageName(Class clazz) { + return getPackageName(clazz.getName()); + } + + /** + * Returns the package name of {@code classFullName} according to the Java Language Specification + * (section 6.7). Unlike {@link Class#getPackage}, this method only parses the class name, without + * attempting to define the {@link Package} and hence load files. + */ + public static String getPackageName(String classFullName) { + int lastDot = classFullName.lastIndexOf('.'); + return (lastDot < 0) ? "" : classFullName.substring(0, lastDot); + } + + /** + * Ensures that the given classes are initialized, as described in JLS Section + * 12.4.2. + * + *

WARNING: Normally it's a smell if a class needs to be explicitly initialized, because static + * state hurts system maintainability and testability. In cases when you have no choice while + * interoperating with a legacy framework, this method helps to keep the code less ugly. + * + * @throws ExceptionInInitializerError if an exception is thrown during initialization of a class + */ + public static void initialize(Class... classes) { + for (Class clazz : classes) { + try { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + } + + /** + * Returns a proxy instance that implements {@code interfaceType} by dispatching method + * invocations to {@code handler}. The class loader of {@code interfaceType} will be used to + * define the proxy class. To implement multiple interfaces or specify a class loader, use {@link + * Proxy#newProxyInstance}. + * + * @throws IllegalArgumentException if {@code interfaceType} does not specify the type of a Java + * interface + */ + public static T newProxy(Class interfaceType, InvocationHandler handler) { + checkNotNull(handler); + checkArgument(interfaceType.isInterface(), "%s is not an interface", interfaceType); + Object object = + Proxy.newProxyInstance( + interfaceType.getClassLoader(), new Class[] {interfaceType}, handler); + return interfaceType.cast(object); + } + + private Reflection() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeCapture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeCapture.java new file mode 100644 index 0000000000000..c26f6996d5610 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeCapture.java @@ -0,0 +1,41 @@ +/* + * 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.iotdb.google.common.reflect; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * Captures the actual type of {@code T}. + * + * @author Ben Yu + */ +@ElementTypesAreNonnullByDefault +abstract class TypeCapture { + + /** Returns the captured type. */ + final Type capture() { + Type superclass = getClass().getGenericSuperclass(); + checkArgument(superclass instanceof ParameterizedType, "%s isn't parameterized", superclass); + return ((ParameterizedType) superclass).getActualTypeArguments()[0]; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeParameter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeParameter.java new file mode 100644 index 0000000000000..0393275e19a6e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeParameter.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.google.common.reflect; + +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** + * Captures a free type variable that can be used in {@link TypeToken#where}. For example: + * + *

{@code
+ * static  TypeToken> listOf(Class elementType) {
+ *   return new TypeToken>() {}
+ *       .where(new TypeParameter() {}, elementType);
+ * }
+ * }
+ * + * @author Ben Yu + * @since 12.0 + */ +@ElementTypesAreNonnullByDefault +/* + * A nullable bound would let users create a TypeParameter instance for a parameter with a nullable + * bound. However, it would also let them create `new TypeParameter<@Nullable T>() {}`, which + * wouldn't behave as users might expect. Additionally, it's not clear how the TypeToken API could + * support even a "normal" `TypeParameter` when `` has a nullable bound. (See the discussion + * on TypeToken.where.) So, in the interest of failing fast and encouraging the user to switch to a + * non-null bound if possible, let's require a non-null bound here. + * + * TODO(cpovirk): Elaborate on "wouldn't behave as users might expect." + */ +public abstract class TypeParameter extends TypeCapture { + + final TypeVariable typeVariable; + + protected TypeParameter() { + Type type = capture(); + checkArgument(type instanceof TypeVariable, "%s should be a type variable.", type); + this.typeVariable = (TypeVariable) type; + } + + @Override + public final int hashCode() { + return typeVariable.hashCode(); + } + + @Override + public final boolean equals(Object o) { + if (o instanceof TypeParameter) { + TypeParameter that = (TypeParameter) o; + return typeVariable.equals(that.typeVariable); + } + return false; + } + + @Override + public String toString() { + return typeVariable.toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeResolver.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeResolver.java new file mode 100644 index 0000000000000..21674cae57c66 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeResolver.java @@ -0,0 +1,608 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Maps; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.util.Arrays.asList; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * An object of this class encapsulates type mappings from type variables. Mappings are established + * with {@link #where} and types are resolved using {@link #resolveType}. + * + *

Note that usually type mappings are already implied by the static type hierarchy (for example, + * the {@code E} type variable declared by class {@code List} naturally maps to {@code String} in + * the context of {@code class MyStringList implements List}). In such case, prefer to use + * {@link TypeToken#resolveType} since it's simpler and more type safe. This class should only be + * used when the type mapping isn't implied by the static type hierarchy, but provided through other + * means such as an annotation or external configuration file. + * + * @author Ben Yu + * @since 15.0 + */ +@ElementTypesAreNonnullByDefault +public final class TypeResolver { + + private final TypeTable typeTable; + + public TypeResolver() { + this.typeTable = new TypeTable(); + } + + private TypeResolver(TypeTable typeTable) { + this.typeTable = typeTable; + } + + /** + * Returns a resolver that resolves types "covariantly". + * + *

For example, when resolving {@code List} in the context of {@code ArrayList}, {@code + * } is covariantly resolved to {@code } such that return type of {@code List::get} is + * {@code }. + */ + static TypeResolver covariantly(Type contextType) { + return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(contextType)); + } + + /** + * Returns a resolver that resolves types "invariantly". + * + *

For example, when resolving {@code List} in the context of {@code ArrayList}, {@code + * } cannot be invariantly resolved to {@code } because otherwise the parameter type of + * {@code List::set} will be {@code } and it'll falsely say any object can be passed into + * {@code ArrayList::set}. + * + *

Instead, {@code } will be resolved to a capture in the form of a type variable {@code + * }, effectively preventing {@code set} from accepting any type. + */ + static TypeResolver invariantly(Type contextType) { + Type invariantContext = WildcardCapturer.INSTANCE.capture(contextType); + return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(invariantContext)); + } + + /** + * Returns a new {@code TypeResolver} with type variables in {@code formal} mapping to types in + * {@code actual}. + * + *

For example, if {@code formal} is a {@code TypeVariable T}, and {@code actual} is {@code + * String.class}, then {@code new TypeResolver().where(formal, actual)} will {@linkplain + * #resolveType resolve} {@code ParameterizedType List} to {@code List}, and resolve + * {@code Map} to {@code Map} etc. Similarly, {@code formal} and + * {@code actual} can be {@code Map} and {@code Map} respectively, or they + * can be {@code E[]} and {@code String[]} respectively, or even any arbitrary combination + * thereof. + * + * @param formal The type whose type variables or itself is mapped to other type(s). It's almost + * always a bug if {@code formal} isn't a type variable and contains no type variable. Make + * sure you are passing the two parameters in the right order. + * @param actual The type that the formal type variable(s) are mapped to. It can be or contain yet + * other type variables, in which case these type variables will be further resolved if + * corresponding mappings exist in the current {@code TypeResolver} instance. + */ + public TypeResolver where(Type formal, Type actual) { + Map mappings = Maps.newHashMap(); + populateTypeMappings(mappings, checkNotNull(formal), checkNotNull(actual)); + return where(mappings); + } + + /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */ + TypeResolver where(Map mappings) { + return new TypeResolver(typeTable.where(mappings)); + } + + private static void populateTypeMappings( + Map mappings, Type from, Type to) { + if (from.equals(to)) { + return; + } + new TypeVisitor() { + @Override + void visitTypeVariable(TypeVariable typeVariable) { + mappings.put(new TypeVariableKey(typeVariable), to); + } + + @Override + void visitWildcardType(WildcardType fromWildcardType) { + if (!(to instanceof WildcardType)) { + return; // okay to say is anything + } + WildcardType toWildcardType = (WildcardType) to; + Type[] fromUpperBounds = fromWildcardType.getUpperBounds(); + Type[] toUpperBounds = toWildcardType.getUpperBounds(); + Type[] fromLowerBounds = fromWildcardType.getLowerBounds(); + Type[] toLowerBounds = toWildcardType.getLowerBounds(); + checkArgument( + fromUpperBounds.length == toUpperBounds.length + && fromLowerBounds.length == toLowerBounds.length, + "Incompatible type: %s vs. %s", + fromWildcardType, + to); + for (int i = 0; i < fromUpperBounds.length; i++) { + populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]); + } + for (int i = 0; i < fromLowerBounds.length; i++) { + populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]); + } + } + + @Override + void visitParameterizedType(ParameterizedType fromParameterizedType) { + if (to instanceof WildcardType) { + return; // Okay to say Foo is + } + ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to); + if (fromParameterizedType.getOwnerType() != null + && toParameterizedType.getOwnerType() != null) { + populateTypeMappings( + mappings, fromParameterizedType.getOwnerType(), toParameterizedType.getOwnerType()); + } + checkArgument( + fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()), + "Inconsistent raw type: %s vs. %s", + fromParameterizedType, + to); + Type[] fromArgs = fromParameterizedType.getActualTypeArguments(); + Type[] toArgs = toParameterizedType.getActualTypeArguments(); + checkArgument( + fromArgs.length == toArgs.length, + "%s not compatible with %s", + fromParameterizedType, + toParameterizedType); + for (int i = 0; i < fromArgs.length; i++) { + populateTypeMappings(mappings, fromArgs[i], toArgs[i]); + } + } + + @Override + void visitGenericArrayType(GenericArrayType fromArrayType) { + if (to instanceof WildcardType) { + return; // Okay to say A[] is + } + Type componentType = Types.getComponentType(to); + checkArgument(componentType != null, "%s is not an array type.", to); + populateTypeMappings(mappings, fromArrayType.getGenericComponentType(), componentType); + } + + @Override + void visitClass(Class fromClass) { + if (to instanceof WildcardType) { + return; // Okay to say Foo is + } + // Can't map from a raw class to anything other than itself or a wildcard. + // You can't say "assuming String is Integer". + // And we don't support "assuming String is T"; user has to say "assuming T is String". + throw new IllegalArgumentException("No type mapping from " + fromClass + " to " + to); + } + }.visit(from); + } + + /** + * Resolves all type variables in {@code type} and all downstream types and returns a + * corresponding type with type variables resolved. + */ + public Type resolveType(Type type) { + checkNotNull(type); + if (type instanceof TypeVariable) { + return typeTable.resolve((TypeVariable) type); + } else if (type instanceof ParameterizedType) { + return resolveParameterizedType((ParameterizedType) type); + } else if (type instanceof GenericArrayType) { + return resolveGenericArrayType((GenericArrayType) type); + } else if (type instanceof WildcardType) { + return resolveWildcardType((WildcardType) type); + } else { + // if Class, no resolution needed, we are done. + return type; + } + } + + Type[] resolveTypesInPlace(Type[] types) { + for (int i = 0; i < types.length; i++) { + types[i] = resolveType(types[i]); + } + return types; + } + + private Type[] resolveTypes(Type[] types) { + Type[] result = new Type[types.length]; + for (int i = 0; i < types.length; i++) { + result[i] = resolveType(types[i]); + } + return result; + } + + private WildcardType resolveWildcardType(WildcardType type) { + Type[] lowerBounds = type.getLowerBounds(); + Type[] upperBounds = type.getUpperBounds(); + return new Types.WildcardTypeImpl(resolveTypes(lowerBounds), resolveTypes(upperBounds)); + } + + private Type resolveGenericArrayType(GenericArrayType type) { + Type componentType = type.getGenericComponentType(); + Type resolvedComponentType = resolveType(componentType); + return Types.newArrayType(resolvedComponentType); + } + + private ParameterizedType resolveParameterizedType(ParameterizedType type) { + Type owner = type.getOwnerType(); + Type resolvedOwner = (owner == null) ? null : resolveType(owner); + Type resolvedRawType = resolveType(type.getRawType()); + + Type[] args = type.getActualTypeArguments(); + Type[] resolvedArgs = resolveTypes(args); + return Types.newParameterizedTypeWithOwner( + resolvedOwner, (Class) resolvedRawType, resolvedArgs); + } + + private static T expectArgument(Class type, Object arg) { + try { + return type.cast(arg); + } catch (ClassCastException e) { + throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName()); + } + } + + /** A TypeTable maintains mapping from {@link TypeVariable} to types. */ + private static class TypeTable { + private final ImmutableMap map; + + TypeTable() { + this.map = ImmutableMap.of(); + } + + private TypeTable(ImmutableMap map) { + this.map = map; + } + + /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */ + final TypeTable where(Map mappings) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.putAll(map); + for (Entry mapping : mappings.entrySet()) { + TypeVariableKey variable = mapping.getKey(); + Type type = mapping.getValue(); + checkArgument(!variable.equalsType(type), "Type variable %s bound to itself", variable); + builder.put(variable, type); + } + return new TypeTable(builder.buildOrThrow()); + } + + final Type resolve(TypeVariable var) { + TypeTable unguarded = this; + TypeTable guarded = + new TypeTable() { + @Override + public Type resolveInternal(TypeVariable intermediateVar, TypeTable forDependent) { + if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) { + return intermediateVar; + } + return unguarded.resolveInternal(intermediateVar, forDependent); + } + }; + return resolveInternal(var, guarded); + } + + /** + * Resolves {@code var} using the encapsulated type mapping. If it maps to yet another + * non-reified type or has bounds, {@code forDependants} is used to do further resolution, which + * doesn't try to resolve any type variable on generic declarations that are already being + * resolved. + * + *

Should only be called and overridden by {@link #resolve(TypeVariable)}. + */ + Type resolveInternal(TypeVariable var, TypeTable forDependants) { + Type type = map.get(new TypeVariableKey(var)); + if (type == null) { + Type[] bounds = var.getBounds(); + if (bounds.length == 0) { + return var; + } + Type[] resolvedBounds = new TypeResolver(forDependants).resolveTypes(bounds); + /* + * We'd like to simply create our own TypeVariable with the newly resolved bounds. There's + * just one problem: Starting with JDK 7u51, the JDK TypeVariable's equals() method doesn't + * recognize instances of our TypeVariable implementation. This is a problem because users + * compare TypeVariables from the JDK against TypeVariables returned by TypeResolver. To + * work with all JDK versions, TypeResolver must return the appropriate TypeVariable + * implementation in each of the three possible cases: + * + * 1. Prior to JDK 7u51, the JDK TypeVariable implementation interoperates with ours. + * Therefore, we can always create our own TypeVariable. + * + * 2. Starting with JDK 7u51, the JDK TypeVariable implementations does not interoperate + * with ours. Therefore, we have to be careful about whether we create our own TypeVariable: + * + * 2a. If the resolved types are identical to the original types, then we can return the + * original, identical JDK TypeVariable. By doing so, we sidestep the problem entirely. + * + * 2b. If the resolved types are different from the original types, things are trickier. The + * only way to get a TypeVariable instance for the resolved types is to create our own. The + * created TypeVariable will not interoperate with any JDK TypeVariable. But this is OK: We + * don't _want_ our new TypeVariable to be equal to the JDK TypeVariable because it has + * _different bounds_ than the JDK TypeVariable. And it wouldn't make sense for our new + * TypeVariable to be equal to any _other_ JDK TypeVariable, either, because any other JDK + * TypeVariable must have a different declaration or name. The only TypeVariable that our + * new TypeVariable _will_ be equal to is an equivalent TypeVariable that was also created + * by us. And that equality is guaranteed to hold because it doesn't involve the JDK + * TypeVariable implementation at all. + */ + if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY + && Arrays.equals(bounds, resolvedBounds)) { + return var; + } + return Types.newArtificialTypeVariable( + var.getGenericDeclaration(), var.getName(), resolvedBounds); + } + // in case the type is yet another type variable. + return new TypeResolver(forDependants).resolveType(type); + } + } + + private static final class TypeMappingIntrospector extends TypeVisitor { + + private final Map mappings = Maps.newHashMap(); + + /** + * Returns type mappings using type parameters and type arguments found in the generic + * superclass and the super interfaces of {@code contextClass}. + */ + static ImmutableMap getTypeMappings(Type contextType) { + checkNotNull(contextType); + TypeMappingIntrospector introspector = new TypeMappingIntrospector(); + introspector.visit(contextType); + return ImmutableMap.copyOf(introspector.mappings); + } + + @Override + void visitClass(Class clazz) { + visit(clazz.getGenericSuperclass()); + visit(clazz.getGenericInterfaces()); + } + + @Override + void visitParameterizedType(ParameterizedType parameterizedType) { + Class rawClass = (Class) parameterizedType.getRawType(); + TypeVariable[] vars = rawClass.getTypeParameters(); + Type[] typeArgs = parameterizedType.getActualTypeArguments(); + checkState(vars.length == typeArgs.length); + for (int i = 0; i < vars.length; i++) { + map(new TypeVariableKey(vars[i]), typeArgs[i]); + } + visit(rawClass); + visit(parameterizedType.getOwnerType()); + } + + @Override + void visitTypeVariable(TypeVariable t) { + visit(t.getBounds()); + } + + @Override + void visitWildcardType(WildcardType t) { + visit(t.getUpperBounds()); + } + + private void map(TypeVariableKey var, Type arg) { + if (mappings.containsKey(var)) { + // Mapping already established + // This is possible when following both superClass -> enclosingClass + // and enclosingclass -> superClass paths. + // Since we follow the path of superclass first, enclosing second, + // superclass mapping should take precedence. + return; + } + // First, check whether var -> arg forms a cycle + for (Type t = arg; t != null; t = mappings.get(TypeVariableKey.forLookup(t))) { + if (var.equalsType(t)) { + // cycle detected, remove the entire cycle from the mapping so that + // each type variable resolves deterministically to itself. + // Otherwise, an F -> T cycle will end up resolving both F and T + // nondeterministically to either F or T. + for (Type x = arg; x != null; x = mappings.remove(TypeVariableKey.forLookup(x))) {} + return; + } + } + mappings.put(var, arg); + } + } + + // This is needed when resolving types against a context with wildcards + // For example: + // class Holder { + // void set(T data) {...} + // } + // Holder> should *not* resolve the set() method to set(List data). + // Instead, it should create a capture of the wildcard so that set() rejects any List. + private static class WildcardCapturer { + + static final WildcardCapturer INSTANCE = new WildcardCapturer(); + + private final AtomicInteger id; + + private WildcardCapturer() { + this(new AtomicInteger()); + } + + private WildcardCapturer(AtomicInteger id) { + this.id = id; + } + + final Type capture(Type type) { + checkNotNull(type); + if (type instanceof Class) { + return type; + } + if (type instanceof TypeVariable) { + return type; + } + if (type instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType) type; + return Types.newArrayType( + notForTypeVariable().capture(arrayType.getGenericComponentType())); + } + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Class rawType = (Class) parameterizedType.getRawType(); + TypeVariable[] typeVars = rawType.getTypeParameters(); + Type[] typeArgs = parameterizedType.getActualTypeArguments(); + for (int i = 0; i < typeArgs.length; i++) { + typeArgs[i] = forTypeVariable(typeVars[i]).capture(typeArgs[i]); + } + return Types.newParameterizedTypeWithOwner( + notForTypeVariable().captureNullable(parameterizedType.getOwnerType()), + rawType, + typeArgs); + } + if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + Type[] lowerBounds = wildcardType.getLowerBounds(); + if (lowerBounds.length == 0) { // ? extends something changes to capture-of + return captureAsTypeVariable(wildcardType.getUpperBounds()); + } else { + // TODO(benyu): handle ? super T somehow. + return type; + } + } + throw new AssertionError("must have been one of the known types"); + } + + TypeVariable captureAsTypeVariable(Type[] upperBounds) { + String name = + "capture#" + id.incrementAndGet() + "-of ? extends " + Joiner.on('&').join(upperBounds); + return Types.newArtificialTypeVariable(WildcardCapturer.class, name, upperBounds); + } + + private WildcardCapturer forTypeVariable(TypeVariable typeParam) { + return new WildcardCapturer(id) { + @Override + TypeVariable captureAsTypeVariable(Type[] upperBounds) { + Set combined = new LinkedHashSet<>(asList(upperBounds)); + // Since this is an artificially generated type variable, we don't bother checking + // subtyping between declared type bound and actual type bound. So it's possible that we + // may generate something like . + // Checking subtype between declared and actual type bounds + // adds recursive isSubtypeOf() call and feels complicated. + // There is no contract one way or another as long as isSubtypeOf() works as expected. + combined.addAll(asList(typeParam.getBounds())); + if (combined.size() > 1) { // Object is implicit and only useful if it's the only bound. + combined.remove(Object.class); + } + return super.captureAsTypeVariable(combined.toArray(new Type[0])); + } + }; + } + + private WildcardCapturer notForTypeVariable() { + return new WildcardCapturer(id); + } + + private Type captureNullable(Type type) { + if (type == null) { + return null; + } + return capture(type); + } + } + + /** + * Wraps around {@code TypeVariable} to ensure that any two type variables are equal as long as + * they are declared by the same {@link java.lang.reflect.GenericDeclaration} and have the same + * name, even if their bounds differ. + * + *

While resolving a type variable from a {@code var -> type} map, we don't care whether the + * type variable's bound has been partially resolved. As long as the type variable "identity" + * matches. + * + *

On the other hand, if for example we are resolving {@code List} to {@code + * List}, we need to compare that {@code } is unequal to {@code } in order to decide to use the transformed type instead of the original type. + */ + static final class TypeVariableKey { + private final TypeVariable var; + + TypeVariableKey(TypeVariable var) { + this.var = checkNotNull(var); + } + + @Override + public int hashCode() { + return Objects.hashCode(var.getGenericDeclaration(), var.getName()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TypeVariableKey) { + TypeVariableKey that = (TypeVariableKey) obj; + return equalsTypeVariable(that.var); + } else { + return false; + } + } + + @Override + public String toString() { + return var.toString(); + } + + /** Wraps {@code t} in a {@code TypeVariableKey} if it's a type variable. */ + static TypeVariableKey forLookup(Type t) { + if (t instanceof TypeVariable) { + return new TypeVariableKey((TypeVariable) t); + } else { + return null; + } + } + + /** + * Returns true if {@code type} is a {@code TypeVariable} with the same name and declared by the + * same {@code GenericDeclaration}. + */ + boolean equalsType(Type type) { + if (type instanceof TypeVariable) { + return equalsTypeVariable((TypeVariable) type); + } else { + return false; + } + } + + private boolean equalsTypeVariable(TypeVariable that) { + return var.getGenericDeclaration().equals(that.getGenericDeclaration()) + && var.getName().equals(that.getName()); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeToInstanceMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeToInstanceMap.java new file mode 100644 index 0000000000000..87abd014cc987 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeToInstanceMap.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.google.common.reflect; + +import java.util.Map; + +/** + * A map, each entry of which maps a {@link TypeToken} to an instance of that type. In addition to + * implementing {@code Map}, the additional type-safe operations {@link #putInstance} and {@link + * #getInstance} are available. + * + *

Generally, implementations don't support {@link #put} and {@link #putAll} because there is no + * way to check an object at runtime to be an instance of a {@link TypeToken}. Instead, caller + * should use the type safe {@link #putInstance}. + * + *

Also, if caller suppresses unchecked warnings and passes in an {@code Iterable} for + * type {@code Iterable}, the map won't be able to detect and throw type error. + * + *

Like any other {@code Map}, this map may contain entries for primitive types, + * and a primitive type and its corresponding wrapper type may map to different values. + * + * @param the common supertype that all entries must share; often this is simply {@link Object} + * @author Ben Yu + * @since 13.0 + */ +@ElementTypesAreNonnullByDefault +public interface TypeToInstanceMap extends Map, B> { + + /** + * Returns the value the specified class is mapped to, or {@code null} if no entry for this class + * is present. This will only return a value that was bound to this specific class, not a value + * that may have been bound to a subtype. + * + *

{@code getInstance(Foo.class)} is equivalent to {@code + * getInstance(TypeToken.of(Foo.class))}. + */ + T getInstance(Class type); + + /** + * Returns the value the specified type is mapped to, or {@code null} if no entry for this type is + * present. This will only return a value that was bound to this specific type, not a value that + * may have been bound to a subtype. + */ + T getInstance(TypeToken type); + + /** + * Maps the specified class to the specified value. Does not associate this value with any + * of the class's supertypes. + * + *

{@code putInstance(Foo.class, foo)} is equivalent to {@code + * putInstance(TypeToken.of(Foo.class), foo)}. + * + * @return the value previously associated with this class (possibly {@code null}), or {@code + * null} if there was no previous entry. + */ + T putInstance(Class type, @ParametricNullness T value); + + /** + * Maps the specified type to the specified value. Does not associate this value with any + * of the type's supertypes. + * + * @return the value previously associated with this type (possibly {@code null}), or {@code null} + * if there was no previous entry. + */ + T putInstance(TypeToken type, @ParametricNullness T value); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeToken.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeToken.java new file mode 100644 index 0000000000000..4648dafd46266 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeToken.java @@ -0,0 +1,1464 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Predicate; +import org.apache.iotdb.google.common.collect.FluentIterable; +import org.apache.iotdb.google.common.collect.ForwardingSet; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Ordering; +import org.apache.iotdb.google.common.primitives.Primitives; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; + +/** + * A {@link Type} with generics. + * + *

Operations that are otherwise only available in {@link Class} are implemented to support + * {@code Type}, for example {@link #isSubtypeOf}, {@link #isArray} and {@link #getComponentType}. + * It also provides additional utilities such as {@link #getTypes}, {@link #resolveType}, etc. + * + *

There are three ways to get a {@code TypeToken} instance: + * + *

+ * + *

{@code TypeToken} is serializable when no type variable is contained in the type. + * + *

Note to Guice users: {@code TypeToken} is similar to Guice's {@code TypeLiteral} class except + * that it is serializable and offers numerous additional utility methods. + * + * @author Bob Lee + * @author Sven Mawson + * @author Ben Yu + * @since 12.0 + */ +@SuppressWarnings("serial") // SimpleTypeToken is the serialized form. +@ElementTypesAreNonnullByDefault +public abstract class TypeToken extends TypeCapture implements Serializable { + + private final Type runtimeType; + + /** Resolver for resolving parameter and field types with {@link #runtimeType} as context. */ + private transient TypeResolver invariantTypeResolver; + + /** Resolver for resolving covariant types with {@link #runtimeType} as context. */ + private transient TypeResolver covariantTypeResolver; + + /** + * Constructs a new type token of {@code T}. + * + *

Clients create an empty anonymous subclass. Doing so embeds the type parameter in the + * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure. + * + *

For example: + * + *

{@code
+   * TypeToken> t = new TypeToken>() {};
+   * }
+ */ + protected TypeToken() { + this.runtimeType = capture(); + checkState( + !(runtimeType instanceof TypeVariable), + "Cannot construct a TypeToken for a type variable.\n" + + "You probably meant to call new TypeToken<%s>(getClass()) " + + "that can resolve the type variable for you.\n" + + "If you do need to create a TypeToken of a type variable, " + + "please use TypeToken.of() instead.", + runtimeType); + } + + /** + * Constructs a new type token of {@code T} while resolving free type variables in the context of + * {@code declaringClass}. + * + *

Clients create an empty anonymous subclass. Doing so embeds the type parameter in the + * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure. + * + *

For example: + * + *

{@code
+   * abstract class IKnowMyType {
+   *   TypeToken getMyType() {
+   *     return new TypeToken(getClass()) {};
+   *   }
+   * }
+   *
+   * new IKnowMyType() {}.getMyType() => String
+   * }
+ */ + protected TypeToken(Class declaringClass) { + Type captured = super.capture(); + if (captured instanceof Class) { + this.runtimeType = captured; + } else { + this.runtimeType = TypeResolver.covariantly(declaringClass).resolveType(captured); + } + } + + private TypeToken(Type type) { + this.runtimeType = checkNotNull(type); + } + + /** Returns an instance of type token that wraps {@code type}. */ + public static TypeToken of(Class type) { + return new SimpleTypeToken<>(type); + } + + /** Returns an instance of type token that wraps {@code type}. */ + public static TypeToken of(Type type) { + return new SimpleTypeToken<>(type); + } + + /** + * Returns the raw type of {@code T}. Formally speaking, if {@code T} is returned by {@link + * java.lang.reflect.Method#getGenericReturnType}, the raw type is what's returned by {@link + * java.lang.reflect.Method#getReturnType} of the same method object. Specifically: + * + *
    + *
  • If {@code T} is a {@code Class} itself, {@code T} itself is returned. + *
  • If {@code T} is a {@link ParameterizedType}, the raw type of the parameterized type is + * returned. + *
  • If {@code T} is a {@link GenericArrayType}, the returned type is the corresponding array + * class. For example: {@code List[] => List[]}. + *
  • If {@code T} is a type variable or a wildcard type, the raw type of the first upper bound + * is returned. For example: {@code => Foo}. + *
+ */ + public final Class getRawType() { + // For wildcard or type variable, the first bound determines the runtime type. + Class rawType = getRawTypes().iterator().next(); + @SuppressWarnings("unchecked") // raw type is |T| + Class result = (Class) rawType; + return result; + } + + /** Returns the represented type. */ + public final Type getType() { + return runtimeType; + } + + /** + * Returns a new {@code TypeToken} where type variables represented by {@code typeParam} are + * substituted by {@code typeArg}. For example, it can be used to construct {@code Map} for + * any {@code K} and {@code V} type: + * + *
{@code
+   * static  TypeToken> mapOf(
+   *     TypeToken keyType, TypeToken valueType) {
+   *   return new TypeToken>() {}
+   *       .where(new TypeParameter() {}, keyType)
+   *       .where(new TypeParameter() {}, valueType);
+   * }
+   * }
+ * + * @param The parameter type + * @param typeParam the parameter type variable + * @param typeArg the actual type to substitute + */ + /* + * TODO(cpovirk): Is there any way for us to support TypeParameter instances for type parameters + * that have nullable bounds? Unfortunately, if we change the parameter to TypeParameter, then users might pass a TypeParameter, where Y is a subtype of X, while still + * passing a TypeToken. This would be invalid. Maybe we could accept a TypeParameter<@PolyNull + * X> if we support such a thing? It would be weird or misleading for users to be able to pass + * `new TypeParameter<@Nullable T>() {}` and have it act as a plain `TypeParameter`, but + * hopefully no one would do that, anyway. See also the comment on TypeParameter itself. + * + * TODO(cpovirk): Elaborate on this / merge with other comment? + */ + public final TypeToken where(TypeParameter typeParam, TypeToken typeArg) { + TypeResolver resolver = + new TypeResolver() + .where( + ImmutableMap.of( + new TypeResolver.TypeVariableKey(typeParam.typeVariable), typeArg.runtimeType)); + // If there's any type error, we'd report now rather than later. + return new SimpleTypeToken<>(resolver.resolveType(runtimeType)); + } + + /** + * Returns a new {@code TypeToken} where type variables represented by {@code typeParam} are + * substituted by {@code typeArg}. For example, it can be used to construct {@code Map} for + * any {@code K} and {@code V} type: + * + *
{@code
+   * static  TypeToken> mapOf(
+   *     Class keyType, Class valueType) {
+   *   return new TypeToken>() {}
+   *       .where(new TypeParameter() {}, keyType)
+   *       .where(new TypeParameter() {}, valueType);
+   * }
+   * }
+ * + * @param The parameter type + * @param typeParam the parameter type variable + * @param typeArg the actual type to substitute + */ + /* + * TODO(cpovirk): Is there any way for us to support TypeParameter instances for type parameters + * that have nullable bounds? See discussion on the other overload of this method. + */ + public final TypeToken where(TypeParameter typeParam, Class typeArg) { + return where(typeParam, of(typeArg)); + } + + /** + * Resolves the given {@code type} against the type context represented by this type. For example: + * + *
{@code
+   * new TypeToken>() {}.resolveType(
+   *     List.class.getMethod("get", int.class).getGenericReturnType())
+   * => String.class
+   * }
+ */ + public final TypeToken resolveType(Type type) { + checkNotNull(type); + // Being conservative here because the user could use resolveType() to resolve a type in an + // invariant context. + return of(getInvariantTypeResolver().resolveType(type)); + } + + private TypeToken resolveSupertype(Type type) { + TypeToken supertype = of(getCovariantTypeResolver().resolveType(type)); + // super types' type mapping is a subset of type mapping of this type. + supertype.covariantTypeResolver = covariantTypeResolver; + supertype.invariantTypeResolver = invariantTypeResolver; + return supertype; + } + + /** + * Returns the generic superclass of this type or {@code null} if the type represents {@link + * Object} or an interface. This method is similar but different from {@link + * Class#getGenericSuperclass}. For example, {@code new TypeToken() + * {}.getGenericSuperclass()} will return {@code new TypeToken>() {}}; while + * {@code StringArrayList.class.getGenericSuperclass()} will return {@code ArrayList}, where + * {@code E} is the type variable declared by class {@code ArrayList}. + * + *

If this type is a type variable or wildcard, its first upper bound is examined and returned + * if the bound is a class or extends from a class. This means that the returned type could be a + * type variable too. + */ + final TypeToken getGenericSuperclass() { + if (runtimeType instanceof TypeVariable) { + // First bound is always the super class, if one exists. + return boundAsSuperclass(((TypeVariable) runtimeType).getBounds()[0]); + } + if (runtimeType instanceof WildcardType) { + // wildcard has one and only one upper bound. + return boundAsSuperclass(((WildcardType) runtimeType).getUpperBounds()[0]); + } + Type superclass = getRawType().getGenericSuperclass(); + if (superclass == null) { + return null; + } + @SuppressWarnings("unchecked") // super class of T + TypeToken superToken = (TypeToken) resolveSupertype(superclass); + return superToken; + } + + private TypeToken boundAsSuperclass(Type bound) { + TypeToken token = of(bound); + if (token.getRawType().isInterface()) { + return null; + } + @SuppressWarnings("unchecked") // only upper bound of T is passed in. + TypeToken superclass = (TypeToken) token; + return superclass; + } + + /** + * Returns the generic interfaces that this type directly {@code implements}. This method is + * similar but different from {@link Class#getGenericInterfaces()}. For example, {@code new + * TypeToken>() {}.getGenericInterfaces()} will return a list that contains {@code + * new TypeToken>() {}}; while {@code List.class.getGenericInterfaces()} will + * return an array that contains {@code Iterable}, where the {@code T} is the type variable + * declared by interface {@code Iterable}. + * + *

If this type is a type variable or wildcard, its upper bounds are examined and those that + * are either an interface or upper-bounded only by interfaces are returned. This means that the + * returned types could include type variables too. + */ + final ImmutableList> getGenericInterfaces() { + if (runtimeType instanceof TypeVariable) { + return boundsAsInterfaces(((TypeVariable) runtimeType).getBounds()); + } + if (runtimeType instanceof WildcardType) { + return boundsAsInterfaces(((WildcardType) runtimeType).getUpperBounds()); + } + ImmutableList.Builder> builder = ImmutableList.builder(); + for (Type interfaceType : getRawType().getGenericInterfaces()) { + @SuppressWarnings("unchecked") // interface of T + TypeToken resolvedInterface = + (TypeToken) resolveSupertype(interfaceType); + builder.add(resolvedInterface); + } + return builder.build(); + } + + private ImmutableList> boundsAsInterfaces(Type[] bounds) { + ImmutableList.Builder> builder = ImmutableList.builder(); + for (Type bound : bounds) { + @SuppressWarnings("unchecked") // upper bound of T + TypeToken boundType = (TypeToken) of(bound); + if (boundType.getRawType().isInterface()) { + builder.add(boundType); + } + } + return builder.build(); + } + + /** + * Returns the set of interfaces and classes that this type is or is a subtype of. The returned + * types are parameterized with proper type arguments. + * + *

Subtypes are always listed before supertypes. But the reverse is not true. A type isn't + * necessarily a subtype of all the types following. Order between types without subtype + * relationship is arbitrary and not guaranteed. + * + *

Specifically, returns true if any of the following conditions is met: + * + *

    + *
  1. 'this' and {@code formalType} are equal. + *
  2. 'this' and {@code formalType} have equal canonical form. + *
  3. {@code formalType} is {@code } and 'this' is a subtype of {@code Foo}. + *
  4. {@code formalType} is {@code } and 'this' is a supertype of {@code Foo}. + *
+ * + * Note that condition 2 isn't technically accurate under the context of a recursively bounded + * type variables. For example, {@code Enum>} canonicalizes to {@code Enum} + * where {@code E} is the type variable declared on the {@code Enum} class declaration. It's + * technically not true that {@code Foo>>} is a subtype of {@code + * Foo>} according to JLS. See testRecursiveWildcardSubtypeBug() for a real example. + * + *

It appears that properly handling recursive type bounds in the presence of implicit type + * bounds is not easy. For now we punt, hoping that this defect should rarely cause issues in real + * code. + * + * @param formalType is {@code Foo} a supertype of {@code Foo}? + * @param declaration The type variable in the context of a parameterized type. Used to infer type + * bound when {@code formalType} is a wildcard with implicit upper bound. + */ + private boolean is(Type formalType, TypeVariable declaration) { + if (runtimeType.equals(formalType)) { + return true; + } + if (formalType instanceof WildcardType) { + WildcardType your = canonicalizeWildcardType(declaration, (WildcardType) formalType); + // if "formalType" is , "this" can be: + // Foo, SubFoo, , , or + // . + // if "formalType" is , "this" can be: + // Foo, SuperFoo, or . + return every(your.getUpperBounds()).isSupertypeOf(runtimeType) + && every(your.getLowerBounds()).isSubtypeOf(runtimeType); + } + return canonicalizeWildcardsInType(runtimeType).equals(canonicalizeWildcardsInType(formalType)); + } + + /** + * In reflection, {@code Foo.getUpperBounds()[0]} is always {@code Object.class}, even when Foo + * is defined as {@code Foo}. Thus directly calling {@code .is(String.class)} + * will return false. To mitigate, we canonicalize wildcards by enforcing the following + * invariants: + * + *

    + *
  1. {@code canonicalize(t)} always produces the equal result for equivalent types. For + * example both {@code Enum} and {@code Enum>} canonicalize to {@code + * Enum}. + *
  2. {@code canonicalize(t)} produces a "literal" supertype of t. For example: {@code Enum>} canonicalizes to {@code Enum}, which is a supertype (if we disregard + * the upper bound is implicitly an Enum too). + *
  3. If {@code canonicalize(A) == canonicalize(B)}, then {@code Foo.isSubtypeOf(Foo)} + * and vice versa. i.e. {@code A.is(B)} and {@code B.is(A)}. + *
  4. {@code canonicalize(canonicalize(A)) == canonicalize(A)}. + *
+ */ + private static Type canonicalizeTypeArg(TypeVariable declaration, Type typeArg) { + return typeArg instanceof WildcardType + ? canonicalizeWildcardType(declaration, ((WildcardType) typeArg)) + : canonicalizeWildcardsInType(typeArg); + } + + private static Type canonicalizeWildcardsInType(Type type) { + if (type instanceof ParameterizedType) { + return canonicalizeWildcardsInParameterizedType((ParameterizedType) type); + } + if (type instanceof GenericArrayType) { + return Types.newArrayType( + canonicalizeWildcardsInType(((GenericArrayType) type).getGenericComponentType())); + } + return type; + } + + // WARNING: the returned type may have empty upper bounds, which may violate common expectations + // by user code or even some of our own code. It's fine for the purpose of checking subtypes. + // Just don't ever let the user access it. + private static WildcardType canonicalizeWildcardType( + TypeVariable declaration, WildcardType type) { + Type[] declared = declaration.getBounds(); + List upperBounds = new ArrayList<>(); + for (Type bound : type.getUpperBounds()) { + if (!any(declared).isSubtypeOf(bound)) { + upperBounds.add(canonicalizeWildcardsInType(bound)); + } + } + return new Types.WildcardTypeImpl(type.getLowerBounds(), upperBounds.toArray(new Type[0])); + } + + private static ParameterizedType canonicalizeWildcardsInParameterizedType( + ParameterizedType type) { + Class rawType = (Class) type.getRawType(); + TypeVariable[] typeVars = rawType.getTypeParameters(); + Type[] typeArgs = type.getActualTypeArguments(); + for (int i = 0; i < typeArgs.length; i++) { + typeArgs[i] = canonicalizeTypeArg(typeVars[i], typeArgs[i]); + } + return Types.newParameterizedTypeWithOwner(type.getOwnerType(), rawType, typeArgs); + } + + private static Bounds every(Type[] bounds) { + // Every bound must match. On any false, result is false. + return new Bounds(bounds, false); + } + + private static Bounds any(Type[] bounds) { + // Any bound matches. On any true, result is true. + return new Bounds(bounds, true); + } + + private static class Bounds { + private final Type[] bounds; + private final boolean target; + + Bounds(Type[] bounds, boolean target) { + this.bounds = bounds; + this.target = target; + } + + boolean isSubtypeOf(Type supertype) { + for (Type bound : bounds) { + if (of(bound).isSubtypeOf(supertype) == target) { + return target; + } + } + return !target; + } + + boolean isSupertypeOf(Type subtype) { + TypeToken type = of(subtype); + for (Type bound : bounds) { + if (type.isSubtypeOf(bound) == target) { + return target; + } + } + return !target; + } + } + + private ImmutableSet> getRawTypes() { + ImmutableSet.Builder> builder = ImmutableSet.builder(); + new TypeVisitor() { + @Override + void visitTypeVariable(TypeVariable t) { + visit(t.getBounds()); + } + + @Override + void visitWildcardType(WildcardType t) { + visit(t.getUpperBounds()); + } + + @Override + void visitParameterizedType(ParameterizedType t) { + builder.add((Class) t.getRawType()); + } + + @Override + void visitClass(Class t) { + builder.add(t); + } + + @Override + void visitGenericArrayType(GenericArrayType t) { + builder.add(Types.getArrayClass(of(t.getGenericComponentType()).getRawType())); + } + }.visit(runtimeType); + // Cast from ImmutableSet> to ImmutableSet> + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableSet> result = (ImmutableSet) builder.build(); + return result; + } + + private boolean isOwnedBySubtypeOf(Type supertype) { + for (TypeToken type : getTypes()) { + Type ownerType = type.getOwnerTypeIfPresent(); + if (ownerType != null && of(ownerType).isSubtypeOf(supertype)) { + return true; + } + } + return false; + } + + /** + * Returns the owner type of a {@link ParameterizedType} or enclosing class of a {@link Class}, or + * null otherwise. + */ + private Type getOwnerTypeIfPresent() { + if (runtimeType instanceof ParameterizedType) { + return ((ParameterizedType) runtimeType).getOwnerType(); + } else if (runtimeType instanceof Class) { + return ((Class) runtimeType).getEnclosingClass(); + } else { + return null; + } + } + + /** + * Returns the type token representing the generic type declaration of {@code cls}. For example: + * {@code TypeToken.getGenericType(Iterable.class)} returns {@code Iterable}. + * + *

If {@code cls} isn't parameterized and isn't a generic array, the type token of the class is + * returned. + */ + @VisibleForTesting + static TypeToken toGenericType(Class cls) { + if (cls.isArray()) { + Type arrayOfGenericType = + Types.newArrayType( + // If we are passed with int[].class, don't turn it to GenericArrayType + toGenericType(cls.getComponentType()).runtimeType); + @SuppressWarnings("unchecked") // array is covariant + TypeToken result = (TypeToken) of(arrayOfGenericType); + return result; + } + TypeVariable>[] typeParams = cls.getTypeParameters(); + Type ownerType = + cls.isMemberClass() && !Modifier.isStatic(cls.getModifiers()) + ? toGenericType(cls.getEnclosingClass()).runtimeType + : null; + + if ((typeParams.length > 0) || ((ownerType != null) && ownerType != cls.getEnclosingClass())) { + @SuppressWarnings("unchecked") // Like, it's Iterable for Iterable.class + TypeToken type = + (TypeToken) + of(Types.newParameterizedTypeWithOwner(ownerType, cls, typeParams)); + return type; + } else { + return of(cls); + } + } + + private TypeResolver getCovariantTypeResolver() { + TypeResolver resolver = covariantTypeResolver; + if (resolver == null) { + resolver = (covariantTypeResolver = TypeResolver.covariantly(runtimeType)); + } + return resolver; + } + + private TypeResolver getInvariantTypeResolver() { + TypeResolver resolver = invariantTypeResolver; + if (resolver == null) { + resolver = (invariantTypeResolver = TypeResolver.invariantly(runtimeType)); + } + return resolver; + } + + private TypeToken getSupertypeFromUpperBounds( + Class supertype, Type[] upperBounds) { + for (Type upperBound : upperBounds) { + @SuppressWarnings("unchecked") // T's upperbound is . + TypeToken bound = (TypeToken) of(upperBound); + if (bound.isSubtypeOf(supertype)) { + @SuppressWarnings({"rawtypes", "unchecked"}) // guarded by the isSubtypeOf check. + TypeToken result = bound.getSupertype((Class) supertype); + return result; + } + } + throw new IllegalArgumentException(supertype + " isn't a super type of " + this); + } + + private TypeToken getSubtypeFromLowerBounds(Class subclass, Type[] lowerBounds) { + if (lowerBounds.length > 0) { + @SuppressWarnings("unchecked") // T's lower bound is + TypeToken bound = (TypeToken) of(lowerBounds[0]); + // Java supports only one lowerbound anyway. + return bound.getSubtype(subclass); + } + throw new IllegalArgumentException(subclass + " isn't a subclass of " + this); + } + + private TypeToken getArraySupertype(Class supertype) { + // with component type, we have lost generic type information + // Use raw type so that compiler allows us to call getSupertype() + @SuppressWarnings("rawtypes") + TypeToken componentType = getComponentType(); + // TODO(cpovirk): checkArgument? + if (componentType == null) { + throw new IllegalArgumentException(supertype + " isn't a super type of " + this); + } + // array is covariant. component type is super type, so is the array type. + @SuppressWarnings("unchecked") // going from raw type back to generics + /* + * requireNonNull is safe because we call getArraySupertype only after checking + * supertype.isArray(). + */ + TypeToken componentSupertype = + componentType.getSupertype(requireNonNull(supertype.getComponentType())); + @SuppressWarnings("unchecked") // component type is super type, so is array type. + TypeToken result = + (TypeToken) + // If we are passed with int[].class, don't turn it to GenericArrayType + of(newArrayClassOrGenericArrayType(componentSupertype.runtimeType)); + return result; + } + + private TypeToken getArraySubtype(Class subclass) { + Class subclassComponentType = subclass.getComponentType(); + if (subclassComponentType == null) { + throw new IllegalArgumentException(subclass + " does not appear to be a subtype of " + this); + } + // array is covariant. component type is subtype, so is the array type. + // requireNonNull is safe because we call getArraySubtype only when isArray(). + TypeToken componentSubtype = + requireNonNull(getComponentType()).getSubtype(subclassComponentType); + @SuppressWarnings("unchecked") // component type is subtype, so is array type. + TypeToken result = + (TypeToken) + // If we are passed with int[].class, don't turn it to GenericArrayType + of(newArrayClassOrGenericArrayType(componentSubtype.runtimeType)); + return result; + } + + private Type resolveTypeArgsForSubclass(Class subclass) { + // If both runtimeType and subclass are not parameterized, return subclass + // If runtimeType is not parameterized but subclass is, process subclass as a parameterized type + // If runtimeType is a raw type (i.e. is a parameterized type specified as a Class), we + // return subclass as a raw type + if (runtimeType instanceof Class + && ((subclass.getTypeParameters().length == 0) + || (getRawType().getTypeParameters().length != 0))) { + // no resolution needed + return subclass; + } + // class Base {} + // class Sub extends Base {} + // Base.subtype(Sub.class): + + // Sub.getSupertype(Base.class) => Base + // => X=String, Y=Integer + // => Sub=Sub + TypeToken genericSubtype = toGenericType(subclass); + @SuppressWarnings({"rawtypes", "unchecked"}) // subclass isn't + Type supertypeWithArgsFromSubtype = + genericSubtype.getSupertype((Class) getRawType()).runtimeType; + return new TypeResolver() + .where(supertypeWithArgsFromSubtype, runtimeType) + .resolveType(genericSubtype.runtimeType); + } + + /** + * Creates an array class if {@code componentType} is a class, or else, a {@link + * GenericArrayType}. This is what Java7 does for generic array type parameters. + */ + private static Type newArrayClassOrGenericArrayType(Type componentType) { + return Types.JavaVersion.JAVA7.newArrayType(componentType); + } + + private static final class SimpleTypeToken extends TypeToken { + + SimpleTypeToken(Type type) { + super(type); + } + + private static final long serialVersionUID = 0; + } + + /** + * Collects parent types from a subtype. + * + * @param The type "kind". Either a TypeToken, or Class. + */ + private abstract static class TypeCollector { + + static final TypeCollector> FOR_GENERIC_TYPE = + new TypeCollector>() { + @Override + Class getRawType(TypeToken type) { + return type.getRawType(); + } + + @Override + Iterable> getInterfaces(TypeToken type) { + return type.getGenericInterfaces(); + } + + @Override + TypeToken getSuperclass(TypeToken type) { + return type.getGenericSuperclass(); + } + }; + + static final TypeCollector> FOR_RAW_TYPE = + new TypeCollector>() { + @Override + Class getRawType(Class type) { + return type; + } + + @Override + Iterable> getInterfaces(Class type) { + return Arrays.asList(type.getInterfaces()); + } + + @Override + Class getSuperclass(Class type) { + return type.getSuperclass(); + } + }; + + /** For just classes, we don't have to traverse interfaces. */ + final TypeCollector classesOnly() { + return new ForwardingTypeCollector(this) { + @Override + Iterable getInterfaces(K type) { + return ImmutableSet.of(); + } + + @Override + ImmutableList collectTypes(Iterable types) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (K type : types) { + if (!getRawType(type).isInterface()) { + builder.add(type); + } + } + return super.collectTypes(builder.build()); + } + }; + } + + final ImmutableList collectTypes(K type) { + return collectTypes(ImmutableList.of(type)); + } + + ImmutableList collectTypes(Iterable types) { + // type -> order number. 1 for Object, 2 for anything directly below, so on so forth. + Map map = Maps.newHashMap(); + for (K type : types) { + collectTypes(type, map); + } + return sortKeysByValue(map, Ordering.natural().reverse()); + } + + /** Collects all types to map, and returns the total depth from T up to Object. */ + private int collectTypes(K type, Map map) { + Integer existing = map.get(type); + if (existing != null) { + // short circuit: if set contains type it already contains its supertypes + return existing; + } + // Interfaces should be listed before Object. + int aboveMe = getRawType(type).isInterface() ? 1 : 0; + for (K interfaceType : getInterfaces(type)) { + aboveMe = Math.max(aboveMe, collectTypes(interfaceType, map)); + } + K superclass = getSuperclass(type); + if (superclass != null) { + aboveMe = Math.max(aboveMe, collectTypes(superclass, map)); + } + /* + * TODO(benyu): should we include Object for interface? Also, CharSequence[] and Object[] for + * String[]? + * + */ + map.put(type, aboveMe + 1); + return aboveMe + 1; + } + + private static ImmutableList sortKeysByValue( + Map map, Comparator valueComparator) { + Ordering keyOrdering = + new Ordering() { + @Override + public int compare(K left, K right) { + // requireNonNull is safe because we are passing keys in the map. + return valueComparator.compare( + requireNonNull(map.get(left)), requireNonNull(map.get(right))); + } + }; + return keyOrdering.immutableSortedCopy(map.keySet()); + } + + abstract Class getRawType(K type); + + abstract Iterable getInterfaces(K type); + + abstract K getSuperclass(K type); + + private static class ForwardingTypeCollector extends TypeCollector { + + private final TypeCollector delegate; + + ForwardingTypeCollector(TypeCollector delegate) { + this.delegate = delegate; + } + + @Override + Class getRawType(K type) { + return delegate.getRawType(type); + } + + @Override + Iterable getInterfaces(K type) { + return delegate.getInterfaces(type); + } + + @Override + K getSuperclass(K type) { + return delegate.getSuperclass(type); + } + } + } + + // This happens to be the hash of the class as of now. So setting it makes a backward compatible + // change. Going forward, if any incompatible change is added, we can change the UID back to 1. + private static final long serialVersionUID = 3637540370352322684L; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeVisitor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeVisitor.java new file mode 100644 index 0000000000000..23fc48aaa5946 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/TypeVisitor.java @@ -0,0 +1,110 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.collect.Sets; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Set; + +/** + * Based on what a {@link Type} is, dispatch it to the corresponding {@code visit*} method. By + * default, no recursion is done for type arguments or type bounds. But subclasses can opt to do + * recursion by calling {@link #visit} for any {@code Type} while visitation is in progress. For + * example, this can be used to reject wildcards or type variables contained in a type as in: + * + *

{@code
+ * new TypeVisitor() {
+ *   protected void visitParameterizedType(ParameterizedType t) {
+ *     visit(t.getOwnerType());
+ *     visit(t.getActualTypeArguments());
+ *   }
+ *   protected void visitGenericArrayType(GenericArrayType t) {
+ *     visit(t.getGenericComponentType());
+ *   }
+ *   protected void visitTypeVariable(TypeVariable t) {
+ *     throw new IllegalArgumentException("Cannot contain type variable.");
+ *   }
+ *   protected void visitWildcardType(WildcardType t) {
+ *     throw new IllegalArgumentException("Cannot contain wildcard type.");
+ *   }
+ * }.visit(type);
+ * }
+ * + *

One {@code Type} is visited at most once. The second time the same type is visited, it's + * ignored by {@link #visit}. This avoids infinite recursion caused by recursive type bounds. + * + *

This class is not thread safe. + * + * @author Ben Yu + */ +@ElementTypesAreNonnullByDefault +abstract class TypeVisitor { + + private final Set visited = Sets.newHashSet(); + + /** + * Visits the given types. Null types are ignored. This allows subclasses to call {@code + * visit(parameterizedType.getOwnerType())} safely without having to check nulls. + */ + public final void visit(Type... types) { + for (Type type : types) { + if (type == null || !visited.add(type)) { + // null owner type, or already visited; + continue; + } + boolean succeeded = false; + try { + if (type instanceof TypeVariable) { + visitTypeVariable((TypeVariable) type); + } else if (type instanceof WildcardType) { + visitWildcardType((WildcardType) type); + } else if (type instanceof ParameterizedType) { + visitParameterizedType((ParameterizedType) type); + } else if (type instanceof Class) { + visitClass((Class) type); + } else if (type instanceof GenericArrayType) { + visitGenericArrayType((GenericArrayType) type); + } else { + throw new AssertionError("Unknown type: " + type); + } + succeeded = true; + } finally { + if (!succeeded) { // When the visitation failed, we don't want to ignore the second. + visited.remove(type); + } + } + } + } + + void visitClass(Class t) {} + + void visitGenericArrayType(GenericArrayType t) {} + + void visitParameterizedType(ParameterizedType t) {} + + void visitTypeVariable(TypeVariable t) {} + + void visitWildcardType(WildcardType t) {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Types.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Types.java new file mode 100644 index 0000000000000..44d774a238497 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/Types.java @@ -0,0 +1,684 @@ +/* + * 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.iotdb.google.common.reflect; + +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.base.Objects; +import org.apache.iotdb.google.common.base.Predicates; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.Iterables; + +import java.io.Serializable; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.security.AccessControlException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.collect.Iterables.transform; + +/** + * Utilities for working with {@link Type}. + * + * @author Ben Yu + */ +@ElementTypesAreNonnullByDefault +final class Types { + + /** Class#toString without the "class " and "interface " prefixes */ + private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null"); + + /** Returns the array type of {@code componentType}. */ + static Type newArrayType(Type componentType) { + if (componentType instanceof WildcardType) { + WildcardType wildcard = (WildcardType) componentType; + Type[] lowerBounds = wildcard.getLowerBounds(); + checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds."); + if (lowerBounds.length == 1) { + return supertypeOf(newArrayType(lowerBounds[0])); + } else { + Type[] upperBounds = wildcard.getUpperBounds(); + checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound."); + return subtypeOf(newArrayType(upperBounds[0])); + } + } + return JavaVersion.CURRENT.newArrayType(componentType); + } + + /** + * Returns a type where {@code rawType} is parameterized by {@code arguments} and is owned by + * {@code ownerType}. + */ + static ParameterizedType newParameterizedTypeWithOwner( + Type ownerType, Class rawType, Type... arguments) { + if (ownerType == null) { + return newParameterizedType(rawType, arguments); + } + // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE + checkNotNull(arguments); + checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType); + return new ParameterizedTypeImpl(ownerType, rawType, arguments); + } + + /** Returns a type where {@code rawType} is parameterized by {@code arguments}. */ + static ParameterizedType newParameterizedType(Class rawType, Type... arguments) { + return new ParameterizedTypeImpl( + ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments); + } + + /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */ + private enum ClassOwnership { + OWNED_BY_ENCLOSING_CLASS { + @Override + Class getOwnerType(Class rawType) { + return rawType.getEnclosingClass(); + } + }, + LOCAL_CLASS_HAS_NO_OWNER { + @Override + Class getOwnerType(Class rawType) { + if (rawType.isLocalClass()) { + return null; + } else { + return rawType.getEnclosingClass(); + } + } + }; + + abstract Class getOwnerType(Class rawType); + + static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior(); + + private static ClassOwnership detectJvmBehavior() { + class LocalClass {} + Class subclass = new LocalClass() {}.getClass(); + // requireNonNull is safe because we're examining a type that's known to have a superclass. + ParameterizedType parameterizedType = + requireNonNull((ParameterizedType) subclass.getGenericSuperclass()); + for (ClassOwnership behavior : ClassOwnership.values()) { + if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) { + return behavior; + } + } + throw new AssertionError(); + } + } + + /** + * Returns a new {@link TypeVariable} that belongs to {@code declaration} with {@code name} and + * {@code bounds}. + */ + static TypeVariable newArtificialTypeVariable( + D declaration, String name, Type... bounds) { + return newTypeVariableImpl( + declaration, name, (bounds.length == 0) ? new Type[] {Object.class} : bounds); + } + + /** Returns a new {@link WildcardType} with {@code upperBound}. */ + @VisibleForTesting + static WildcardType subtypeOf(Type upperBound) { + return new WildcardTypeImpl(new Type[0], new Type[] {upperBound}); + } + + /** Returns a new {@link WildcardType} with {@code lowerBound}. */ + @VisibleForTesting + static WildcardType supertypeOf(Type lowerBound) { + return new WildcardTypeImpl(new Type[] {lowerBound}, new Type[] {Object.class}); + } + + /** + * Returns a human-readable string representation of {@code type}. + * + *

The format is subject to change. + */ + static String toString(Type type) { + return (type instanceof Class) ? ((Class) type).getName() : type.toString(); + } + + static Type getComponentType(Type type) { + checkNotNull(type); + AtomicReference result = new AtomicReference<>(); + new TypeVisitor() { + @Override + void visitTypeVariable(TypeVariable t) { + result.set(subtypeOfComponentType(t.getBounds())); + } + + @Override + void visitWildcardType(WildcardType t) { + result.set(subtypeOfComponentType(t.getUpperBounds())); + } + + @Override + void visitGenericArrayType(GenericArrayType t) { + result.set(t.getGenericComponentType()); + } + + @Override + void visitClass(Class t) { + result.set(t.getComponentType()); + } + }.visit(type); + return result.get(); + } + + /** + * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null + * otherwise. + */ + private static Type subtypeOfComponentType(Type[] bounds) { + for (Type bound : bounds) { + Type componentType = getComponentType(bound); + if (componentType != null) { + // Only the first bound can be a class or array. + // Bounds after the first can only be interfaces. + if (componentType instanceof Class) { + Class componentClass = (Class) componentType; + if (componentClass.isPrimitive()) { + return componentClass; + } + } + return subtypeOf(componentType); + } + } + return null; + } + + private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable { + + private final Type componentType; + + GenericArrayTypeImpl(Type componentType) { + this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType); + } + + @Override + public Type getGenericComponentType() { + return componentType; + } + + @Override + public String toString() { + return Types.toString(componentType) + "[]"; + } + + @Override + public int hashCode() { + return componentType.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof GenericArrayType) { + GenericArrayType that = (GenericArrayType) obj; + return Objects.equal(getGenericComponentType(), that.getGenericComponentType()); + } + return false; + } + + private static final long serialVersionUID = 0; + } + + private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { + + private final Type ownerType; + private final ImmutableList argumentsList; + private final Class rawType; + + ParameterizedTypeImpl(Type ownerType, Class rawType, Type[] typeArguments) { + checkNotNull(rawType); + checkArgument(typeArguments.length == rawType.getTypeParameters().length); + disallowPrimitiveType(typeArguments, "type parameter"); + this.ownerType = ownerType; + this.rawType = rawType; + this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments); + } + + @Override + public Type[] getActualTypeArguments() { + return toArray(argumentsList); + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return ownerType; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (ownerType != null && JavaVersion.CURRENT.jdkTypeDuplicatesOwnerName()) { + builder.append(JavaVersion.CURRENT.typeName(ownerType)).append('.'); + } + return builder + .append(rawType.getName()) + .append('<') + .append(COMMA_JOINER.join(transform(argumentsList, JavaVersion.CURRENT::typeName))) + .append('>') + .toString(); + } + + @Override + public int hashCode() { + return (ownerType == null ? 0 : ownerType.hashCode()) + ^ argumentsList.hashCode() + ^ rawType.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ParameterizedType)) { + return false; + } + ParameterizedType that = (ParameterizedType) other; + return getRawType().equals(that.getRawType()) + && Objects.equal(getOwnerType(), that.getOwnerType()) + && Arrays.equals(getActualTypeArguments(), that.getActualTypeArguments()); + } + + private static final long serialVersionUID = 0; + } + + private static TypeVariable newTypeVariableImpl( + D genericDeclaration, String name, Type[] bounds) { + TypeVariableImpl typeVariableImpl = new TypeVariableImpl<>(genericDeclaration, name, bounds); + @SuppressWarnings("unchecked") + TypeVariable typeVariable = + Reflection.newProxy( + TypeVariable.class, new TypeVariableInvocationHandler(typeVariableImpl)); + return typeVariable; + } + + /** + * Invocation handler to work around a compatibility problem between Java 7 and Java 8. + * + *

Java 8 introduced a new method {@code getAnnotatedBounds()} in the {@link TypeVariable} + * interface, whose return type {@code AnnotatedType[]} is also new in Java 8. That means that we + * cannot implement that interface in source code in a way that will compile on both Java 7 and + * Java 8. If we include the {@code getAnnotatedBounds()} method then its return type means it + * won't compile on Java 7, while if we don't include the method then the compiler will complain + * that an abstract method is unimplemented. So instead we use a dynamic proxy to get an + * implementation. If the method being called on the {@code TypeVariable} instance has the same + * name as one of the public methods of {@link TypeVariableImpl}, the proxy calls the same method + * on its instance of {@code TypeVariableImpl}. Otherwise it throws {@link + * UnsupportedOperationException}; this should only apply to {@code getAnnotatedBounds()}. This + * does mean that users on Java 8 who obtain an instance of {@code TypeVariable} from {@link + * TypeResolver#resolveType} will not be able to call {@code getAnnotatedBounds()} on it, but that + * should hopefully be rare. + * + *

TODO(b/147144588): We are currently also missing the methods inherited from {@link + * AnnotatedElement}, which {@code TypeVariable} began to extend only in Java 8. Those methods + * refer only to types present in Java 7, so we could implement them in {@code TypeVariableImpl} + * today. (We could probably then make {@code TypeVariableImpl} implement {@code AnnotatedElement} + * so that we get partial compile-time checking.) + * + *

By default this method does nothing. + */ + protected void startUp() throws Exception {} + + /** + * Run the service. This method is invoked on the execution thread. Implementations must respond + * to stop requests. You could poll for lifecycle changes in a work loop: + * + *

+   *   public void run() {
+   *     while ({@link #isRunning()}) {
+   *       // perform a unit of work
+   *     }
+   *   }
+   * 
+ * + *

...or you could respond to stop requests by implementing {@link #triggerShutdown()}, which + * should cause {@link #run()} to return. + */ + protected abstract void run() throws Exception; + + /** + * Stop the service. This method is invoked on the execution thread. + * + *

By default this method does nothing. + */ + // TODO: consider supporting a TearDownTestCase-like API + protected void shutDown() throws Exception {} + + /** + * Invoked to request the service to stop. + * + *

By default this method does nothing. + * + *

Currently, this method is invoked while holding a lock. If an implementation of this method + * blocks, it can prevent this service from changing state. If you need to performing a blocking + * operation in order to trigger shutdown, consider instead registering a listener and + * implementing {@code stopping}. Note, however, that {@code stopping} does not run at exactly the + * same times as {@code triggerShutdown}. + */ + protected void triggerShutdown() {} + + /** + * Returns the {@link Executor} that will be used to run this service. Subclasses may override + * this method to use a custom {@link Executor}, which may configure its worker thread with a + * specific name, thread group or priority. The returned executor's {@link + * Executor#execute(Runnable) execute()} method is called when this service is started, and should + * return promptly. + * + *

The default implementation returns a new {@link Executor} that sets the name of its threads + * to the string returned by {@link #serviceName} + */ + protected Executor executor() { + return command -> MoreExecutors.newThread(serviceName(), command).start(); + } + + @Override + public String toString() { + return serviceName() + " [" + state() + "]"; + } + + @Override + public final boolean isRunning() { + return delegate.isRunning(); + } + + @Override + public final State state() { + return delegate.state(); + } + + /** + * @since 13.0 + */ + @Override + public final void addListener(Listener listener, Executor executor) { + delegate.addListener(listener, executor); + } + + /** + * @since 14.0 + */ + @Override + public final Throwable failureCause() { + return delegate.failureCause(); + } + + /** + * @since 15.0 + */ + @Override + public final Service startAsync() { + delegate.startAsync(); + return this; + } + + /** + * @since 15.0 + */ + @Override + public final Service stopAsync() { + delegate.stopAsync(); + return this; + } + + /** + * @since 15.0 + */ + @Override + public final void awaitRunning() { + delegate.awaitRunning(); + } + + /** + * @since 28.0 + */ + @Override + public final void awaitRunning(Duration timeout) throws TimeoutException { + Service.super.awaitRunning(timeout); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + delegate.awaitRunning(timeout, unit); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitTerminated() { + delegate.awaitTerminated(); + } + + /** + * @since 28.0 + */ + @Override + public final void awaitTerminated(Duration timeout) throws TimeoutException { + Service.super.awaitTerminated(timeout); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + delegate.awaitTerminated(timeout, unit); + } + + /** + * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name + * in debugging output. + * + *

Subclasses may override this method. + * + * @since 14.0 (present in 10.0 as getServiceName) + */ + protected String serviceName() { + return getClass().getSimpleName(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractFuture.java new file mode 100644 index 0000000000000..0bc766d7a5734 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractFuture.java @@ -0,0 +1,1546 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Strings; +import org.apache.iotdb.google.common.util.concurrent.internal.InternalFutureFailureAccess; +import org.apache.iotdb.google.common.util.concurrent.internal.InternalFutures; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Locale; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.concurrent.locks.LockSupport; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.lang.Integer.toHexString; +import static java.lang.System.identityHashCode; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.NullnessCasts.uncheckedNull; + +/** + * An abstract implementation of {@link ListenableFuture}, intended for advanced users only. More + * common ways to create a {@code ListenableFuture} include instantiating a {@link SettableFuture}, + * submitting a task to a {@link ListeningExecutorService}, and deriving a {@code Future} from an + * existing one, typically using methods like {@link Futures#transform(ListenableFuture, + * org.apache.iotdb.google.common.base.Function, java.util.concurrent.Executor) Futures.transform} + * and {@link Futures#catching(ListenableFuture, Class, + * org.apache.iotdb.google.common.base.Function, java.util.concurrent.Executor) Futures.catching}. + * + *

This class implements all methods in {@code ListenableFuture}. Subclasses should provide a way + * to set the result of the computation through the protected methods {@link #set(Object)}, {@link + * #setFuture(ListenableFuture)} and {@link #setException(Throwable)}. Subclasses may also override + * {@link #afterDone()}, which will be invoked automatically when the future completes. Subclasses + * should rarely override other methods. + * + * @author Sven Mawson + * @author Luke Sandberg + * @since 1.0 + */ +@SuppressWarnings({ + "ShortCircuitBoolean", // we use non-short circuiting comparisons intentionally + "nullness", // TODO(b/147136275): Remove once our checker understands & and |. +}) +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class AbstractFuture extends InternalFutureFailureAccess + implements ListenableFuture { + // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || + + static final boolean GENERATE_CANCELLATION_CAUSES; + + static { + // System.getProperty may throw if the security policy does not permit access. + boolean generateCancellationCauses; + try { + generateCancellationCauses = + Boolean.parseBoolean( + System.getProperty("guava.concurrent.generate_cancellation_cause", "false")); + } catch (SecurityException e) { + generateCancellationCauses = false; + } + GENERATE_CANCELLATION_CAUSES = generateCancellationCauses; + } + + /** + * Tag interface marking trusted subclasses. This enables some optimizations. The implementation + * of this interface must also be an AbstractFuture and must not override or expose for overriding + * any of the public methods of ListenableFuture. + */ + interface Trusted extends ListenableFuture {} + + /** + * A less abstract subclass of AbstractFuture. This can be used to optimize setFuture by ensuring + * that {@link #get} calls exactly the implementation of {@link AbstractFuture#get}. + */ + abstract static class TrustedFuture extends AbstractFuture + implements Trusted { + @Override + @ParametricNullness + public final V get() throws InterruptedException, ExecutionException { + return super.get(); + } + + @Override + @ParametricNullness + public final V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return super.get(timeout, unit); + } + + @Override + public final boolean isDone() { + return super.isDone(); + } + + @Override + public final boolean isCancelled() { + return super.isCancelled(); + } + + @Override + public final void addListener(Runnable listener, Executor executor) { + super.addListener(listener, executor); + } + + @Override + public final boolean cancel(boolean mayInterruptIfRunning) { + return super.cancel(mayInterruptIfRunning); + } + } + + // Logger to log exceptions caught when running listeners. + private static final Logger log = Logger.getLogger(AbstractFuture.class.getName()); + + // A heuristic for timed gets. If the remaining timeout is less than this, spin instead of + // blocking. This value is what AbstractQueuedSynchronizer uses. + private static final long SPIN_THRESHOLD_NANOS = 1000L; + + private static final AtomicHelper ATOMIC_HELPER; + + static { + AtomicHelper helper; + Throwable thrownUnsafeFailure = null; + Throwable thrownAtomicReferenceFieldUpdaterFailure = null; + + try { + helper = new UnsafeAtomicHelper(); + } catch (RuntimeException | Error unsafeFailure) { + thrownUnsafeFailure = unsafeFailure; + // catch absolutely everything and fall through to our 'SafeAtomicHelper' + // The access control checks that ARFU does means the caller class has to be AbstractFuture + // instead of SafeAtomicHelper, so we annoyingly define these here + try { + helper = + new SafeAtomicHelper( + newUpdater(Waiter.class, Thread.class, "thread"), + newUpdater(Waiter.class, Waiter.class, "next"), + newUpdater(AbstractFuture.class, Waiter.class, "waiters"), + newUpdater(AbstractFuture.class, Listener.class, "listeners"), + newUpdater(AbstractFuture.class, Object.class, "value")); + } catch (RuntimeException | Error atomicReferenceFieldUpdaterFailure) { + // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause + // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. + // For these users fallback to a suboptimal implementation, based on synchronized. This will + // be a definite performance hit to those users. + thrownAtomicReferenceFieldUpdaterFailure = atomicReferenceFieldUpdaterFailure; + helper = new SynchronizedHelper(); + } + } + ATOMIC_HELPER = helper; + + // Prevent rare disastrous classloading in first call to LockSupport.park. + // See: https://bugs.openjdk.java.net/browse/JDK-8074773 + @SuppressWarnings("unused") + Class ensureLoaded = LockSupport.class; + + // Log after all static init is finished; if an installed logger uses any Futures methods, it + // shouldn't break in cases where reflection is missing/broken. + if (thrownAtomicReferenceFieldUpdaterFailure != null) { + log.log(Level.SEVERE, "UnsafeAtomicHelper is broken!", thrownUnsafeFailure); + log.log( + Level.SEVERE, "SafeAtomicHelper is broken!", thrownAtomicReferenceFieldUpdaterFailure); + } + } + + /** Waiter links form a Treiber stack, in the {@link #waiters} field. */ + private static final class Waiter { + static final Waiter TOMBSTONE = new Waiter(false /* ignored param */); + + volatile Thread thread; + volatile Waiter next; + + /** + * Constructor for the TOMBSTONE, avoids use of ATOMIC_HELPER in case this class is loaded + * before the ATOMIC_HELPER. Apparently this is possible on some android platforms. + */ + Waiter(boolean unused) {} + + Waiter() { + // avoid volatile write, write is made visible by subsequent CAS on waiters field + ATOMIC_HELPER.putThread(this, Thread.currentThread()); + } + + // non-volatile write to the next field. Should be made visible by subsequent CAS on waiters + // field. + void setNext(Waiter next) { + ATOMIC_HELPER.putNext(this, next); + } + + void unpark() { + // This is racy with removeWaiter. The consequence of the race is that we may spuriously call + // unpark even though the thread has already removed itself from the list. But even if we did + // use a CAS, that race would still exist (it would just be ever so slightly smaller). + Thread w = thread; + if (w != null) { + thread = null; + LockSupport.unpark(w); + } + } + } + + /** + * Marks the given node as 'deleted' (null waiter) and then scans the list to unlink all deleted + * nodes. This is an O(n) operation in the common case (and O(n^2) in the worst), but we are saved + * by two things. + * + *

    + *
  • This is only called when a waiting thread times out or is interrupted. Both of which + * should be rare. + *
  • The waiters list should be very short. + *
+ */ + private void removeWaiter(Waiter node) { + node.thread = null; // mark as 'deleted' + restart: + while (true) { + Waiter pred = null; + Waiter curr = waiters; + if (curr == Waiter.TOMBSTONE) { + return; // give up if someone is calling complete + } + Waiter succ; + while (curr != null) { + succ = curr.next; + if (curr.thread != null) { // we aren't unlinking this node, update pred. + pred = curr; + } else if (pred != null) { // We are unlinking this node and it has a predecessor. + pred.next = succ; + if (pred.thread == null) { // We raced with another node that unlinked pred. Restart. + continue restart; + } + } else if (!ATOMIC_HELPER.casWaiters(this, curr, succ)) { // We are unlinking head + continue restart; // We raced with an add or complete + } + curr = succ; + } + break; + } + } + + /** Listeners also form a stack through the {@link #listeners} field. */ + private static final class Listener { + static final Listener TOMBSTONE = new Listener(); + // null only for TOMBSTONE + final Runnable task; + // null only for TOMBSTONE + final Executor executor; + + // writes to next are made visible by subsequent CAS's on the listeners field + Listener next; + + Listener(Runnable task, Executor executor) { + this.task = task; + this.executor = executor; + } + + Listener() { + this.task = null; + this.executor = null; + } + } + + /** A special value to represent {@code null}. */ + private static final Object NULL = new Object(); + + /** A special value to represent failure, when {@link #setException} is called successfully. */ + private static final class Failure { + static final Failure FALLBACK_INSTANCE = + new Failure( + new Throwable("Failure occurred while trying to finish a future.") { + @Override + public synchronized Throwable fillInStackTrace() { + return this; // no stack trace + } + }); + final Throwable exception; + + Failure(Throwable exception) { + this.exception = checkNotNull(exception); + } + } + + /** A special value to represent cancellation and the 'wasInterrupted' bit. */ + private static final class Cancellation { + // constants to use when GENERATE_CANCELLATION_CAUSES = false + static final Cancellation CAUSELESS_INTERRUPTED; + static final Cancellation CAUSELESS_CANCELLED; + + static { + if (GENERATE_CANCELLATION_CAUSES) { + CAUSELESS_CANCELLED = null; + CAUSELESS_INTERRUPTED = null; + } else { + CAUSELESS_CANCELLED = new Cancellation(false, null); + CAUSELESS_INTERRUPTED = new Cancellation(true, null); + } + } + + final boolean wasInterrupted; + final Throwable cause; + + Cancellation(boolean wasInterrupted, Throwable cause) { + this.wasInterrupted = wasInterrupted; + this.cause = cause; + } + } + + /** A special value that encodes the 'setFuture' state. */ + private static final class SetFuture implements Runnable { + final AbstractFuture owner; + final ListenableFuture future; + + SetFuture(AbstractFuture owner, ListenableFuture future) { + this.owner = owner; + this.future = future; + } + + @Override + public void run() { + if (owner.value != this) { + // nothing to do, we must have been cancelled, don't bother inspecting the future. + return; + } + Object valueToSet = getFutureValue(future); + if (ATOMIC_HELPER.casValue(owner, this, valueToSet)) { + complete( + owner, + /* + * Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so + * don't invoke interruptTask. + */ + false); + } + } + } + + // TODO(lukes): investigate using the @Contended annotation on these fields when jdk8 is + // available. + /** + * This field encodes the current state of the future. + * + *

The valid values are: + * + *

    + *
  • {@code null} initial state, nothing has happened. + *
  • {@link Cancellation} terminal state, {@code cancel} was called. + *
  • {@link Failure} terminal state, {@code setException} was called. + *
  • {@link SetFuture} intermediate state, {@code setFuture} was called. + *
  • {@link #NULL} terminal state, {@code set(null)} was called. + *
  • Any other non-null value, terminal state, {@code set} was called with a non-null + * argument. + *
+ */ + private volatile Object value; + + /** All listeners. */ + private volatile Listener listeners; + + /** All waiting threads. */ + private volatile Waiter waiters; + + /** Constructor for use by subclasses. */ + protected AbstractFuture() {} + + // Gets and Timed Gets + // + // * Be responsive to interruption + // * Don't create Waiter nodes if you aren't going to park, this helps reduce contention on the + // waiters field. + // * Future completion is defined by when #value becomes non-null/non SetFuture + // * Future completion can be observed if the waiters field contains a TOMBSTONE + + // Timed Get + // There are a few design constraints to consider + // * We want to be responsive to small timeouts, unpark() has non trivial latency overheads (I + // have observed 12 micros on 64-bit linux systems to wake up a parked thread). So if the + // timeout is small we shouldn't park(). This needs to be traded off with the cpu overhead of + // spinning, so we use SPIN_THRESHOLD_NANOS which is what AbstractQueuedSynchronizer uses for + // similar purposes. + // * We want to behave reasonably for timeouts of 0 + // * We are more responsive to completion than timeouts. This is because parkNanos depends on + // system scheduling and as such we could either miss our deadline, or unpark() could be delayed + // so that it looks like we timed out even though we didn't. For comparison FutureTask respects + // completion preferably and AQS is non-deterministic (depends on where in the queue the waiter + // is). If we wanted to be strict about it, we could store the unpark() time in the Waiter node + // and we could use that to make a decision about whether or not we timed out prior to being + // unparked. + + /** + * {@inheritDoc} + * + *

The default {@link AbstractFuture} implementation throws {@code InterruptedException} if the + * current thread is interrupted during the call, even if the value is already available. + * + * @throws CancellationException {@inheritDoc} + */ + @Override + @ParametricNullness + public V get(long timeout, TimeUnit unit) + throws InterruptedException, TimeoutException, ExecutionException { + // NOTE: if timeout < 0, remainingNanos will be < 0 and we will fall into the while(true) loop + // at the bottom and throw a timeoutexception. + final long timeoutNanos = unit.toNanos(timeout); // we rely on the implicit null check on unit. + long remainingNanos = timeoutNanos; + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Object localValue = value; + if (localValue != null & !(localValue instanceof SetFuture)) { + return getDoneValue(localValue); + } + // we delay calling nanoTime until we know we will need to either park or spin + final long endNanos = remainingNanos > 0 ? System.nanoTime() + remainingNanos : 0; + long_wait_loop: + if (remainingNanos >= SPIN_THRESHOLD_NANOS) { + Waiter oldHead = waiters; + if (oldHead != Waiter.TOMBSTONE) { + Waiter node = new Waiter(); + do { + node.setNext(oldHead); + if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) { + while (true) { + OverflowAvoidingLockSupport.parkNanos(this, remainingNanos); + // Check interruption first, if we woke up due to interruption we need to honor that. + if (Thread.interrupted()) { + removeWaiter(node); + throw new InterruptedException(); + } + + // Otherwise re-read and check doneness. If we loop then it must have been a spurious + // wakeup + localValue = value; + if (localValue != null & !(localValue instanceof SetFuture)) { + return getDoneValue(localValue); + } + + // timed out? + remainingNanos = endNanos - System.nanoTime(); + if (remainingNanos < SPIN_THRESHOLD_NANOS) { + // Remove the waiter, one way or another we are done parking this thread. + removeWaiter(node); + break long_wait_loop; // jump down to the busy wait loop + } + } + } + oldHead = waiters; // re-read and loop. + } while (oldHead != Waiter.TOMBSTONE); + } + // re-read value, if we get here then we must have observed a TOMBSTONE while trying to add a + // waiter. + // requireNonNull is safe because value is always set before TOMBSTONE. + return getDoneValue(requireNonNull(value)); + } + // If we get here then we have remainingNanos < SPIN_THRESHOLD_NANOS and there is no node on the + // waiters list + while (remainingNanos > 0) { + localValue = value; + if (localValue != null & !(localValue instanceof SetFuture)) { + return getDoneValue(localValue); + } + if (Thread.interrupted()) { + throw new InterruptedException(); + } + remainingNanos = endNanos - System.nanoTime(); + } + + String futureToString = toString(); + final String unitString = unit.toString().toLowerCase(Locale.ROOT); + String message = "Waited " + timeout + " " + unit.toString().toLowerCase(Locale.ROOT); + // Only report scheduling delay if larger than our spin threshold - otherwise it's just noise + if (remainingNanos + SPIN_THRESHOLD_NANOS < 0) { + // We over-waited for our timeout. + message += " (plus "; + long overWaitNanos = -remainingNanos; + long overWaitUnits = unit.convert(overWaitNanos, TimeUnit.NANOSECONDS); + long overWaitLeftoverNanos = overWaitNanos - unit.toNanos(overWaitUnits); + boolean shouldShowExtraNanos = + overWaitUnits == 0 || overWaitLeftoverNanos > SPIN_THRESHOLD_NANOS; + if (overWaitUnits > 0) { + message += overWaitUnits + " " + unitString; + if (shouldShowExtraNanos) { + message += ","; + } + message += " "; + } + if (shouldShowExtraNanos) { + message += overWaitLeftoverNanos + " nanoseconds "; + } + + message += "delay)"; + } + // It's confusing to see a completed future in a timeout message; if isDone() returns false, + // then we know it must have given a pending toString value earlier. If not, then the future + // completed after the timeout expired, and the message might be success. + if (isDone()) { + throw new TimeoutException(message + " but future completed as timeout expired"); + } + throw new TimeoutException(message + " for " + futureToString); + } + + /** + * {@inheritDoc} + * + *

The default {@link AbstractFuture} implementation throws {@code InterruptedException} if the + * current thread is interrupted during the call, even if the value is already available. + * + * @throws CancellationException {@inheritDoc} + */ + @Override + @ParametricNullness + public V get() throws InterruptedException, ExecutionException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Object localValue = value; + if (localValue != null & !(localValue instanceof SetFuture)) { + return getDoneValue(localValue); + } + Waiter oldHead = waiters; + if (oldHead != Waiter.TOMBSTONE) { + Waiter node = new Waiter(); + do { + node.setNext(oldHead); + if (ATOMIC_HELPER.casWaiters(this, oldHead, node)) { + // we are on the stack, now wait for completion. + while (true) { + LockSupport.park(this); + // Check interruption first, if we woke up due to interruption we need to honor that. + if (Thread.interrupted()) { + removeWaiter(node); + throw new InterruptedException(); + } + // Otherwise re-read and check doneness. If we loop then it must have been a spurious + // wakeup + localValue = value; + if (localValue != null & !(localValue instanceof SetFuture)) { + return getDoneValue(localValue); + } + } + } + oldHead = waiters; // re-read and loop. + } while (oldHead != Waiter.TOMBSTONE); + } + // re-read value, if we get here then we must have observed a TOMBSTONE while trying to add a + // waiter. + // requireNonNull is safe because value is always set before TOMBSTONE. + return getDoneValue(requireNonNull(value)); + } + + /** Unboxes {@code obj}. Assumes that obj is not {@code null} or a {@link SetFuture}. */ + @ParametricNullness + private V getDoneValue(Object obj) throws ExecutionException { + // While this seems like it might be too branch-y, simple benchmarking proves it to be + // unmeasurable (comparing done AbstractFutures with immediateFuture) + if (obj instanceof Cancellation) { + throw cancellationExceptionWithCause("Task was cancelled.", ((Cancellation) obj).cause); + } else if (obj instanceof Failure) { + throw new ExecutionException(((Failure) obj).exception); + } else if (obj == NULL) { + /* + * It's safe to return null because we would only have stored it in the first place if it were + * a valid value for V. + */ + return uncheckedNull(); + } else { + @SuppressWarnings("unchecked") // this is the only other option + V asV = (V) obj; + return asV; + } + } + + @Override + public boolean isDone() { + final Object localValue = value; + return localValue != null & !(localValue instanceof SetFuture); + } + + @Override + public boolean isCancelled() { + final Object localValue = value; + return localValue instanceof Cancellation; + } + + /** + * {@inheritDoc} + * + *

If a cancellation attempt succeeds on a {@code Future} that had previously been {@linkplain + * #setFuture set asynchronously}, then the cancellation will also be propagated to the delegate + * {@code Future} that was supplied in the {@code setFuture} call. + * + *

Rather than override this method to perform additional cancellation work or cleanup, + * subclasses should override {@link #afterDone}, consulting {@link #isCancelled} and {@link + * #wasInterrupted} as necessary. This ensures that the work is done even if the future is + * cancelled without a call to {@code cancel}, such as by calling {@code + * setFuture(cancelledFuture)}. + * + *

Beware of completing a future while holding a lock. Its listeners may do slow work or + * acquire other locks, risking deadlocks. + */ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + Object localValue = value; + boolean rValue = false; + if (localValue == null | localValue instanceof SetFuture) { + // Try to delay allocating the exception. At this point we may still lose the CAS, but it is + // certainly less likely. + Object valueToSet = + GENERATE_CANCELLATION_CAUSES + ? new Cancellation( + mayInterruptIfRunning, new CancellationException("Future.cancel() was called.")) + /* + * requireNonNull is safe because we've initialized these if + * !GENERATE_CANCELLATION_CAUSES. + * + * TODO(cpovirk): Maybe it would be cleaner to define a CancellationSupplier interface + * with two implementations, one that contains causeless Cancellation instances and + * the other of which creates new Cancellation instances each time it's called? Yet + * another alternative is to fill in a non-null value for each of the fields no matter + * what and to just not use it if !GENERATE_CANCELLATION_CAUSES. + */ + : requireNonNull( + mayInterruptIfRunning + ? Cancellation.CAUSELESS_INTERRUPTED + : Cancellation.CAUSELESS_CANCELLED); + AbstractFuture abstractFuture = this; + while (true) { + if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) { + rValue = true; + complete(abstractFuture, mayInterruptIfRunning); + if (localValue instanceof SetFuture) { + // propagate cancellation to the future set in setfuture, this is racy, and we don't + // care if we are successful or not. + ListenableFuture futureToPropagateTo = ((SetFuture) localValue).future; + if (futureToPropagateTo instanceof Trusted) { + // If the future is a TrustedFuture then we specifically avoid calling cancel() + // this has 2 benefits + // 1. for long chains of futures strung together with setFuture we consume less stack + // 2. we avoid allocating Cancellation objects at every level of the cancellation + // chain + // We can only do this for TrustedFuture, because TrustedFuture.cancel is final and + // does nothing but delegate to this method. + AbstractFuture trusted = (AbstractFuture) futureToPropagateTo; + localValue = trusted.value; + if (localValue == null | localValue instanceof SetFuture) { + abstractFuture = trusted; + continue; // loop back up and try to complete the new future + } + } else { + // not a TrustedFuture, call cancel directly. + futureToPropagateTo.cancel(mayInterruptIfRunning); + } + } + break; + } + // obj changed, reread + localValue = abstractFuture.value; + if (!(localValue instanceof SetFuture)) { + // obj cannot be null at this point, because value can only change from null to non-null. + // So if value changed (and it did since we lost the CAS), then it cannot be null and + // since it isn't a SetFuture, then the future must be done and we should exit the loop + break; + } + } + } + return rValue; + } + + /** + * Subclasses can override this method to implement interruption of the future's computation. The + * method is invoked automatically by a successful call to {@link #cancel(boolean) cancel(true)}. + * + *

The default implementation does nothing. + * + *

This method is likely to be deprecated. Prefer to override {@link #afterDone}, checking + * {@link #wasInterrupted} to decide whether to interrupt your task. + * + * @since 10.0 + */ + protected void interruptTask() {} + + /** + * Returns true if this future was cancelled with {@code mayInterruptIfRunning} set to {@code + * true}. + * + * @since 14.0 + */ + protected final boolean wasInterrupted() { + final Object localValue = value; + return (localValue instanceof Cancellation) && ((Cancellation) localValue).wasInterrupted; + } + + /** + * {@inheritDoc} + * + * @since 10.0 + */ + @Override + public void addListener(Runnable listener, Executor executor) { + checkNotNull(listener, "Runnable was null."); + checkNotNull(executor, "Executor was null."); + // Checking isDone and listeners != TOMBSTONE may seem redundant, but our contract for + // addListener says that listeners execute 'immediate' if the future isDone(). However, our + // protocol for completing a future is to assign the value field (which sets isDone to true) and + // then to release waiters, followed by executing afterDone(), followed by releasing listeners. + // That means that it is possible to observe that the future isDone and that your listeners + // don't execute 'immediately'. By checking isDone here we avoid that. + // A corollary to all that is that we don't need to check isDone inside the loop because if we + // get into the loop we know that we weren't done when we entered and therefore we aren't under + // an obligation to execute 'immediately'. + if (!isDone()) { + Listener oldHead = listeners; + if (oldHead != Listener.TOMBSTONE) { + Listener newNode = new Listener(listener, executor); + do { + newNode.next = oldHead; + if (ATOMIC_HELPER.casListeners(this, oldHead, newNode)) { + return; + } + oldHead = listeners; // re-read + } while (oldHead != Listener.TOMBSTONE); + } + } + // If we get here then the Listener TOMBSTONE was set, which means the future is done, call + // the listener. + executeListener(listener, executor); + } + + /** + * Sets the result of this {@code Future} unless this {@code Future} has already been cancelled or + * set (including {@linkplain #setFuture set asynchronously}). When a call to this method returns, + * the {@code Future} is guaranteed to be {@linkplain #isDone done} only if the call was + * accepted (in which case it returns {@code true}). If it returns {@code false}, the {@code + * Future} may have previously been set asynchronously, in which case its result may not be known + * yet. That result, though not yet known, cannot be overridden by a call to a {@code set*} + * method, only by a call to {@link #cancel}. + * + *

Beware of completing a future while holding a lock. Its listeners may do slow work or + * acquire other locks, risking deadlocks. + * + * @param value the value to be used as the result + * @return true if the attempt was accepted, completing the {@code Future} + */ + protected boolean set(@ParametricNullness V value) { + Object valueToSet = value == null ? NULL : value; + if (ATOMIC_HELPER.casValue(this, null, valueToSet)) { + complete(this, /* callInterruptTask= */ false); + return true; + } + return false; + } + + /** + * Sets the failed result of this {@code Future} unless this {@code Future} has already been + * cancelled or set (including {@linkplain #setFuture set asynchronously}). When a call to this + * method returns, the {@code Future} is guaranteed to be {@linkplain #isDone done} only if + * the call was accepted (in which case it returns {@code true}). If it returns {@code false}, the + * {@code Future} may have previously been set asynchronously, in which case its result may not be + * known yet. That result, though not yet known, cannot be overridden by a call to a {@code set*} + * method, only by a call to {@link #cancel}. + * + *

Beware of completing a future while holding a lock. Its listeners may do slow work or + * acquire other locks, risking deadlocks. + * + * @param throwable the exception to be used as the failed result + * @return true if the attempt was accepted, completing the {@code Future} + */ + protected boolean setException(Throwable throwable) { + Object valueToSet = new Failure(checkNotNull(throwable)); + if (ATOMIC_HELPER.casValue(this, null, valueToSet)) { + complete(this, /* callInterruptTask= */ false); + return true; + } + return false; + } + + /** + * Sets the result of this {@code Future} to match the supplied input {@code Future} once the + * supplied {@code Future} is done, unless this {@code Future} has already been cancelled or set + * (including "set asynchronously," defined below). + * + *

If the supplied future is {@linkplain #isDone done} when this method is called and the call + * is accepted, then this future is guaranteed to have been completed with the supplied future by + * the time this method returns. If the supplied future is not done and the call is accepted, then + * the future will be set asynchronously. Note that such a result, though not yet known, + * cannot be overridden by a call to a {@code set*} method, only by a call to {@link #cancel}. + * + *

If the call {@code setFuture(delegate)} is accepted and this {@code Future} is later + * cancelled, cancellation will be propagated to {@code delegate}. Additionally, any call to + * {@code setFuture} after any cancellation will propagate cancellation to the supplied {@code + * Future}. + * + *

Note that, even if the supplied future is cancelled and it causes this future to complete, + * it will never trigger interruption behavior. In particular, it will not cause this future to + * invoke the {@link #interruptTask} method, and the {@link #wasInterrupted} method will not + * return {@code true}. + * + *

Beware of completing a future while holding a lock. Its listeners may do slow work or + * acquire other locks, risking deadlocks. + * + * @param future the future to delegate to + * @return true if the attempt was accepted, indicating that the {@code Future} was not previously + * cancelled or set. + * @since 19.0 + */ + protected boolean setFuture(ListenableFuture future) { + checkNotNull(future); + Object localValue = value; + if (localValue == null) { + if (future.isDone()) { + Object value = getFutureValue(future); + if (ATOMIC_HELPER.casValue(this, null, value)) { + complete( + this, + /* + * Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so + * don't invoke interruptTask. + */ + false); + return true; + } + return false; + } + SetFuture valueToSet = new SetFuture(this, future); + if (ATOMIC_HELPER.casValue(this, null, valueToSet)) { + // the listener is responsible for calling completeWithFuture, directExecutor is appropriate + // since all we are doing is unpacking a completed future which should be fast. + try { + future.addListener(valueToSet, DirectExecutor.INSTANCE); + } catch (RuntimeException | Error t) { + // addListener has thrown an exception! SetFuture.run can't throw any exceptions so this + // must have been caused by addListener itself. The most likely explanation is a + // misconfigured mock. Try to switch to Failure. + Failure failure; + try { + failure = new Failure(t); + } catch (RuntimeException | Error oomMostLikely) { + failure = Failure.FALLBACK_INSTANCE; + } + // Note: The only way this CAS could fail is if cancel() has raced with us. That is ok. + boolean unused = ATOMIC_HELPER.casValue(this, valueToSet, failure); + } + return true; + } + localValue = value; // we lost the cas, fall through and maybe cancel + } + // The future has already been set to something. If it is cancellation we should cancel the + // incoming future. + if (localValue instanceof Cancellation) { + // we don't care if it fails, this is best-effort. + future.cancel(((Cancellation) localValue).wasInterrupted); + } + return false; + } + + /** + * Returns a value that satisfies the contract of the {@link #value} field based on the state of + * given future. + * + *

This is approximately the inverse of {@link #getDoneValue(Object)} + */ + private static Object getFutureValue(ListenableFuture future) { + if (future instanceof Trusted) { + // Break encapsulation for TrustedFuture instances since we know that subclasses cannot + // override .get() (since it is final) and therefore this is equivalent to calling .get() + // and unpacking the exceptions like we do below (just much faster because it is a single + // field read instead of a read, several branches and possibly creating exceptions). + Object v = ((AbstractFuture) future).value; + if (v instanceof Cancellation) { + // If the other future was interrupted, clear the interrupted bit while preserving the cause + // this will make it consistent with how non-trustedfutures work which cannot propagate the + // wasInterrupted bit + Cancellation c = (Cancellation) v; + if (c.wasInterrupted) { + v = + c.cause != null + ? new Cancellation(/* wasInterrupted= */ false, c.cause) + : Cancellation.CAUSELESS_CANCELLED; + } + } + // requireNonNull is safe as long as we call this method only on completed futures. + return requireNonNull(v); + } + if (future instanceof InternalFutureFailureAccess) { + Throwable throwable = + InternalFutures.tryInternalFastPathGetFailure((InternalFutureFailureAccess) future); + if (throwable != null) { + return new Failure(throwable); + } + } + boolean wasCancelled = future.isCancelled(); + // Don't allocate a CancellationException if it's not necessary + if (!GENERATE_CANCELLATION_CAUSES & wasCancelled) { + /* + * requireNonNull is safe because we've initialized CAUSELESS_CANCELLED if + * !GENERATE_CANCELLATION_CAUSES. + */ + return requireNonNull(Cancellation.CAUSELESS_CANCELLED); + } + // Otherwise calculate the value by calling .get() + try { + Object v = getUninterruptibly(future); + if (wasCancelled) { + return new Cancellation( + false, + new IllegalArgumentException( + "get() did not throw CancellationException, despite reporting " + + "isCancelled() == true: " + + future)); + } + return v == null ? NULL : v; + } catch (ExecutionException exception) { + if (wasCancelled) { + return new Cancellation( + false, + new IllegalArgumentException( + "get() did not throw CancellationException, despite reporting " + + "isCancelled() == true: " + + future, + exception)); + } + return new Failure(exception.getCause()); + } catch (CancellationException cancellation) { + if (!wasCancelled) { + return new Failure( + new IllegalArgumentException( + "get() threw CancellationException, despite reporting isCancelled() == false: " + + future, + cancellation)); + } + return new Cancellation(false, cancellation); + } catch (RuntimeException | Error t) { + return new Failure(t); + } + } + + /** + * An inlined private copy of {@link Uninterruptibles#getUninterruptibly} used to break an + * internal dependency on other /util/concurrent classes. + */ + @ParametricNullness + private static V getUninterruptibly(Future future) + throws ExecutionException { + boolean interrupted = false; + try { + while (true) { + try { + return future.get(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** Unblocks all threads and runs all listeners. */ + private static void complete(AbstractFuture param, boolean callInterruptTask) { + // Declare a "true" local variable so that the Checker Framework will infer nullness. + AbstractFuture future = param; + + Listener next = null; + outer: + while (true) { + future.releaseWaiters(); + /* + * We call interruptTask() immediately before afterDone() so that migrating between the two + * can be a no-op. + */ + if (callInterruptTask) { + future.interruptTask(); + /* + * Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so don't + * invoke interruptTask on any subsequent futures. + */ + callInterruptTask = false; + } + // We call this before the listeners in order to avoid needing to manage a separate stack data + // structure for them. Also, some implementations rely on this running prior to listeners + // so that the cleanup work is visible to listeners. + // afterDone() should be generally fast and only used for cleanup work... but in theory can + // also be recursive and create StackOverflowErrors + future.afterDone(); + // push the current set of listeners onto next + next = future.clearListeners(next); + future = null; + while (next != null) { + Listener curr = next; + next = next.next; + /* + * requireNonNull is safe because the listener stack never contains TOMBSTONE until after + * clearListeners. + */ + Runnable task = requireNonNull(curr.task); + if (task instanceof SetFuture) { + SetFuture setFuture = (SetFuture) task; + // We unwind setFuture specifically to avoid StackOverflowErrors in the case of long + // chains of SetFutures + // Handling this special case is important because there is no way to pass an executor to + // setFuture, so a user couldn't break the chain by doing this themselves. It is also + // potentially common if someone writes a recursive Futures.transformAsync transformer. + future = setFuture.owner; + if (future.value == setFuture) { + Object valueToSet = getFutureValue(setFuture.future); + if (ATOMIC_HELPER.casValue(future, setFuture, valueToSet)) { + continue outer; + } + } + // otherwise the future we were trying to set is already done. + } else { + /* + * requireNonNull is safe because the listener stack never contains TOMBSTONE until after + * clearListeners. + */ + executeListener(task, requireNonNull(curr.executor)); + } + } + break; + } + } + + /** + * Callback method that is called exactly once after the future is completed. + * + *

If {@link #interruptTask} is also run during completion, {@link #afterDone} runs after it. + * + *

The default implementation of this method in {@code AbstractFuture} does nothing. This is + * intended for very lightweight cleanup work, for example, timing statistics or clearing fields. + * If your task does anything heavier consider, just using a listener with an executor. + * + * @since 20.0 + */ + protected void afterDone() {} + + // TODO(b/114236866): Inherit doc from InternalFutureFailureAccess. Also, -link to its URL. + /** + * Usually returns {@code null} but, if this {@code Future} has failed, may optionally + * return the cause of the failure. "Failure" means specifically "completed with an exception"; it + * does not include "was cancelled." To be explicit: If this method returns a non-null value, + * then: + * + *

    + *
  • {@code isDone()} must return {@code true} + *
  • {@code isCancelled()} must return {@code false} + *
  • {@code get()} must not block, and it must throw an {@code ExecutionException} with the + * return value of this method as its cause + *
+ * + *

This method is {@code protected} so that classes like {@code + * org.apache.iotdb.google.common.util.concurrent.SettableFuture} do not expose it to their users + * as an instance method. In the unlikely event that you need to call this method, call {@link + * InternalFutures#tryInternalFastPathGetFailure(InternalFutureFailureAccess)}. + * + * @since 27.0 + */ + @Override + /* + * We should annotate the superclass, InternalFutureFailureAccess, to say that its copy of this + * method returns @Nullable, too. However, we're not sure if we want to make any changes to that + * class, since it's in a separate artifact that we planned to release only a single version of. + */ + protected final Throwable tryInternalFastPathGetFailure() { + if (this instanceof Trusted) { + Object obj = value; + if (obj instanceof Failure) { + return ((Failure) obj).exception; + } + } + return null; + } + + /** + * If this future has been cancelled (and possibly interrupted), cancels (and possibly interrupts) + * the given future (if available). + */ + final void maybePropagateCancellationTo(Future related) { + if (related != null & isCancelled()) { + related.cancel(wasInterrupted()); + } + } + + /** Releases all threads in the {@link #waiters} list, and clears the list. */ + private void releaseWaiters() { + Waiter head = ATOMIC_HELPER.gasWaiters(this, Waiter.TOMBSTONE); + for (Waiter currentWaiter = head; currentWaiter != null; currentWaiter = currentWaiter.next) { + currentWaiter.unpark(); + } + } + + /** + * Clears the {@link #listeners} list and prepends its contents to {@code onto}, least recently + * added first. + */ + private Listener clearListeners(Listener onto) { + // We need to + // 1. atomically swap the listeners with TOMBSTONE, this is because addListener uses that + // to synchronize with us + // 2. reverse the linked list, because despite our rather clear contract, people depend on us + // executing listeners in the order they were added + // 3. push all the items onto 'onto' and return the new head of the stack + Listener head = ATOMIC_HELPER.gasListeners(this, Listener.TOMBSTONE); + Listener reversedList = onto; + while (head != null) { + Listener tmp = head; + head = head.next; + tmp.next = reversedList; + reversedList = tmp; + } + return reversedList; + } + + // TODO(user): move parts into a default method on ListenableFuture? + @Override + public String toString() { + // TODO(cpovirk): Presize to something plausible? + StringBuilder builder = new StringBuilder(); + if (getClass().getName().startsWith("org.apache.iotdb.google.common.util.concurrent.")) { + builder.append(getClass().getSimpleName()); + } else { + builder.append(getClass().getName()); + } + builder.append('@').append(toHexString(identityHashCode(this))).append("[status="); + if (isCancelled()) { + builder.append("CANCELLED"); + } else if (isDone()) { + addDoneString(builder); + } else { + addPendingString(builder); // delegates to addDoneString if future completes midway + } + return builder.append("]").toString(); + } + + /** + * Provide a human-readable explanation of why this future has not yet completed. + * + * @return null if an explanation cannot be provided (e.g. because the future is done). + * @since 23.0 + */ + protected String pendingToString() { + // TODO(diamondm) consider moving this into addPendingString so it's always in the output + if (this instanceof ScheduledFuture) { + return "remaining delay=[" + + ((ScheduledFuture) this).getDelay(TimeUnit.MILLISECONDS) + + " ms]"; + } + return null; + } + + private void addPendingString(StringBuilder builder) { + // Capture current builder length so it can be truncated if this future ends up completing while + // the toString is being calculated + int truncateLength = builder.length(); + + builder.append("PENDING"); + + Object localValue = value; + if (localValue instanceof SetFuture) { + builder.append(", setFuture=["); + appendUserObject(builder, ((SetFuture) localValue).future); + builder.append("]"); + } else { + String pendingDescription; + try { + pendingDescription = Strings.emptyToNull(pendingToString()); + } catch (RuntimeException | StackOverflowError e) { + // Don't call getMessage or toString() on the exception, in case the exception thrown by the + // subclass is implemented with bugs similar to the subclass. + pendingDescription = "Exception thrown from implementation: " + e.getClass(); + } + if (pendingDescription != null) { + builder.append(", info=[").append(pendingDescription).append("]"); + } + } + + // The future may complete while calculating the toString, so we check once more to see if the + // future is done + if (isDone()) { + // Truncate anything that was appended before realizing this future is done + builder.delete(truncateLength, builder.length()); + addDoneString(builder); + } + } + + private void addDoneString(StringBuilder builder) { + try { + V value = getUninterruptibly(this); + builder.append("SUCCESS, result=["); + appendResultObject(builder, value); + builder.append("]"); + } catch (ExecutionException e) { + builder.append("FAILURE, cause=[").append(e.getCause()).append("]"); + } catch (CancellationException e) { + builder.append("CANCELLED"); // shouldn't be reachable + } catch (RuntimeException e) { + builder.append("UNKNOWN, cause=[").append(e.getClass()).append(" thrown from get()]"); + } + } + + /** + * Any object can be the result of a Future, and not every object has a reasonable toString() + * implementation. Using a reconstruction of the default Object.toString() prevents OOMs and stack + * overflows, and helps avoid sensitive data inadvertently ending up in exception messages. + */ + private void appendResultObject(StringBuilder builder, Object o) { + if (o == null) { + builder.append("null"); + } else if (o == this) { + builder.append("this future"); + } else { + builder + .append(o.getClass().getName()) + .append("@") + .append(Integer.toHexString(System.identityHashCode(o))); + } + } + + /** Helper for printing user supplied objects into our toString method. */ + private void appendUserObject(StringBuilder builder, Object o) { + // This is some basic recursion detection for when people create cycles via set/setFuture or + // when deep chains of futures exist resulting in a StackOverflowException. We could detect + // arbitrary cycles using a thread local but this should be a good enough solution (it is also + // what jdk collections do in these cases) + try { + if (o == this) { + builder.append("this future"); + } else { + builder.append(o); + } + } catch (RuntimeException | StackOverflowError e) { + // Don't call getMessage or toString() on the exception, in case the exception thrown by the + // user object is implemented with bugs similar to the user object. + builder.append("Exception thrown from implementation: ").append(e.getClass()); + } + } + + /** + * Submits the given runnable to the given {@link Executor} catching and logging all {@linkplain + * RuntimeException runtime exceptions} thrown by the executor. + */ + private static void executeListener(Runnable runnable, Executor executor) { + try { + executor.execute(runnable); + } catch (RuntimeException e) { + // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if + // we're given a bad one. We only catch RuntimeException because we want Errors to propagate + // up. + log.log( + Level.SEVERE, + "RuntimeException while executing runnable " + runnable + " with executor " + executor, + e); + } + } + + private abstract static class AtomicHelper { + /** Non-volatile write of the thread to the {@link Waiter#thread} field. */ + abstract void putThread(Waiter waiter, Thread newValue); + + /** Non-volatile write of the waiter to the {@link Waiter#next} field. */ + abstract void putNext(Waiter waiter, Waiter newValue); + + /** Performs a CAS operation on the {@link #waiters} field. */ + abstract boolean casWaiters(AbstractFuture future, Waiter expect, Waiter update); + + /** Performs a CAS operation on the {@link #listeners} field. */ + abstract boolean casListeners(AbstractFuture future, Listener expect, Listener update); + + /** Performs a GAS operation on the {@link #waiters} field. */ + abstract Waiter gasWaiters(AbstractFuture future, Waiter update); + + /** Performs a GAS operation on the {@link #listeners} field. */ + abstract Listener gasListeners(AbstractFuture future, Listener update); + + /** Performs a CAS operation on the {@link #value} field. */ + abstract boolean casValue(AbstractFuture future, Object expect, Object update); + } + + /** + * {@link AtomicHelper} based on {@link sun.misc.Unsafe}. + * + *

Static initialization of this class will fail if the {@link sun.misc.Unsafe} object cannot + * be accessed. + */ + @SuppressWarnings("sunapi") + private static final class UnsafeAtomicHelper extends AtomicHelper { + static final sun.misc.Unsafe UNSAFE; + static final long LISTENERS_OFFSET; + static final long WAITERS_OFFSET; + static final long VALUE_OFFSET; + static final long WAITER_THREAD_OFFSET; + static final long WAITER_NEXT_OFFSET; + + static { + sun.misc.Unsafe unsafe = null; + try { + unsafe = sun.misc.Unsafe.getUnsafe(); + } catch (SecurityException tryReflectionInstead) { + try { + unsafe = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", e.getCause()); + } + } + try { + Class abstractFuture = AbstractFuture.class; + WAITERS_OFFSET = unsafe.objectFieldOffset(abstractFuture.getDeclaredField("waiters")); + LISTENERS_OFFSET = unsafe.objectFieldOffset(abstractFuture.getDeclaredField("listeners")); + VALUE_OFFSET = unsafe.objectFieldOffset(abstractFuture.getDeclaredField("value")); + WAITER_THREAD_OFFSET = unsafe.objectFieldOffset(Waiter.class.getDeclaredField("thread")); + WAITER_NEXT_OFFSET = unsafe.objectFieldOffset(Waiter.class.getDeclaredField("next")); + UNSAFE = unsafe; + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } catch (RuntimeException e) { + throw e; + } + } + + @Override + void putThread(Waiter waiter, Thread newValue) { + UNSAFE.putObject(waiter, WAITER_THREAD_OFFSET, newValue); + } + + @Override + void putNext(Waiter waiter, Waiter newValue) { + UNSAFE.putObject(waiter, WAITER_NEXT_OFFSET, newValue); + } + + /** Performs a CAS operation on the {@link #waiters} field. */ + @Override + boolean casWaiters(AbstractFuture future, Waiter expect, Waiter update) { + return UNSAFE.compareAndSwapObject(future, WAITERS_OFFSET, expect, update); + } + + /** Performs a CAS operation on the {@link #listeners} field. */ + @Override + boolean casListeners(AbstractFuture future, Listener expect, Listener update) { + return UNSAFE.compareAndSwapObject(future, LISTENERS_OFFSET, expect, update); + } + + /** Performs a GAS operation on the {@link #listeners} field. */ + @Override + Listener gasListeners(AbstractFuture future, Listener update) { + return (Listener) UNSAFE.getAndSetObject(future, LISTENERS_OFFSET, update); + } + + /** Performs a GAS operation on the {@link #waiters} field. */ + @Override + Waiter gasWaiters(AbstractFuture future, Waiter update) { + return (Waiter) UNSAFE.getAndSetObject(future, WAITERS_OFFSET, update); + } + + /** Performs a CAS operation on the {@link #value} field. */ + @Override + boolean casValue(AbstractFuture future, Object expect, Object update) { + return UNSAFE.compareAndSwapObject(future, VALUE_OFFSET, expect, update); + } + } + + /** {@link AtomicHelper} based on {@link AtomicReferenceFieldUpdater}. */ + @SuppressWarnings("rawtypes") + private static final class SafeAtomicHelper extends AtomicHelper { + final AtomicReferenceFieldUpdater waiterThreadUpdater; + final AtomicReferenceFieldUpdater waiterNextUpdater; + final AtomicReferenceFieldUpdater waitersUpdater; + final AtomicReferenceFieldUpdater listenersUpdater; + final AtomicReferenceFieldUpdater valueUpdater; + + SafeAtomicHelper( + AtomicReferenceFieldUpdater waiterThreadUpdater, + AtomicReferenceFieldUpdater waiterNextUpdater, + AtomicReferenceFieldUpdater waitersUpdater, + AtomicReferenceFieldUpdater listenersUpdater, + AtomicReferenceFieldUpdater valueUpdater) { + this.waiterThreadUpdater = waiterThreadUpdater; + this.waiterNextUpdater = waiterNextUpdater; + this.waitersUpdater = waitersUpdater; + this.listenersUpdater = listenersUpdater; + this.valueUpdater = valueUpdater; + } + + @Override + void putThread(Waiter waiter, Thread newValue) { + waiterThreadUpdater.lazySet(waiter, newValue); + } + + @Override + void putNext(Waiter waiter, Waiter newValue) { + waiterNextUpdater.lazySet(waiter, newValue); + } + + @Override + boolean casWaiters(AbstractFuture future, Waiter expect, Waiter update) { + return waitersUpdater.compareAndSet(future, expect, update); + } + + @Override + boolean casListeners(AbstractFuture future, Listener expect, Listener update) { + return listenersUpdater.compareAndSet(future, expect, update); + } + + /** Performs a GAS operation on the {@link #listeners} field. */ + @Override + Listener gasListeners(AbstractFuture future, Listener update) { + return listenersUpdater.getAndSet(future, update); + } + + /** Performs a GAS operation on the {@link #waiters} field. */ + @Override + Waiter gasWaiters(AbstractFuture future, Waiter update) { + return waitersUpdater.getAndSet(future, update); + } + + @Override + boolean casValue(AbstractFuture future, Object expect, Object update) { + return valueUpdater.compareAndSet(future, expect, update); + } + } + + /** + * {@link AtomicHelper} based on {@code synchronized} and volatile writes. + * + *

This is an implementation of last resort for when certain basic VM features are broken (like + * AtomicReferenceFieldUpdater). + */ + private static final class SynchronizedHelper extends AtomicHelper { + @Override + void putThread(Waiter waiter, Thread newValue) { + waiter.thread = newValue; + } + + @Override + void putNext(Waiter waiter, Waiter newValue) { + waiter.next = newValue; + } + + @Override + boolean casWaiters(AbstractFuture future, Waiter expect, Waiter update) { + synchronized (future) { + if (future.waiters == expect) { + future.waiters = update; + return true; + } + return false; + } + } + + @Override + boolean casListeners(AbstractFuture future, Listener expect, Listener update) { + synchronized (future) { + if (future.listeners == expect) { + future.listeners = update; + return true; + } + return false; + } + } + + /** Performs a GAS operation on the {@link #listeners} field. */ + @Override + Listener gasListeners(AbstractFuture future, Listener update) { + synchronized (future) { + Listener old = future.listeners; + if (old != update) { + future.listeners = update; + } + return old; + } + } + + /** Performs a GAS operation on the {@link #waiters} field. */ + @Override + Waiter gasWaiters(AbstractFuture future, Waiter update) { + synchronized (future) { + Waiter old = future.waiters; + if (old != update) { + future.waiters = update; + } + return old; + } + } + + @Override + boolean casValue(AbstractFuture future, Object expect, Object update) { + synchronized (future) { + if (future.value == expect) { + future.value = update; + return true; + } + return false; + } + } + } + + private static CancellationException cancellationExceptionWithCause( + String message, Throwable cause) { + CancellationException exception = new CancellationException(message); + exception.initCause(cause); + return exception; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractIdleService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractIdleService.java new file mode 100644 index 0000000000000..c215c0ced2dfb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractIdleService.java @@ -0,0 +1,222 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.time.Duration; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** + * Base class for services that do not need a thread while "running" but may need one during startup + * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which + * run in an executor which by default uses a separate thread for each method. + * + * @author Chris Nokleberg + * @since 1.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractIdleService implements Service { + + /* Thread names will look like {@code "MyService STARTING"}. */ + private final Supplier threadNameSupplier = new ThreadNameSupplier(); + + private final class ThreadNameSupplier implements Supplier { + @Override + public String get() { + return serviceName() + " " + state(); + } + } + + /* use AbstractService for state management */ + private final Service delegate = new DelegateService(); + + private final class DelegateService extends AbstractService { + @Override + protected final void doStart() { + MoreExecutors.renamingDecorator(executor(), threadNameSupplier) + .execute( + () -> { + try { + startUp(); + notifyStarted(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); + } + }); + } + + @Override + protected final void doStop() { + MoreExecutors.renamingDecorator(executor(), threadNameSupplier) + .execute( + () -> { + try { + shutDown(); + notifyStopped(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); + } + }); + } + + @Override + public String toString() { + return AbstractIdleService.this.toString(); + } + } + + /** Constructor for use by subclasses. */ + protected AbstractIdleService() {} + + /** Start the service. */ + protected abstract void startUp() throws Exception; + + /** Stop the service. */ + protected abstract void shutDown() throws Exception; + + /** + * Returns the {@link Executor} that will be used to run this service. Subclasses may override + * this method to use a custom {@link Executor}, which may configure its worker thread with a + * specific name, thread group or priority. The returned executor's {@link + * Executor#execute(Runnable) execute()} method is called when this service is started and + * stopped, and should return promptly. + */ + protected Executor executor() { + return command -> MoreExecutors.newThread(threadNameSupplier.get(), command).start(); + } + + @Override + public String toString() { + return serviceName() + " [" + state() + "]"; + } + + @Override + public final boolean isRunning() { + return delegate.isRunning(); + } + + @Override + public final State state() { + return delegate.state(); + } + + /** + * @since 13.0 + */ + @Override + public final void addListener(Listener listener, Executor executor) { + delegate.addListener(listener, executor); + } + + /** + * @since 14.0 + */ + @Override + public final Throwable failureCause() { + return delegate.failureCause(); + } + + /** + * @since 15.0 + */ + @Override + public final Service startAsync() { + delegate.startAsync(); + return this; + } + + /** + * @since 15.0 + */ + @Override + public final Service stopAsync() { + delegate.stopAsync(); + return this; + } + + /** + * @since 15.0 + */ + @Override + public final void awaitRunning() { + delegate.awaitRunning(); + } + + /** + * @since 28.0 + */ + @Override + public final void awaitRunning(Duration timeout) throws TimeoutException { + Service.super.awaitRunning(timeout); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + delegate.awaitRunning(timeout, unit); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitTerminated() { + delegate.awaitTerminated(); + } + + /** + * @since 28.0 + */ + @Override + public final void awaitTerminated(Duration timeout) throws TimeoutException { + Service.super.awaitTerminated(timeout); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + delegate.awaitTerminated(timeout, unit); + } + + /** + * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging + * output. + * + * @since 14.0 + */ + protected String serviceName() { + return getClass().getSimpleName(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractListeningExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractListeningExecutorService.java new file mode 100644 index 0000000000000..c668acd0c06cc --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractListeningExecutorService.java @@ -0,0 +1,83 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.RunnableFuture; + +/** + * Abstract {@link ListeningExecutorService} implementation that creates {@link ListenableFuture} + * instances for each {@link Runnable} and {@link Callable} submitted to it. These tasks are run + * with the abstract {@link #execute execute(Runnable)} method. + * + *

In addition to {@link #execute}, subclasses must implement all methods related to shutdown and + * termination. + * + * @author Chris Povirk + * @since 14.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractListeningExecutorService extends AbstractExecutorService + implements ListeningExecutorService { + + /** + * @since 19.0 (present with return type {@code ListenableFutureTask} since 14.0) + */ + // TODO(kak): consider removing this + @Override + protected final RunnableFuture newTaskFor( + Runnable runnable, @ParametricNullness T value) { + return TrustedListenableFutureTask.create(runnable, value); + } + + /** + * @since 19.0 (present with return type {@code ListenableFutureTask} since 14.0) + */ + // TODO(kak): consider removing this + @Override + protected final RunnableFuture newTaskFor(Callable callable) { + return TrustedListenableFutureTask.create(callable); + } + + // TODO(kak): consider removing this + @Override + public ListenableFuture submit(Runnable task) { + return (ListenableFuture) super.submit(task); + } + + // TODO(kak): consider removing this + @Override + public ListenableFuture submit( + Runnable task, @ParametricNullness T result) { + return (ListenableFuture) super.submit(task, result); + } + + // TODO(kak): consider removing this + @Override + public ListenableFuture submit(Callable task) { + return (ListenableFuture) super.submit(task); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractScheduledService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractScheduledService.java new file mode 100644 index 0000000000000..59422a914b97e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractScheduledService.java @@ -0,0 +1,757 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateCancelledFuture; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** + * Base class for services that can implement {@link #startUp} and {@link #shutDown} but while in + * the "running" state need to perform a periodic task. Subclasses can implement {@link #startUp}, + * {@link #shutDown} and also a {@link #runOneIteration} method that will be executed periodically. + * + *

This class uses the {@link ScheduledExecutorService} returned from {@link #executor} to run + * the {@link #startUp} and {@link #shutDown} methods and also uses that service to schedule the + * {@link #runOneIteration} that will be executed periodically as specified by its {@link + * Scheduler}. When this service is asked to stop via {@link #stopAsync} it will cancel the periodic + * task (but not interrupt it) and wait for it to stop before running the {@link #shutDown} method. + * + *

Subclasses are guaranteed that the life cycle methods ({@link #runOneIteration}, {@link + * #startUp} and {@link #shutDown}) will never run concurrently. Notably, if any execution of {@link + * #runOneIteration} takes longer than its schedule defines, then subsequent executions may start + * late. Also, all life cycle methods are executed with a lock held, so subclasses can safely modify + * shared state without additional synchronization necessary for visibility to later executions of + * the life cycle methods. + * + *

Usage Example

+ * + *

Here is a sketch of a service which crawls a website and uses the scheduling capabilities to + * rate limit itself. + * + *

{@code
+ * class CrawlingService extends AbstractScheduledService {
+ *   private Set visited;
+ *   private Queue toCrawl;
+ *   protected void startUp() throws Exception {
+ *     toCrawl = readStartingUris();
+ *   }
+ *
+ *   protected void runOneIteration() throws Exception {
+ *     Uri uri = toCrawl.remove();
+ *     Collection newUris = crawl(uri);
+ *     visited.add(uri);
+ *     for (Uri newUri : newUris) {
+ *       if (!visited.contains(newUri)) { toCrawl.add(newUri); }
+ *     }
+ *   }
+ *
+ *   protected void shutDown() throws Exception {
+ *     saveUris(toCrawl);
+ *   }
+ *
+ *   protected Scheduler scheduler() {
+ *     return Scheduler.newFixedRateSchedule(0, 1, TimeUnit.SECONDS);
+ *   }
+ * }
+ * }
+ * + *

This class uses the life cycle methods to read in a list of starting URIs and save the set of + * outstanding URIs when shutting down. Also, it takes advantage of the scheduling functionality to + * rate limit the number of queries we perform. + * + * @author Luke Sandberg + * @since 11.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractScheduledService implements Service { + private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); + + /** + * A scheduler defines the policy for how the {@link AbstractScheduledService} should run its + * task. + * + *

Consider using the {@link #newFixedDelaySchedule} and {@link #newFixedRateSchedule} factory + * methods, these provide {@link Scheduler} instances for the common use case of running the + * service with a fixed schedule. If more flexibility is needed then consider subclassing {@link + * CustomScheduler}. + * + * @author Luke Sandberg + * @since 11.0 + */ + public abstract static class Scheduler { + /** + * Returns a {@link Scheduler} that schedules the task using the {@link + * ScheduledExecutorService#scheduleWithFixedDelay} method. + * + * @param initialDelay the time to delay first execution + * @param delay the delay between the termination of one execution and the commencement of the + * next + * @since 28.0 + */ + public static Scheduler newFixedDelaySchedule(Duration initialDelay, Duration delay) { + return newFixedDelaySchedule( + toNanosSaturated(initialDelay), toNanosSaturated(delay), NANOSECONDS); + } + + /** + * Returns a {@link Scheduler} that schedules the task using the {@link + * ScheduledExecutorService#scheduleWithFixedDelay} method. + * + * @param initialDelay the time to delay first execution + * @param delay the delay between the termination of one execution and the commencement of the + * next + * @param unit the time unit of the initialDelay and delay parameters + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static Scheduler newFixedDelaySchedule( + final long initialDelay, final long delay, final TimeUnit unit) { + checkNotNull(unit); + checkArgument(delay > 0, "delay must be > 0, found %s", delay); + return new Scheduler() { + @Override + public Cancellable schedule( + AbstractService service, ScheduledExecutorService executor, Runnable task) { + return new FutureAsCancellable( + executor.scheduleWithFixedDelay(task, initialDelay, delay, unit)); + } + }; + } + + /** + * Returns a {@link Scheduler} that schedules the task using the {@link + * ScheduledExecutorService#scheduleAtFixedRate} method. + * + * @param initialDelay the time to delay first execution + * @param period the period between successive executions of the task + * @since 28.0 + */ + public static Scheduler newFixedRateSchedule(Duration initialDelay, Duration period) { + return newFixedRateSchedule( + toNanosSaturated(initialDelay), toNanosSaturated(period), NANOSECONDS); + } + + /** + * Returns a {@link Scheduler} that schedules the task using the {@link + * ScheduledExecutorService#scheduleAtFixedRate} method. + * + * @param initialDelay the time to delay first execution + * @param period the period between successive executions of the task + * @param unit the time unit of the initialDelay and period parameters + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static Scheduler newFixedRateSchedule( + final long initialDelay, final long period, final TimeUnit unit) { + checkNotNull(unit); + checkArgument(period > 0, "period must be > 0, found %s", period); + return new Scheduler() { + @Override + public Cancellable schedule( + AbstractService service, ScheduledExecutorService executor, Runnable task) { + return new FutureAsCancellable( + executor.scheduleAtFixedRate(task, initialDelay, period, unit)); + } + }; + } + + /** Schedules the task to run on the provided executor on behalf of the service. */ + abstract Cancellable schedule( + AbstractService service, ScheduledExecutorService executor, Runnable runnable); + + private Scheduler() {} + } + + /* use AbstractService for state management */ + private final AbstractService delegate = new ServiceDelegate(); + + private final class ServiceDelegate extends AbstractService { + + // A handle to the running task so that we can stop it when a shutdown has been requested. + // These two fields are volatile because their values will be accessed from multiple threads. + private volatile Cancellable runningTask; + private volatile ScheduledExecutorService executorService; + + // This lock protects the task so we can ensure that none of the template methods (startUp, + // shutDown or runOneIteration) run concurrently with one another. + // TODO(lukes): why don't we use ListenableFuture to sequence things? Then we could drop the + // lock. + private final ReentrantLock lock = new ReentrantLock(); + + class Task implements Runnable { + @Override + public void run() { + lock.lock(); + try { + /* + * requireNonNull is safe because Task isn't run (or at least it doesn't succeed in taking + * the lock) until after it's scheduled and the runningTask field is set. + */ + if (requireNonNull(runningTask).isCancelled()) { + // task may have been cancelled while blocked on the lock. + return; + } + AbstractScheduledService.this.runOneIteration(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + try { + shutDown(); + } catch (Exception ignored) { + restoreInterruptIfIsInterruptedException(ignored); + logger.log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); + } + notifyFailed(t); + // requireNonNull is safe now, just as it was above. + requireNonNull(runningTask).cancel(false); // prevent future invocations. + } finally { + lock.unlock(); + } + } + } + + private final Runnable task = new Task(); + + @Override + protected final void doStart() { + executorService = + MoreExecutors.renamingDecorator(executor(), () -> serviceName() + " " + state()); + executorService.execute( + () -> { + lock.lock(); + try { + startUp(); + /* + * requireNonNull is safe because executorService is never cleared after the + * assignment above. + */ + requireNonNull(executorService); + runningTask = scheduler().schedule(delegate, executorService, task); + notifyStarted(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); + if (runningTask != null) { + // prevent the task from running if possible + runningTask.cancel(false); + } + } finally { + lock.unlock(); + } + }); + } + + @Override + protected final void doStop() { + // Both requireNonNull calls are safe because doStop can run only after a successful doStart. + requireNonNull(runningTask); + requireNonNull(executorService); + runningTask.cancel(false); + executorService.execute( + () -> { + try { + lock.lock(); + try { + if (state() != State.STOPPING) { + // This means that the state has changed since we were scheduled. This implies + // that an execution of runOneIteration has thrown an exception and we have + // transitioned to a failed state, also this means that shutDown has already + // been called, so we do not want to call it again. + return; + } + shutDown(); + } finally { + lock.unlock(); + } + notifyStopped(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); + } + }); + } + + @Override + public String toString() { + return AbstractScheduledService.this.toString(); + } + } + + /** Constructor for use by subclasses. */ + protected AbstractScheduledService() {} + + /** + * Run one iteration of the scheduled task. If any invocation of this method throws an exception, + * the service will transition to the {@link Service.State#FAILED} state and this method will no + * longer be called. + */ + protected abstract void runOneIteration() throws Exception; + + /** + * Start the service. + * + *

By default this method does nothing. + */ + protected void startUp() throws Exception {} + + /** + * Stop the service. This is guaranteed not to run concurrently with {@link #runOneIteration}. + * + *

By default this method does nothing. + */ + protected void shutDown() throws Exception {} + + /** + * Returns the {@link Scheduler} object used to configure this service. This method will only be + * called once. + */ + // TODO(cpovirk): @ForOverride + protected abstract Scheduler scheduler(); + + /** + * Returns the {@link ScheduledExecutorService} that will be used to execute the {@link #startUp}, + * {@link #runOneIteration} and {@link #shutDown} methods. If this method is overridden the + * executor will not be {@linkplain ScheduledExecutorService#shutdown shutdown} when this service + * {@linkplain Service.State#TERMINATED terminates} or {@linkplain Service.State#TERMINATED + * fails}. Subclasses may override this method to supply a custom {@link ScheduledExecutorService} + * instance. This method is guaranteed to only be called once. + * + *

By default this returns a new {@link ScheduledExecutorService} with a single thread pool + * that sets the name of the thread to the {@linkplain #serviceName() service name}. Also, the + * pool will be {@linkplain ScheduledExecutorService#shutdown() shut down} when the service + * {@linkplain Service.State#TERMINATED terminates} or {@linkplain Service.State#TERMINATED + * fails}. + */ + protected ScheduledExecutorService executor() { + class ThreadFactoryImpl implements ThreadFactory { + @Override + public Thread newThread(Runnable runnable) { + return MoreExecutors.newThread(serviceName(), runnable); + } + } + final ScheduledExecutorService executor = + Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl()); + // Add a listener to shut down the executor after the service is stopped. This ensures that the + // JVM shutdown will not be prevented from exiting after this service has stopped or failed. + // Technically this listener is added after start() was called so it is a little gross, but it + // is called within doStart() so we know that the service cannot terminate or fail concurrently + // with adding this listener so it is impossible to miss an event that we are interested in. + addListener( + new Listener() { + @Override + public void terminated(State from) { + executor.shutdown(); + } + + @Override + public void failed(State from, Throwable failure) { + executor.shutdown(); + } + }, + directExecutor()); + return executor; + } + + /** + * Returns the name of this service. {@link AbstractScheduledService} may include the name in + * debugging output. + * + * @since 14.0 + */ + protected String serviceName() { + return getClass().getSimpleName(); + } + + @Override + public String toString() { + return serviceName() + " [" + state() + "]"; + } + + @Override + public final boolean isRunning() { + return delegate.isRunning(); + } + + @Override + public final State state() { + return delegate.state(); + } + + /** + * @since 13.0 + */ + @Override + public final void addListener(Listener listener, Executor executor) { + delegate.addListener(listener, executor); + } + + /** + * @since 14.0 + */ + @Override + public final Throwable failureCause() { + return delegate.failureCause(); + } + + /** + * @since 15.0 + */ + @Override + public final Service startAsync() { + delegate.startAsync(); + return this; + } + + /** + * @since 15.0 + */ + @Override + public final Service stopAsync() { + delegate.stopAsync(); + return this; + } + + /** + * @since 15.0 + */ + @Override + public final void awaitRunning() { + delegate.awaitRunning(); + } + + /** + * @since 28.0 + */ + @Override + public final void awaitRunning(Duration timeout) throws TimeoutException { + Service.super.awaitRunning(timeout); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + delegate.awaitRunning(timeout, unit); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitTerminated() { + delegate.awaitTerminated(); + } + + /** + * @since 28.0 + */ + @Override + public final void awaitTerminated(Duration timeout) throws TimeoutException { + Service.super.awaitTerminated(timeout); + } + + /** + * @since 15.0 + */ + @Override + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + delegate.awaitTerminated(timeout, unit); + } + + interface Cancellable { + void cancel(boolean mayInterruptIfRunning); + + boolean isCancelled(); + } + + private static final class FutureAsCancellable implements Cancellable { + private final Future delegate; + + FutureAsCancellable(Future delegate) { + this.delegate = delegate; + } + + @Override + public void cancel(boolean mayInterruptIfRunning) { + delegate.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + } + + /** + * A {@link Scheduler} that provides a convenient way for the {@link AbstractScheduledService} to + * use a dynamically changing schedule. After every execution of the task, assuming it hasn't been + * cancelled, the {@link #getNextSchedule} method will be called. + * + * @author Luke Sandberg + * @since 11.0 + */ + public abstract static class CustomScheduler extends Scheduler { + + /** A callable class that can reschedule itself using a {@link CustomScheduler}. */ + private final class ReschedulableCallable implements Callable { + + /** The underlying task. */ + private final Runnable wrappedRunnable; + + /** The executor on which this Callable will be scheduled. */ + private final ScheduledExecutorService executor; + + /** + * The service that is managing this callable. This is used so that failure can be reported + * properly. + */ + /* + * This reference is part of a reference cycle, which is typically something we want to avoid + * under j2objc -- but it is not detected by our j2objc cycle test. The cycle: + * + * - CustomScheduler.service contains an instance of ServiceDelegate. (It needs it so that it + * can call notifyFailed.) + * + * - ServiceDelegate.runningTask contains an instance of ReschedulableCallable (at least in + * the case that the service is using CustomScheduler). (It needs it so that it can cancel + * the task and detect whether it has been cancelled.) + * + * - ReschedulableCallable has a reference back to its enclosing CustomScheduler. (It needs it + * so that it can call getNextSchedule). + * + * Maybe there is a way to avoid this cycle. But we think the cycle is safe enough to ignore: + * Each task is retained for only as long as it is running -- so it's retained only as long as + * it would already be retained by the underlying executor. + * + * If the cycle test starts reporting this cycle in the future, we should add an entry to + * cycle_suppress_list.txt. + */ + private final AbstractService service; + + /** + * This lock is used to ensure safe and correct cancellation, it ensures that a new task is + * not scheduled while a cancel is ongoing. Also it protects the currentFuture variable to + * ensure that it is assigned atomically with being scheduled. + */ + private final ReentrantLock lock = new ReentrantLock(); + + /** The future that represents the next execution of this task. */ + private SupplantableFuture cancellationDelegate; + + ReschedulableCallable( + AbstractService service, ScheduledExecutorService executor, Runnable runnable) { + this.wrappedRunnable = runnable; + this.executor = executor; + this.service = service; + } + + @Override + public Void call() throws Exception { + wrappedRunnable.run(); + reschedule(); + return null; + } + + /** + * Atomically reschedules this task and assigns the new future to {@link + * #cancellationDelegate}. + */ + public Cancellable reschedule() { + // invoke the callback outside the lock, prevents some shenanigans. + Schedule schedule; + try { + schedule = CustomScheduler.this.getNextSchedule(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + service.notifyFailed(t); + return new FutureAsCancellable(immediateCancelledFuture()); + } + // We reschedule ourselves with a lock held for two reasons. 1. we want to make sure that + // cancel calls cancel on the correct future. 2. we want to make sure that the assignment + // to currentFuture doesn't race with itself so that currentFuture is assigned in the + // correct order. + Throwable scheduleFailure = null; + Cancellable toReturn; + lock.lock(); + try { + toReturn = initializeOrUpdateCancellationDelegate(schedule); + } catch (RuntimeException | Error e) { + // If an exception is thrown by the subclass then we need to make sure that the service + // notices and transitions to the FAILED state. We do it by calling notifyFailed directly + // because the service does not monitor the state of the future so if the exception is not + // caught and forwarded to the service the task would stop executing but the service would + // have no idea. + // TODO(lukes): consider building everything in terms of ListenableScheduledFuture then + // the AbstractService could monitor the future directly. Rescheduling is still hard... + // but it would help with some of these lock ordering issues. + scheduleFailure = e; + toReturn = new FutureAsCancellable(immediateCancelledFuture()); + } finally { + lock.unlock(); + } + // Call notifyFailed outside the lock to avoid lock ordering issues. + if (scheduleFailure != null) { + service.notifyFailed(scheduleFailure); + } + return toReturn; + } + + /* + * The GuardedBy checker warns us that we're not holding cancellationDelegate.lock. But in + * fact we are holding it because it is the same as this.lock, which we know we are holding, + * thanks to @GuardedBy above. (cancellationDelegate.lock is initialized to this.lock in the + * call to `new SupplantableFuture` below.) + */ + @SuppressWarnings("GuardedBy") + private Cancellable initializeOrUpdateCancellationDelegate(Schedule schedule) { + if (cancellationDelegate == null) { + return cancellationDelegate = new SupplantableFuture(lock, submitToExecutor(schedule)); + } + if (!cancellationDelegate.currentFuture.isCancelled()) { + cancellationDelegate.currentFuture = submitToExecutor(schedule); + } + return cancellationDelegate; + } + + private ScheduledFuture submitToExecutor(Schedule schedule) { + return executor.schedule(this, schedule.delay, schedule.unit); + } + } + + /** + * Contains the most recently submitted {@code Future}, which may be cancelled or updated, + * always under a lock. + */ + private static final class SupplantableFuture implements Cancellable { + private final ReentrantLock lock; + + private Future currentFuture; + + SupplantableFuture(ReentrantLock lock, Future currentFuture) { + this.lock = lock; + this.currentFuture = currentFuture; + } + + @Override + public void cancel(boolean mayInterruptIfRunning) { + /* + * Lock to ensure that a task cannot be rescheduled while a cancel is ongoing. + * + * In theory, cancel() could execute arbitrary listeners -- bad to do while holding a lock. + * However, we don't expose currentFuture to users, so they can't attach listeners. And the + * Future might not even be a ListenableFuture, just a plain Future. That said, similar + * problems can exist with methods like FutureTask.done(), not to mention slow calls to + * Thread.interrupt() (as discussed in InterruptibleTask). At the end of the day, it's + * unlikely that cancel() will be slow, so we can probably get away with calling it while + * holding a lock. Still, it would be nice to avoid somehow. + */ + lock.lock(); + try { + currentFuture.cancel(mayInterruptIfRunning); + } finally { + lock.unlock(); + } + } + + @Override + public boolean isCancelled() { + lock.lock(); + try { + return currentFuture.isCancelled(); + } finally { + lock.unlock(); + } + } + } + + @Override + final Cancellable schedule( + AbstractService service, ScheduledExecutorService executor, Runnable runnable) { + return new ReschedulableCallable(service, executor, runnable).reschedule(); + } + + /** + * A value object that represents an absolute delay until a task should be invoked. + * + * @author Luke Sandberg + * @since 11.0 + */ + protected static final class Schedule { + + private final long delay; + private final TimeUnit unit; + + /** + * @param delay the time from now to delay execution + * @param unit the time unit of the delay parameter + */ + public Schedule(long delay, TimeUnit unit) { + this.delay = delay; + this.unit = checkNotNull(unit); + } + + /** + * @param delay the time from now to delay execution + * @since 31.1 + */ + public Schedule(Duration delay) { + this(toNanosSaturated(delay), NANOSECONDS); + } + } + + /** + * Calculates the time at which to next invoke the task. + * + *

This is guaranteed to be called immediately after the task has completed an iteration and + * on the same thread as the previous execution of {@link + * AbstractScheduledService#runOneIteration}. + * + * @return a schedule that defines the delay before the next execution. + */ + // TODO(cpovirk): @ForOverride + protected abstract Schedule getNextSchedule() throws Exception; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractService.java new file mode 100644 index 0000000000000..b31ea429cb9c8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractService.java @@ -0,0 +1,640 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.util.concurrent.Monitor.Guard; +import org.apache.iotdb.google.common.util.concurrent.Service.State; + +import java.time.Duration; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.FAILED; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.NEW; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.RUNNING; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.STARTING; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.STOPPING; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.TERMINATED; + +/** + * Base class for implementing services that can handle {@link #doStart} and {@link #doStop} + * requests, responding to them with {@link #notifyStarted()} and {@link #notifyStopped()} + * callbacks. Its subclasses must manage threads manually; consider {@link + * AbstractExecutionThreadService} if you need only a single execution thread. + * + * @author Jesse Wilson + * @author Luke Sandberg + * @since 1.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractService implements Service { + private static final ListenerCallQueue.Event STARTING_EVENT = + new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.starting(); + } + + @Override + public String toString() { + return "starting()"; + } + }; + private static final ListenerCallQueue.Event RUNNING_EVENT = + new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.running(); + } + + @Override + public String toString() { + return "running()"; + } + }; + private static final ListenerCallQueue.Event STOPPING_FROM_STARTING_EVENT = + stoppingEvent(STARTING); + private static final ListenerCallQueue.Event STOPPING_FROM_RUNNING_EVENT = + stoppingEvent(RUNNING); + + private static final ListenerCallQueue.Event TERMINATED_FROM_NEW_EVENT = + terminatedEvent(NEW); + private static final ListenerCallQueue.Event TERMINATED_FROM_STARTING_EVENT = + terminatedEvent(STARTING); + private static final ListenerCallQueue.Event TERMINATED_FROM_RUNNING_EVENT = + terminatedEvent(RUNNING); + private static final ListenerCallQueue.Event TERMINATED_FROM_STOPPING_EVENT = + terminatedEvent(STOPPING); + + private static ListenerCallQueue.Event terminatedEvent(final State from) { + return new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.terminated(from); + } + + @Override + public String toString() { + return "terminated({from = " + from + "})"; + } + }; + } + + private static ListenerCallQueue.Event stoppingEvent(final State from) { + return new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.stopping(from); + } + + @Override + public String toString() { + return "stopping({from = " + from + "})"; + } + }; + } + + private final Monitor monitor = new Monitor(); + + private final Guard isStartable = new IsStartableGuard(); + + private final class IsStartableGuard extends Guard { + IsStartableGuard() { + super(AbstractService.this.monitor); + } + + @Override + public boolean isSatisfied() { + return state() == NEW; + } + } + + private final Guard isStoppable = new IsStoppableGuard(); + + private final class IsStoppableGuard extends Guard { + IsStoppableGuard() { + super(AbstractService.this.monitor); + } + + @Override + public boolean isSatisfied() { + return state().compareTo(RUNNING) <= 0; + } + } + + private final Guard hasReachedRunning = new HasReachedRunningGuard(); + + private final class HasReachedRunningGuard extends Guard { + HasReachedRunningGuard() { + super(AbstractService.this.monitor); + } + + @Override + public boolean isSatisfied() { + return state().compareTo(RUNNING) >= 0; + } + } + + private final Guard isStopped = new IsStoppedGuard(); + + private final class IsStoppedGuard extends Guard { + IsStoppedGuard() { + super(AbstractService.this.monitor); + } + + @Override + public boolean isSatisfied() { + return state().compareTo(TERMINATED) >= 0; + } + } + + /** The listeners to notify during a state transition. */ + private final ListenerCallQueue listeners = new ListenerCallQueue<>(); + + /** + * The current state of the service. This should be written with the lock held but can be read + * without it because it is an immutable object in a volatile field. This is desirable so that + * methods like {@link #state}, {@link #failureCause} and notably {@link #toString} can be run + * without grabbing the lock. + * + *

To update this field correctly the lock must be held to guarantee that the state is + * consistent. + */ + private volatile StateSnapshot snapshot = new StateSnapshot(NEW); + + /** Constructor for use by subclasses. */ + protected AbstractService() {} + + /** + * This method is called by {@link #startAsync} to initiate service startup. The invocation of + * this method should cause a call to {@link #notifyStarted()}, either during this method's run, + * or after it has returned. If startup fails, the invocation should cause a call to {@link + * #notifyFailed(Throwable)} instead. + * + *

This method should return promptly; prefer to do work on a different thread where it is + * convenient. It is invoked exactly once on service startup, even when {@link #startAsync} is + * called multiple times. + */ + protected abstract void doStart(); + + /** + * This method should be used to initiate service shutdown. The invocation of this method should + * cause a call to {@link #notifyStopped()}, either during this method's run, or after it has + * returned. If shutdown fails, the invocation should cause a call to {@link + * #notifyFailed(Throwable)} instead. + * + *

This method should return promptly; prefer to do work on a different thread where it is + * convenient. It is invoked exactly once on service shutdown, even when {@link #stopAsync} is + * called multiple times. + * + *

If {@link #stopAsync} is called on a {@link State#STARTING} service, this method is not + * invoked immediately. Instead, it will be deferred until after the service is {@link + * State#RUNNING}. Services that need to cancel startup work can override {@link #doCancelStart}. + */ + protected abstract void doStop(); + + /** + * This method is called by {@link #stopAsync} when the service is still starting (i.e. {@link + * #startAsync} has been called but {@link #notifyStarted} has not). Subclasses can override the + * method to cancel pending work and then call {@link #notifyStopped} to stop the service. + * + *

This method should return promptly; prefer to do work on a different thread where it is + * convenient. It is invoked exactly once on service shutdown, even when {@link #stopAsync} is + * called multiple times. + * + *

When this method is called {@link #state()} will return {@link State#STOPPING}, which is the + * external state observable by the caller of {@link #stopAsync}. + * + * @since 27.0 + */ + protected void doCancelStart() {} + + @Override + public final Service startAsync() { + if (monitor.enterIf(isStartable)) { + try { + snapshot = new StateSnapshot(STARTING); + enqueueStartingEvent(); + doStart(); + } catch (Throwable startupFailure) { + restoreInterruptIfIsInterruptedException(startupFailure); + notifyFailed(startupFailure); + } finally { + monitor.leave(); + dispatchListenerEvents(); + } + } else { + throw new IllegalStateException("Service " + this + " has already been started"); + } + return this; + } + + @Override + public final Service stopAsync() { + if (monitor.enterIf(isStoppable)) { + try { + State previous = state(); + switch (previous) { + case NEW: + snapshot = new StateSnapshot(TERMINATED); + enqueueTerminatedEvent(NEW); + break; + case STARTING: + snapshot = new StateSnapshot(STARTING, true, null); + enqueueStoppingEvent(STARTING); + doCancelStart(); + break; + case RUNNING: + snapshot = new StateSnapshot(STOPPING); + enqueueStoppingEvent(RUNNING); + doStop(); + break; + case STOPPING: + case TERMINATED: + case FAILED: + // These cases are impossible due to the if statement above. + throw new AssertionError("isStoppable is incorrectly implemented, saw: " + previous); + } + } catch (Throwable shutdownFailure) { + restoreInterruptIfIsInterruptedException(shutdownFailure); + notifyFailed(shutdownFailure); + } finally { + monitor.leave(); + dispatchListenerEvents(); + } + } + return this; + } + + @Override + public final void awaitRunning() { + monitor.enterWhenUninterruptibly(hasReachedRunning); + try { + checkCurrentState(RUNNING); + } finally { + monitor.leave(); + } + } + + /** + * @since 28.0 + */ + @Override + public final void awaitRunning(Duration timeout) throws TimeoutException { + Service.super.awaitRunning(timeout); + } + + @Override + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + if (monitor.enterWhenUninterruptibly(hasReachedRunning, timeout, unit)) { + try { + checkCurrentState(RUNNING); + } finally { + monitor.leave(); + } + } else { + // It is possible due to races that we are currently in the expected state even though we + // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never + // even check the guard. I don't think we care too much about this use case but it could lead + // to a confusing error message. + throw new TimeoutException("Timed out waiting for " + this + " to reach the RUNNING state."); + } + } + + @Override + public final void awaitTerminated() { + monitor.enterWhenUninterruptibly(isStopped); + try { + checkCurrentState(TERMINATED); + } finally { + monitor.leave(); + } + } + + /** + * @since 28.0 + */ + @Override + public final void awaitTerminated(Duration timeout) throws TimeoutException { + Service.super.awaitTerminated(timeout); + } + + @Override + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + if (monitor.enterWhenUninterruptibly(isStopped, timeout, unit)) { + try { + checkCurrentState(TERMINATED); + } finally { + monitor.leave(); + } + } else { + // It is possible due to races that we are currently in the expected state even though we + // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never + // even check the guard. I don't think we care too much about this use case but it could lead + // to a confusing error message. + throw new TimeoutException( + "Timed out waiting for " + + this + + " to reach a terminal state. " + + "Current state: " + + state()); + } + } + + /** Checks that the current state is equal to the expected state. */ + private void checkCurrentState(State expected) { + State actual = state(); + if (actual != expected) { + if (actual == FAILED) { + // Handle this specially so that we can include the failureCause, if there is one. + throw new IllegalStateException( + "Expected the service " + this + " to be " + expected + ", but the service has FAILED", + failureCause()); + } + throw new IllegalStateException( + "Expected the service " + this + " to be " + expected + ", but was " + actual); + } + } + + /** + * Implementing classes should invoke this method once their service has started. It will cause + * the service to transition from {@link State#STARTING} to {@link State#RUNNING}. + * + * @throws IllegalStateException if the service is not {@link State#STARTING}. + */ + protected final void notifyStarted() { + monitor.enter(); + try { + // We have to examine the internal state of the snapshot here to properly handle the stop + // while starting case. + if (snapshot.state != STARTING) { + IllegalStateException failure = + new IllegalStateException( + "Cannot notifyStarted() when the service is " + snapshot.state); + notifyFailed(failure); + throw failure; + } + + if (snapshot.shutdownWhenStartupFinishes) { + snapshot = new StateSnapshot(STOPPING); + // We don't call listeners here because we already did that when we set the + // shutdownWhenStartupFinishes flag. + doStop(); + } else { + snapshot = new StateSnapshot(RUNNING); + enqueueRunningEvent(); + } + } finally { + monitor.leave(); + dispatchListenerEvents(); + } + } + + /** + * Implementing classes should invoke this method once their service has stopped. It will cause + * the service to transition from {@link State#STARTING} or {@link State#STOPPING} to {@link + * State#TERMINATED}. + * + * @throws IllegalStateException if the service is not one of {@link State#STOPPING}, {@link + * State#STARTING}, or {@link State#RUNNING}. + */ + protected final void notifyStopped() { + monitor.enter(); + try { + State previous = state(); + switch (previous) { + case NEW: + case TERMINATED: + case FAILED: + throw new IllegalStateException("Cannot notifyStopped() when the service is " + previous); + case RUNNING: + case STARTING: + case STOPPING: + snapshot = new StateSnapshot(TERMINATED); + enqueueTerminatedEvent(previous); + break; + } + } finally { + monitor.leave(); + dispatchListenerEvents(); + } + } + + /** + * Invoke this method to transition the service to the {@link State#FAILED}. The service will + * not be stopped if it is running. Invoke this method when a service has failed critically + * or otherwise cannot be started nor stopped. + */ + protected final void notifyFailed(Throwable cause) { + checkNotNull(cause); + + monitor.enter(); + try { + State previous = state(); + switch (previous) { + case NEW: + case TERMINATED: + throw new IllegalStateException("Failed while in state:" + previous, cause); + case RUNNING: + case STARTING: + case STOPPING: + snapshot = new StateSnapshot(FAILED, false, cause); + enqueueFailedEvent(previous, cause); + break; + case FAILED: + // Do nothing + break; + } + } finally { + monitor.leave(); + dispatchListenerEvents(); + } + } + + @Override + public final boolean isRunning() { + return state() == RUNNING; + } + + @Override + public final State state() { + return snapshot.externalState(); + } + + /** + * @since 14.0 + */ + @Override + public final Throwable failureCause() { + return snapshot.failureCause(); + } + + /** + * @since 13.0 + */ + @Override + public final void addListener(Listener listener, Executor executor) { + listeners.addListener(listener, executor); + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [" + state() + "]"; + } + + /** + * Attempts to execute all the listeners in {@link #listeners} while not holding the {@link + * #monitor}. + */ + private void dispatchListenerEvents() { + if (!monitor.isOccupiedByCurrentThread()) { + listeners.dispatch(); + } + } + + private void enqueueStartingEvent() { + listeners.enqueue(STARTING_EVENT); + } + + private void enqueueRunningEvent() { + listeners.enqueue(RUNNING_EVENT); + } + + private void enqueueStoppingEvent(final State from) { + if (from == State.STARTING) { + listeners.enqueue(STOPPING_FROM_STARTING_EVENT); + } else if (from == State.RUNNING) { + listeners.enqueue(STOPPING_FROM_RUNNING_EVENT); + } else { + throw new AssertionError(); + } + } + + private void enqueueTerminatedEvent(final State from) { + switch (from) { + case NEW: + listeners.enqueue(TERMINATED_FROM_NEW_EVENT); + break; + case STARTING: + listeners.enqueue(TERMINATED_FROM_STARTING_EVENT); + break; + case RUNNING: + listeners.enqueue(TERMINATED_FROM_RUNNING_EVENT); + break; + case STOPPING: + listeners.enqueue(TERMINATED_FROM_STOPPING_EVENT); + break; + case TERMINATED: + case FAILED: + throw new AssertionError(); + } + } + + private void enqueueFailedEvent(final State from, final Throwable cause) { + // can't memoize this one due to the exception + listeners.enqueue( + new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.failed(from, cause); + } + + @Override + public String toString() { + return "failed({from = " + from + ", cause = " + cause + "})"; + } + }); + } + + /** + * An immutable snapshot of the current state of the service. This class represents a consistent + * snapshot of the state and therefore it can be used to answer simple queries without needing to + * grab a lock. + */ + // @Immutable except that Throwable is mutable (initCause(), setStackTrace(), mutable subclasses). + private static final class StateSnapshot { + /** + * The internal state, which equals external state unless shutdownWhenStartupFinishes is true. + */ + final State state; + + /** If true, the user requested a shutdown while the service was still starting up. */ + final boolean shutdownWhenStartupFinishes; + + /** + * The exception that caused this service to fail. This will be {@code null} unless the service + * has failed. + */ + final Throwable failure; + + StateSnapshot(State internalState) { + this(internalState, false, null); + } + + StateSnapshot(State internalState, boolean shutdownWhenStartupFinishes, Throwable failure) { + checkArgument( + !shutdownWhenStartupFinishes || internalState == STARTING, + "shutdownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.", + internalState); + checkArgument( + (failure != null) == (internalState == FAILED), + "A failure cause should be set if and only if the state is failed. Got %s and %s " + + "instead.", + internalState, + failure); + this.state = internalState; + this.shutdownWhenStartupFinishes = shutdownWhenStartupFinishes; + this.failure = failure; + } + + /** + * @see Service#state() + */ + State externalState() { + if (shutdownWhenStartupFinishes && state == STARTING) { + return STOPPING; + } else { + return state; + } + } + + /** + * @see Service#failureCause() + */ + Throwable failureCause() { + checkState( + state == FAILED, + "failureCause() is only valid if the service has failed, service is %s", + state); + // requireNonNull is safe because the constructor requires a non-null cause with state=FAILED. + return requireNonNull(failure); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractTransformFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractTransformFuture.java new file mode 100644 index 0000000000000..12a8bfca3441a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractTransformFuture.java @@ -0,0 +1,259 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Futures.getDone; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.rejectionPropagatingExecutor; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** Implementations of {@code Futures.transform*}. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +@SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. +abstract class AbstractTransformFuture + extends FluentFuture.TrustedFuture implements Runnable { + static ListenableFuture create( + ListenableFuture input, + AsyncFunction function, + Executor executor) { + checkNotNull(executor); + AsyncTransformFuture output = new AsyncTransformFuture<>(input, function); + input.addListener(output, rejectionPropagatingExecutor(executor, output)); + return output; + } + + static ListenableFuture create( + ListenableFuture input, Function function, Executor executor) { + checkNotNull(function); + TransformFuture output = new TransformFuture<>(input, function); + input.addListener(output, rejectionPropagatingExecutor(executor, output)); + return output; + } + + /* + * In certain circumstances, this field might theoretically not be visible to an afterDone() call + * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture. + */ + ListenableFuture inputFuture; + F function; + + AbstractTransformFuture(ListenableFuture inputFuture, F function) { + this.inputFuture = checkNotNull(inputFuture); + this.function = checkNotNull(function); + } + + @Override + public final void run() { + ListenableFuture localInputFuture = inputFuture; + F localFunction = function; + if (isCancelled() | localInputFuture == null | localFunction == null) { + return; + } + inputFuture = null; + + if (localInputFuture.isCancelled()) { + @SuppressWarnings("unchecked") + boolean unused = + setFuture((ListenableFuture) localInputFuture); // Respects cancellation cause setting + return; + } + + /* + * Any of the setException() calls below can fail if the output Future is cancelled between now + * and then. This means that we're silently swallowing an exception -- maybe even an Error. But + * this is no worse than what FutureTask does in that situation. Additionally, because the + * Future was cancelled, its listeners have been run, so its consumers will not hang. + * + * Contrast this to the situation we have if setResult() throws, a situation described below. + */ + I sourceResult; + try { + sourceResult = getDone(localInputFuture); + } catch (CancellationException e) { + // TODO(user): verify future behavior - unify logic with getFutureValue in AbstractFuture. + // This + // code should be unreachable with correctly implemented Futures. + // Cancel this future and return. + // At this point, inputFuture is cancelled and outputFuture doesn't exist, so the value of + // mayInterruptIfRunning is irrelevant. + cancel(false); + return; + } catch (ExecutionException e) { + // Set the cause of the exception as this future's exception. + setException(e.getCause()); + return; + } catch (RuntimeException e) { + // Bug in inputFuture.get(). Propagate to the output Future so that its consumers don't hang. + setException(e); + return; + } catch (Error e) { + /* + * StackOverflowError, OutOfMemoryError (e.g., from allocating ExecutionException), or + * something. Try to treat it like a RuntimeException. If we overflow the stack again, the + * resulting Error will propagate upward up to the root call to set(). + */ + setException(e); + return; + } + + T transformResult; + try { + transformResult = doTransform(localFunction, sourceResult); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + // This exception is irrelevant in this thread, but useful for the client. + setException(t); + return; + } finally { + function = null; + } + + /* + * If set()/setValue() throws an Error, we let it propagate. Why? The most likely Error is a + * StackOverflowError (from deep transform(..., directExecutor()) nesting), and calling + * setException(stackOverflowError) would fail: + * + * - If the stack overflowed before set()/setValue() could even store the result in the output + * Future, then a call setException() would likely also overflow. + * + * - If the stack overflowed after set()/setValue() stored its result, then a call to + * setException() will be a no-op because the Future is already done. + * + * Both scenarios are bad: The output Future might never complete, or, if it does complete, it + * might not run some of its listeners. The likely result is that the app will hang. (And of + * course stack overflows are bad news in general. For example, we may have overflowed in the + * middle of defining a class. If so, that class will never be loadable in this process.) The + * best we can do (since logging may overflow the stack) is to let the error propagate. Because + * it is an Error, it won't be caught and logged by AbstractFuture.executeListener. Instead, it + * can propagate through many layers of AbstractTransformFuture up to the root call to set(). + * + * https://github.com/google/guava/issues/2254 + * + * Other kinds of Errors are possible: + * + * - OutOfMemoryError from allocations in setFuture(): The calculus here is similar to + * StackOverflowError: We can't reliably call setException(error). + * + * - Any kind of Error from a listener. Even if we could distinguish that case (by exposing some + * extra state from AbstractFuture), our options are limited: A call to setException() would be + * a no-op. We could log, but if that's what we really want, we should modify + * AbstractFuture.executeListener to do so, since that method would have the ability to continue + * to execute other listeners. + * + * What about RuntimeException? If there is a bug in set()/setValue() that produces one, it will + * propagate, too, but only as far as AbstractFuture.executeListener, which will catch and log + * it. + */ + setResult(transformResult); + } + + /** Template method for subtypes to actually run the transform. */ + @ParametricNullness + abstract T doTransform(F function, @ParametricNullness I result) throws Exception; + + /** Template method for subtypes to actually set the result. */ + abstract void setResult(@ParametricNullness T result); + + @Override + protected final void afterDone() { + maybePropagateCancellationTo(inputFuture); + this.inputFuture = null; + this.function = null; + } + + @Override + protected String pendingToString() { + ListenableFuture localInputFuture = inputFuture; + F localFunction = function; + String superString = super.pendingToString(); + String resultString = ""; + if (localInputFuture != null) { + resultString = "inputFuture=[" + localInputFuture + "], "; + } + if (localFunction != null) { + return resultString + "function=[" + localFunction + "]"; + } else if (superString != null) { + return resultString + superString; + } + return null; + } + + /** + * An {@link AbstractTransformFuture} that delegates to an {@link AsyncFunction} and {@link + * #setFuture(ListenableFuture)}. + */ + private static final class AsyncTransformFuture + extends AbstractTransformFuture< + I, O, AsyncFunction, ListenableFuture> { + AsyncTransformFuture( + ListenableFuture inputFuture, AsyncFunction function) { + super(inputFuture, function); + } + + @Override + ListenableFuture doTransform( + AsyncFunction function, @ParametricNullness I input) + throws Exception { + ListenableFuture outputFuture = function.apply(input); + checkNotNull( + outputFuture, + "AsyncFunction.apply returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)? %s", + function); + return outputFuture; + } + + @Override + void setResult(ListenableFuture result) { + setFuture(result); + } + } + + /** + * An {@link AbstractTransformFuture} that delegates to a {@link Function} and {@link + * #set(Object)}. + */ + private static final class TransformFuture + extends AbstractTransformFuture, O> { + TransformFuture( + ListenableFuture inputFuture, Function function) { + super(inputFuture, function); + } + + @Override + @ParametricNullness + O doTransform(Function function, @ParametricNullness I input) { + return function.apply(input); + } + + @Override + void setResult(@ParametricNullness O result) { + set(result); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AggregateFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AggregateFuture.java new file mode 100644 index 0000000000000..0aedfa88bab4d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AggregateFuture.java @@ -0,0 +1,362 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableCollection; + +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; +import static java.util.logging.Level.SEVERE; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.util.concurrent.AggregateFuture.ReleaseResourcesReason.ALL_INPUT_FUTURES_PROCESSED; +import static org.apache.iotdb.google.common.util.concurrent.AggregateFuture.ReleaseResourcesReason.OUTPUT_FUTURE_DONE; +import static org.apache.iotdb.google.common.util.concurrent.Futures.getDone; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; + +/** + * A future whose value is derived from a collection of input futures. + * + * @param the type of the individual inputs + * @param the type of the output (i.e. this) future + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +abstract class AggregateFuture + extends AggregateFutureState { + private static final Logger logger = Logger.getLogger(AggregateFuture.class.getName()); + + /** + * The input futures. After {@link #init}, this field is read only by {@link #afterDone()} (to + * propagate cancellation) and {@link #toString()}. To access the futures' values, {@code + * AggregateFuture} attaches listeners that hold references to one or more inputs. And in the case + * of {@link CombinedFuture}, the user-supplied callback usually has its own references to inputs. + */ + /* + * In certain circumstances, this field might theoretically not be visible to an afterDone() call + * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture. + */ + private ImmutableCollection> futures; + + private final boolean allMustSucceed; + private final boolean collectsValues; + + AggregateFuture( + ImmutableCollection> futures, + boolean allMustSucceed, + boolean collectsValues) { + super(futures.size()); + this.futures = checkNotNull(futures); + this.allMustSucceed = allMustSucceed; + this.collectsValues = collectsValues; + } + + @Override + protected final void afterDone() { + super.afterDone(); + + ImmutableCollection> localFutures = futures; + releaseResources(OUTPUT_FUTURE_DONE); // nulls out `futures` + + if (isCancelled() & localFutures != null) { + boolean wasInterrupted = wasInterrupted(); + for (Future future : localFutures) { + future.cancel(wasInterrupted); + } + } + /* + * We don't call clearSeenExceptions() until processCompleted(). Prior to that, it may be needed + * again if some outstanding input fails. + */ + } + + @Override + protected final String pendingToString() { + ImmutableCollection> localFutures = futures; + if (localFutures != null) { + return "futures=" + localFutures; + } + return super.pendingToString(); + } + + /** + * Must be called at the end of each subclass's constructor. This method performs the "real" + * initialization; we can't put this in the constructor because, in the case where futures are + * already complete, we would not initialize the subclass before calling {@link + * #collectValueFromNonCancelledFuture}. As this is called after the subclass is constructed, + * we're guaranteed to have properly initialized the subclass. + */ + final void init() { + /* + * requireNonNull is safe because this is called from the constructor after `futures` is set but + * before releaseResources could be called (because we have not yet set up any of the listeners + * that could call it, nor exposed this Future for users to call cancel() on). + */ + requireNonNull(futures); + + // Corner case: List is empty. + if (futures.isEmpty()) { + handleAllCompleted(); + return; + } + + // NOTE: If we ever want to use a custom executor here, have a look at CombinedFuture as we'll + // need to handle RejectedExecutionException + + if (allMustSucceed) { + // We need fail fast, so we have to keep track of which future failed so we can propagate + // the exception immediately + + // Register a listener on each Future in the list to update the state of this future. + // Note that if all the futures on the list are done prior to completing this loop, the last + // call to addListener() will callback to setOneValue(), transitively call our cleanup + // listener, and set this.futures to null. + // This is not actually a problem, since the foreach only needs this.futures to be non-null + // at the beginning of the loop. + int i = 0; + for (ListenableFuture future : futures) { + int index = i++; + future.addListener( + () -> { + try { + if (future.isCancelled()) { + // Clear futures prior to cancelling children. This sets our own state but lets + // the input futures keep running, as some of them may be used elsewhere. + futures = null; + cancel(false); + } else { + collectValueFromNonCancelledFuture(index, future); + } + } finally { + /* + * "null" means: There is no need to access `futures` again during + * `processCompleted` because we're reading each value during a call to + * handleOneInputDone. + */ + decrementCountAndMaybeComplete(null); + } + }, + directExecutor()); + } + } else { + /* + * We'll call the user callback or collect the values only when all inputs complete, + * regardless of whether some failed. This lets us avoid calling expensive methods like + * Future.get() when we don't need to (specifically, for whenAllComplete().call*()), and it + * lets all futures share the same listener. + * + * We store `localFutures` inside the listener because `this.futures` might be nulled out by + * the time the listener runs for the final future -- at which point we need to check all + * inputs for exceptions *if* we're collecting values. If we're not, then the listener doesn't + * need access to the futures again, so we can just pass `null`. + * + * TODO(b/112550045): Allocating a single, cheaper listener is (I think) only an optimization. + * If we make some other optimizations, this one will no longer be necessary. The optimization + * could actually hurt in some cases, as it forces us to keep all inputs in memory until the + * final input completes. + */ + ImmutableCollection> localFutures = + collectsValues ? futures : null; + Runnable listener = () -> decrementCountAndMaybeComplete(localFutures); + for (ListenableFuture future : futures) { + future.addListener(listener, directExecutor()); + } + } + } + + /** + * Fails this future with the given Throwable if {@link #allMustSucceed} is true. Also, logs the + * throwable if it is an {@link Error} or if {@link #allMustSucceed} is {@code true}, the + * throwable did not cause this future to fail, and it is the first time we've seen that + * particular Throwable. + */ + private void handleException(Throwable throwable) { + checkNotNull(throwable); + + if (allMustSucceed) { + // As soon as the first one fails, make that failure the result of the output future. + // The results of all other inputs are then ignored (except for logging any failures). + boolean completedWithFailure = setException(throwable); + if (!completedWithFailure) { + // Go up the causal chain to see if we've already seen this cause; if we have, even if + // it's wrapped by a different exception, don't log it. + boolean firstTimeSeeingThisException = addCausalChain(getOrInitSeenExceptions(), throwable); + if (firstTimeSeeingThisException) { + log(throwable); + return; + } + } + } + + /* + * TODO(cpovirk): Should whenAllComplete().call*() log errors, too? Currently, it doesn't call + * handleException() at all. + */ + if (throwable instanceof Error) { + /* + * TODO(cpovirk): Do we really want to log this if we called setException(throwable) and it + * returned true? This was intentional (CL 46470009), but it seems odd compared to how we + * normally handle Error. + * + * Similarly, do we really want to log the same Error more than once? + */ + log(throwable); + } + } + + private static void log(Throwable throwable) { + String message = + (throwable instanceof Error) + ? "Input Future failed with Error" + : "Got more than one input Future failure. Logging failures after the first"; + logger.log(SEVERE, message, throwable); + } + + @Override + final void addInitialException(Set seen) { + checkNotNull(seen); + if (!isCancelled()) { + /* + * requireNonNull is safe because: + * + * - This is a TrustedFuture, so tryInternalFastPathGetFailure will in fact return the failure + * cause if this Future has failed. + * + * - And this future *has* failed: This method is called only from handleException (through + * getOrInitSeenExceptions). handleException tried to call setException and failed, so + * either this Future was cancelled (which we ruled out with the isCancelled check above), + * or it had already failed. (It couldn't have completed *successfully* or even had + * setFuture called on it: Neither of those can happen until we've finished processing all + * the completed inputs. And we're still processing at least one input, the one that + * triggered handleException.) + * + * TODO(cpovirk): Think about whether we could/should use Verify to check the return value of + * addCausalChain. + */ + boolean unused = addCausalChain(seen, requireNonNull(tryInternalFastPathGetFailure())); + } + } + + /** + * Collects the result (success or failure) of one input future. The input must not have been + * cancelled. For details on when this is called, see {@link #collectOneValue}. + */ + private void collectValueFromNonCancelledFuture(int index, Future future) { + try { + // We get the result, even if collectOneValue is a no-op, so that we can fail fast. + collectOneValue(index, getDone(future)); + } catch (ExecutionException e) { + handleException(e.getCause()); + } catch (RuntimeException | Error t) { + handleException(t); + } + } + + private void decrementCountAndMaybeComplete( + ImmutableCollection> futuresIfNeedToCollectAtCompletion) { + int newRemaining = decrementRemainingAndGet(); + checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + processCompleted(futuresIfNeedToCollectAtCompletion); + } + } + + private void processCompleted( + ImmutableCollection> futuresIfNeedToCollectAtCompletion) { + if (futuresIfNeedToCollectAtCompletion != null) { + int i = 0; + for (Future future : futuresIfNeedToCollectAtCompletion) { + if (!future.isCancelled()) { + collectValueFromNonCancelledFuture(i, future); + } + i++; + } + } + clearSeenExceptions(); + handleAllCompleted(); + /* + * Null out fields, including some used in handleAllCompleted() above (like + * `CollectionFuture.values`). This might be a no-op: If this future completed during + * handleAllCompleted(), they will already have been nulled out. But in the case of + * whenAll*().call*(), this future may be pending until the callback runs -- or even longer in + * the case of callAsync(), which waits for the callback's returned future to complete. + */ + releaseResources(ALL_INPUT_FUTURES_PROCESSED); + } + + /** + * Clears fields that are no longer needed after this future has completed -- or at least all its + * inputs have completed (more precisely, after {@link #handleAllCompleted()} has been called). + * Often called multiple times (that is, both when the inputs complete and when the output + * completes). + * + *

This is similar to our proposed {@code afterCommit} method but not quite the same. See the + * description of CL 265462958. + */ + // TODO(user): Write more tests for memory retention. + void releaseResources(ReleaseResourcesReason reason) { + checkNotNull(reason); + /* + * All elements of `futures` are completed, or this future has already completed and read + * `futures` into a local variable (in preparation for propagating cancellation to them). In + * either case, no one needs to read `futures` for cancellation purposes later. (And + * cancellation purposes are the main reason to access `futures`, as discussed in its docs.) + */ + this.futures = null; + } + + enum ReleaseResourcesReason { + OUTPUT_FUTURE_DONE, + ALL_INPUT_FUTURES_PROCESSED, + } + + /** + * If {@code allMustSucceed} is true, called as each future completes; otherwise, if {@code + * collectsValues} is true, called for each future when all futures complete. + */ + abstract void collectOneValue(int index, @ParametricNullness InputT returnValue); + + abstract void handleAllCompleted(); + + /** Adds the chain to the seen set, and returns whether all the chain was new to us. */ + private static boolean addCausalChain(Set seen, Throwable param) { + // Declare a "true" local variable so that the Checker Framework will infer nullness. + Throwable t = param; + + for (; t != null; t = t.getCause()) { + boolean firstTimeSeen = seen.add(t); + if (!firstTimeSeen) { + /* + * We've seen this, so we've seen its causes, too. No need to re-add them. (There's one case + * where this isn't true, but we ignore it: If we record an exception, then someone calls + * initCause() on it, and then we examine it again, we'll conclude that we've seen the whole + * chain before when in fact we haven't. But this should be rare.) + */ + return false; + } + } + return true; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AggregateFutureState.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AggregateFutureState.java new file mode 100644 index 0000000000000..ea0fbfcec4ac3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AggregateFutureState.java @@ -0,0 +1,210 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater; +import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; +import static org.apache.iotdb.google.common.collect.Sets.newConcurrentHashSet; + +/** + * A helper which does some thread-safe operations for aggregate futures, which must be implemented + * differently in GWT. Namely: + * + *

    + *
  • Lazily initializes a set of seen exceptions + *
  • Decrements a counter atomically + *
+ */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class AggregateFutureState + extends AbstractFuture.TrustedFuture { + // Lazily initialized the first time we see an exception; not released until all the input futures + // have completed and we have processed them all. + private volatile Set seenExceptions = null; + + private volatile int remaining; + + private static final AtomicHelper ATOMIC_HELPER; + + private static final Logger log = Logger.getLogger(AggregateFutureState.class.getName()); + + static { + AtomicHelper helper; + Throwable thrownReflectionFailure = null; + try { + helper = + new SafeAtomicHelper( + newUpdater(AggregateFutureState.class, Set.class, "seenExceptions"), + newUpdater(AggregateFutureState.class, "remaining")); + } catch (RuntimeException | Error reflectionFailure) { + // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause + // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. + // For these users fallback to a suboptimal implementation, based on synchronized. This will + // be a definite performance hit to those users. + thrownReflectionFailure = reflectionFailure; + helper = new SynchronizedAtomicHelper(); + } + ATOMIC_HELPER = helper; + // Log after all static init is finished; if an installed logger uses any Futures methods, it + // shouldn't break in cases where reflection is missing/broken. + if (thrownReflectionFailure != null) { + log.log(Level.SEVERE, "SafeAtomicHelper is broken!", thrownReflectionFailure); + } + } + + AggregateFutureState(int remainingFutures) { + this.remaining = remainingFutures; + } + + final Set getOrInitSeenExceptions() { + /* + * The initialization of seenExceptions has to be more complicated than we'd like. The simple + * approach would be for each caller CAS it from null to a Set populated with its exception. But + * there's another race: If the first thread fails with an exception and a second thread + * immediately fails with the same exception: + * + * Thread1: calls setException(), which returns true, context switch before it can CAS + * seenExceptions to its exception + * + * Thread2: calls setException(), which returns false, CASes seenExceptions to its exception, + * and wrongly believes that its exception is new (leading it to logging it when it shouldn't) + * + * Our solution is for threads to CAS seenExceptions from null to a Set populated with _the + * initial exception_, no matter which thread does the work. This ensures that seenExceptions + * always contains not just the current thread's exception but also the initial thread's. + */ + Set seenExceptionsLocal = seenExceptions; + if (seenExceptionsLocal == null) { + // TODO(cpovirk): Should we use a simpler (presumably cheaper) data structure? + /* + * Using weak references here could let us release exceptions earlier, but: + * + * 1. On Android, querying a WeakReference blocks if the GC is doing an otherwise-concurrent + * pass. + * + * 2. We would probably choose to compare exceptions using == instead of equals() (for + * consistency with how weak references are cleared). That's a behavior change -- arguably the + * removal of a feature. + * + * Fortunately, exceptions rarely contain references to expensive resources. + */ + + // + seenExceptionsLocal = newConcurrentHashSet(); + /* + * Other handleException() callers may see this as soon as we publish it. We need to populate + * it with the initial failure before we do, or else they may think that the initial failure + * has never been seen before. + */ + addInitialException(seenExceptionsLocal); + + ATOMIC_HELPER.compareAndSetSeenExceptions(this, null, seenExceptionsLocal); + /* + * If another handleException() caller created the set, we need to use that copy in case yet + * other callers have added to it. + * + * This read is guaranteed to get us the right value because we only set this once (here). + * + * requireNonNull is safe because either our compareAndSet succeeded or it failed because + * another thread did it for us. + */ + seenExceptionsLocal = requireNonNull(seenExceptions); + } + return seenExceptionsLocal; + } + + /** Populates {@code seen} with the exception that was passed to {@code setException}. */ + abstract void addInitialException(Set seen); + + final int decrementRemainingAndGet() { + return ATOMIC_HELPER.decrementAndGetRemainingCount(this); + } + + final void clearSeenExceptions() { + seenExceptions = null; + } + + private abstract static class AtomicHelper { + /** Atomic compare-and-set of the {@link AggregateFutureState#seenExceptions} field. */ + abstract void compareAndSetSeenExceptions( + AggregateFutureState state, Set expect, Set update); + + /** Atomic decrement-and-get of the {@link AggregateFutureState#remaining} field. */ + abstract int decrementAndGetRemainingCount(AggregateFutureState state); + } + + private static final class SafeAtomicHelper extends AtomicHelper { + final AtomicReferenceFieldUpdater, Set> + seenExceptionsUpdater; + + final AtomicIntegerFieldUpdater> remainingCountUpdater; + + @SuppressWarnings({"rawtypes", "unchecked"}) // Unavoidable with reflection API + SafeAtomicHelper( + AtomicReferenceFieldUpdater seenExceptionsUpdater, + AtomicIntegerFieldUpdater remainingCountUpdater) { + this.seenExceptionsUpdater = + (AtomicReferenceFieldUpdater, Set>) + seenExceptionsUpdater; + this.remainingCountUpdater = + (AtomicIntegerFieldUpdater>) remainingCountUpdater; + } + + @Override + void compareAndSetSeenExceptions( + AggregateFutureState state, Set expect, Set update) { + seenExceptionsUpdater.compareAndSet(state, expect, update); + } + + @Override + int decrementAndGetRemainingCount(AggregateFutureState state) { + return remainingCountUpdater.decrementAndGet(state); + } + } + + private static final class SynchronizedAtomicHelper extends AtomicHelper { + @Override + void compareAndSetSeenExceptions( + AggregateFutureState state, Set expect, Set update) { + synchronized (state) { + if (state.seenExceptions == expect) { + state.seenExceptions = update; + } + } + } + + @Override + int decrementAndGetRemainingCount(AggregateFutureState state) { + synchronized (state) { + return --state.remaining; + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AsyncCallable.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AsyncCallable.java new file mode 100644 index 0000000000000..15c4f6570739a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AsyncCallable.java @@ -0,0 +1,47 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.Future; + +/** + * Computes a value, possibly asynchronously. For an example usage and more information, see {@link + * Futures.FutureCombiner#callAsync(AsyncCallable, java.util.concurrent.Executor)}. + * + *

Much like {@link java.util.concurrent.Callable}, but returning a {@link ListenableFuture} + * result. + * + * @since 20.0 + */ +@FunctionalInterface +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface AsyncCallable { + /** + * Computes a result {@code Future}. The output {@code Future} need not be {@linkplain + * Future#isDone done}, making {@code AsyncCallable} suitable for asynchronous derivations. + * + *

Throwing an exception from this method is equivalent to returning a failing {@link + * ListenableFuture}. + */ + ListenableFuture call() throws Exception; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AsyncFunction.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AsyncFunction.java new file mode 100644 index 0000000000000..7ee5e6b0401df --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AsyncFunction.java @@ -0,0 +1,45 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.Future; + +/** + * Transforms a value, possibly asynchronously. For an example usage and more information, see + * {@link Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)}. + * + * @author Chris Povirk + * @since 11.0 + */ +@GwtCompatible +@FunctionalInterface +@ElementTypesAreNonnullByDefault +public interface AsyncFunction { + /** + * Returns an output {@code Future} to use in place of the given {@code input}. The output {@code + * Future} need not be {@linkplain Future#isDone done}, making {@code AsyncFunction} suitable for + * asynchronous derivations. + * + *

Throwing an exception from this method is equivalent to returning a failing {@code Future}. + */ + ListenableFuture apply(@ParametricNullness I input) throws Exception; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicDouble.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicDouble.java new file mode 100644 index 0000000000000..addcc41cfb4a2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicDouble.java @@ -0,0 +1,305 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.function.DoubleBinaryOperator; +import java.util.function.DoubleUnaryOperator; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.longBitsToDouble; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@code double} value that may be updated atomically. See the {@link + * java.util.concurrent.atomic} package specification for description of the properties of atomic + * variables. An {@code AtomicDouble} is used in applications such as atomic accumulation, and + * cannot be used as a replacement for a {@link Double}. However, this class does extend {@code + * Number} to allow uniform access by tools and utilities that deal with numerically-based classes. + * + *

{@code
+ * static boolean bitEquals(double x, double y) {
+ *   long xBits = Double.doubleToRawLongBits(x);
+ *   long yBits = Double.doubleToRawLongBits(y);
+ *   return xBits == yBits;
+ * }
+ * }
+ * + *

It is possible to write a more scalable updater, at the cost of giving up strict atomicity. + * See for example + * DoubleAdder. + * + * @author Doug Lea + * @author Martin Buchholz + * @since 11.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public class AtomicDouble extends Number implements java.io.Serializable { + private static final long serialVersionUID = 0L; + + private transient volatile long value; + + private static final AtomicLongFieldUpdater updater = + AtomicLongFieldUpdater.newUpdater(AtomicDouble.class, "value"); + + /** + * Creates a new {@code AtomicDouble} with the given initial value. + * + * @param initialValue the initial value + */ + public AtomicDouble(double initialValue) { + value = doubleToRawLongBits(initialValue); + } + + /** Creates a new {@code AtomicDouble} with initial value {@code 0.0}. */ + public AtomicDouble() { + // assert doubleToRawLongBits(0.0) == 0L; + } + + /** + * Gets the current value. + * + * @return the current value + */ + public final double get() { + return longBitsToDouble(value); + } + + /** + * Sets to the given value. + * + * @param newValue the new value + */ + public final void set(double newValue) { + long next = doubleToRawLongBits(newValue); + value = next; + } + + /** + * Eventually sets to the given value. + * + * @param newValue the new value + */ + public final void lazySet(double newValue) { + long next = doubleToRawLongBits(newValue); + updater.lazySet(this, next); + } + + /** + * Atomically sets to the given value and returns the old value. + * + * @param newValue the new value + * @return the previous value + */ + public final double getAndSet(double newValue) { + long next = doubleToRawLongBits(newValue); + return longBitsToDouble(updater.getAndSet(this, next)); + } + + /** + * Atomically sets the value to the given updated value if the current value is bitwise equal to the expected value. + * + * @param expect the expected value + * @param update the new value + * @return {@code true} if successful. False return indicates that the actual value was not + * bitwise equal to the expected value. + */ + public final boolean compareAndSet(double expect, double update) { + return updater.compareAndSet(this, doubleToRawLongBits(expect), doubleToRawLongBits(update)); + } + + /** + * Atomically sets the value to the given updated value if the current value is bitwise equal to the expected value. + * + *

May + * fail spuriously and does not provide ordering guarantees, so is only rarely an appropriate + * alternative to {@code compareAndSet}. + * + * @param expect the expected value + * @param update the new value + * @return {@code true} if successful + */ + public final boolean weakCompareAndSet(double expect, double update) { + return updater.weakCompareAndSet( + this, doubleToRawLongBits(expect), doubleToRawLongBits(update)); + } + + /** + * Atomically adds the given value to the current value. + * + * @param delta the value to add + * @return the previous value + */ + public final double getAndAdd(double delta) { + return getAndAccumulate(delta, Double::sum); + } + + /** + * Atomically adds the given value to the current value. + * + * @param delta the value to add + * @return the updated value + */ + public final double addAndGet(double delta) { + return accumulateAndGet(delta, Double::sum); + } + + /** + * Atomically updates the current value with the results of applying the given function to the + * current and given values. + * + * @param x the update value + * @param accumulatorFunction the accumulator function + * @return the previous value + * @since 31.1 + */ + public final double getAndAccumulate(double x, DoubleBinaryOperator accumulatorFunction) { + checkNotNull(accumulatorFunction); + return getAndUpdate(oldValue -> accumulatorFunction.applyAsDouble(oldValue, x)); + } + + /** + * Atomically updates the current value with the results of applying the given function to the + * current and given values. + * + * @param x the update value + * @param accumulatorFunction the accumulator function + * @return the updated value + * @since 31.1 + */ + public final double accumulateAndGet(double x, DoubleBinaryOperator accumulatorFunction) { + checkNotNull(accumulatorFunction); + return updateAndGet(oldValue -> accumulatorFunction.applyAsDouble(oldValue, x)); + } + + /** + * Atomically updates the current value with the results of applying the given function. + * + * @param updateFunction the update function + * @return the previous value + * @since 31.1 + */ + public final double getAndUpdate(DoubleUnaryOperator updateFunction) { + while (true) { + long current = value; + double currentVal = longBitsToDouble(current); + double nextVal = updateFunction.applyAsDouble(currentVal); + long next = doubleToRawLongBits(nextVal); + if (updater.compareAndSet(this, current, next)) { + return currentVal; + } + } + } + + /** + * Atomically updates the current value with the results of applying the given function. + * + * @param updateFunction the update function + * @return the updated value + * @since 31.1 + */ + public final double updateAndGet(DoubleUnaryOperator updateFunction) { + while (true) { + long current = value; + double currentVal = longBitsToDouble(current); + double nextVal = updateFunction.applyAsDouble(currentVal); + long next = doubleToRawLongBits(nextVal); + if (updater.compareAndSet(this, current, next)) { + return nextVal; + } + } + } + + /** + * Returns the String representation of the current value. + * + * @return the String representation of the current value + */ + @Override + public String toString() { + return Double.toString(get()); + } + + /** + * Returns the value of this {@code AtomicDouble} as an {@code int} after a narrowing primitive + * conversion. + */ + @Override + public int intValue() { + return (int) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code long} after a narrowing primitive + * conversion. + */ + @Override + public long longValue() { + return (long) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code float} after a narrowing primitive + * conversion. + */ + @Override + public float floatValue() { + return (float) get(); + } + + /** Returns the value of this {@code AtomicDouble} as a {@code double}. */ + @Override + public double doubleValue() { + return get(); + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData The current value is emitted (a {@code double}). + */ + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + s.defaultWriteObject(); + + s.writeDouble(get()); + } + + /** Reconstitutes the instance from a stream (that is, deserializes it). */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + set(s.readDouble()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicDoubleArray.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicDoubleArray.java new file mode 100644 index 0000000000000..292176c6723c0 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicDoubleArray.java @@ -0,0 +1,325 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.ImmutableLongArray; + +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.function.DoubleBinaryOperator; +import java.util.function.DoubleUnaryOperator; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.longBitsToDouble; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@code double} array in which elements may be updated atomically. See the {@link + * java.util.concurrent.atomic} package specification for description of the properties of atomic + * variables. + * + *

This class compares primitive {@code double} values in methods such as + * {@link #compareAndSet} by comparing their bitwise representation using {@link + * Double#doubleToRawLongBits}, which differs from both the primitive double {@code ==} operator and + * from {@link Double#equals}, as if implemented by: + * + *

{@code
+ * static boolean bitEquals(double x, double y) {
+ *   long xBits = Double.doubleToRawLongBits(x);
+ *   long yBits = Double.doubleToRawLongBits(y);
+ *   return xBits == yBits;
+ * }
+ * }
+ * + * @author Doug Lea + * @author Martin Buchholz + * @since 11.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public class AtomicDoubleArray implements java.io.Serializable { + private static final long serialVersionUID = 0L; + + // Making this non-final is the lesser evil according to Effective + // Java 2nd Edition Item 76: Write readObject methods defensively. + private transient AtomicLongArray longs; + + /** + * Creates a new {@code AtomicDoubleArray} of the given length, with all elements initially zero. + * + * @param length the length of the array + */ + public AtomicDoubleArray(int length) { + this.longs = new AtomicLongArray(length); + } + + /** + * Creates a new {@code AtomicDoubleArray} with the same length as, and all elements copied from, + * the given array. + * + * @param array the array to copy elements from + * @throws NullPointerException if array is null + */ + public AtomicDoubleArray(double[] array) { + int len = array.length; + long[] longArray = new long[len]; + for (int i = 0; i < len; i++) { + longArray[i] = doubleToRawLongBits(array[i]); + } + this.longs = new AtomicLongArray(longArray); + } + + /** + * Returns the length of the array. + * + * @return the length of the array + */ + public final int length() { + return longs.length(); + } + + /** + * Gets the current value at position {@code i}. + * + * @param i the index + * @return the current value + */ + public final double get(int i) { + return longBitsToDouble(longs.get(i)); + } + + /** + * Atomically sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + */ + public final void set(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + longs.set(i, next); + } + + /** + * Eventually sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + */ + public final void lazySet(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + longs.lazySet(i, next); + } + + /** + * Atomically sets the element at position {@code i} to the given value and returns the old value. + * + * @param i the index + * @param newValue the new value + * @return the previous value + */ + public final double getAndSet(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + return longBitsToDouble(longs.getAndSet(i, next)); + } + + /** + * Atomically sets the element at position {@code i} to the given updated value if the current + * value is bitwise equal to the expected value. + * + * @param i the index + * @param expect the expected value + * @param update the new value + * @return true if successful. False return indicates that the actual value was not equal to the + * expected value. + */ + public final boolean compareAndSet(int i, double expect, double update) { + return longs.compareAndSet(i, doubleToRawLongBits(expect), doubleToRawLongBits(update)); + } + + /** + * Atomically sets the element at position {@code i} to the given updated value if the current + * value is bitwise equal to the expected value. + * + *

May + * fail spuriously and does not provide ordering guarantees, so is only rarely an appropriate + * alternative to {@code compareAndSet}. + * + * @param i the index + * @param expect the expected value + * @param update the new value + * @return true if successful + */ + public final boolean weakCompareAndSet(int i, double expect, double update) { + return longs.weakCompareAndSet(i, doubleToRawLongBits(expect), doubleToRawLongBits(update)); + } + + /** + * Atomically adds the given value to the element at index {@code i}. + * + * @param i the index + * @param delta the value to add + * @return the previous value + */ + public final double getAndAdd(int i, double delta) { + return getAndAccumulate(i, delta, Double::sum); + } + + /** + * Atomically adds the given value to the element at index {@code i}. + * + * @param i the index + * @param delta the value to add + * @return the updated value + */ + public double addAndGet(int i, double delta) { + return accumulateAndGet(i, delta, Double::sum); + } + + /** + * Atomically updates the element at index {@code i} with the results of applying the given + * function to the current and given values. + * + * @param i the index to update + * @param x the update value + * @param accumulatorFunction the accumulator function + * @return the previous value + * @since 31.1 + */ + public final double getAndAccumulate(int i, double x, DoubleBinaryOperator accumulatorFunction) { + checkNotNull(accumulatorFunction); + return getAndUpdate(i, oldValue -> accumulatorFunction.applyAsDouble(oldValue, x)); + } + + /** + * Atomically updates the element at index {@code i} with the results of applying the given + * function to the current and given values. + * + * @param i the index to update + * @param x the update value + * @param accumulatorFunction the accumulator function + * @return the updated value + * @since 31.1 + */ + public final double accumulateAndGet(int i, double x, DoubleBinaryOperator accumulatorFunction) { + checkNotNull(accumulatorFunction); + return updateAndGet(i, oldValue -> accumulatorFunction.applyAsDouble(oldValue, x)); + } + + /** + * Atomically updates the element at index {@code i} with the results of applying the given + * function to the current value. + * + * @param i the index to update + * @param updaterFunction the update function + * @return the previous value + * @since 31.1 + */ + public final double getAndUpdate(int i, DoubleUnaryOperator updaterFunction) { + while (true) { + long current = longs.get(i); + double currentVal = longBitsToDouble(current); + double nextVal = updaterFunction.applyAsDouble(currentVal); + long next = doubleToRawLongBits(nextVal); + if (longs.compareAndSet(i, current, next)) { + return currentVal; + } + } + } + + /** + * Atomically updates the element at index {@code i} with the results of applying the given + * function to the current value. + * + * @param i the index to update + * @param updaterFunction the update function + * @return the updated value + * @since 31.1 + */ + public final double updateAndGet(int i, DoubleUnaryOperator updaterFunction) { + while (true) { + long current = longs.get(i); + double currentVal = longBitsToDouble(current); + double nextVal = updaterFunction.applyAsDouble(currentVal); + long next = doubleToRawLongBits(nextVal); + if (longs.compareAndSet(i, current, next)) { + return nextVal; + } + } + } + + /** + * Returns the String representation of the current values of array. + * + * @return the String representation of the current values of array + */ + @Override + public String toString() { + int iMax = length() - 1; + if (iMax == -1) { + return "[]"; + } + + // Double.toString(Math.PI).length() == 17 + StringBuilder b = new StringBuilder((17 + 2) * (iMax + 1)); + b.append('['); + for (int i = 0; ; i++) { + b.append(longBitsToDouble(longs.get(i))); + if (i == iMax) { + return b.append(']').toString(); + } + b.append(',').append(' '); + } + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData The length of the array is emitted (int), followed by all of its elements (each a + * {@code double}) in the proper order. + */ + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + s.defaultWriteObject(); + + // Write out array length + int length = length(); + s.writeInt(length); + + // Write out all elements in the proper order. + for (int i = 0; i < length; i++) { + s.writeDouble(get(i)); + } + } + + /** Reconstitutes the instance from a stream (that is, deserializes it). */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + int length = s.readInt(); + ImmutableLongArray.Builder builder = ImmutableLongArray.builder(); + for (int i = 0; i < length; i++) { + builder.add(doubleToRawLongBits(s.readDouble())); + } + this.longs = new AtomicLongArray(builder.build().toArray()); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicLongMap.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicLongMap.java new file mode 100644 index 0000000000000..bcf796de5f27e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AtomicLongMap.java @@ -0,0 +1,337 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.LongBinaryOperator; +import java.util.function.LongUnaryOperator; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A map containing {@code long} values that can be atomically updated. While writes to a + * traditional {@code Map} rely on {@code put(K, V)}, the typical mechanism for writing to this map + * is {@code addAndGet(K, long)}, which adds a {@code long} to the value currently associated with + * {@code K}. If a key has not yet been associated with a value, its implicit value is zero. + * + *

Most methods in this class treat absent values and zero values identically, as individually + * documented. Exceptions to this are {@link #containsKey}, {@link #size}, {@link #isEmpty}, {@link + * #asMap}, and {@link #toString}. + * + *

Instances of this class may be used by multiple threads concurrently. All operations are + * atomic unless otherwise noted. + * + *

Instances of this class are serializable if the keys are serializable. + * + *

Note: If your values are always positive and less than 2^31, you may wish to use a + * {@link org.apache.iotdb.google.common.collect.Multiset} such as {@link + * org.apache.iotdb.google.common.collect.ConcurrentHashMultiset} instead. + * + *

Warning: Unlike {@code Multiset}, entries whose values are zero are not automatically + * removed from the map. Instead they must be removed manually with {@link #removeAllZeros}. + * + * @author Charles Fry + * @since 11.0 + */ +@GwtCompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public final class AtomicLongMap implements Serializable { + private final ConcurrentHashMap map; + + private AtomicLongMap(ConcurrentHashMap map) { + this.map = checkNotNull(map); + } + + /** Creates an {@code AtomicLongMap}. */ + public static AtomicLongMap create() { + return new AtomicLongMap(new ConcurrentHashMap<>()); + } + + /** Creates an {@code AtomicLongMap} with the same mappings as the specified {@code Map}. */ + public static AtomicLongMap create(Map m) { + AtomicLongMap result = create(); + result.putAll(m); + return result; + } + + /** + * Returns the value associated with {@code key}, or zero if there is no value associated with + * {@code key}. + */ + public long get(K key) { + return map.getOrDefault(key, 0L); + } + + /** + * Increments by one the value currently associated with {@code key}, and returns the new value. + */ + public long incrementAndGet(K key) { + return addAndGet(key, 1); + } + + /** + * Decrements by one the value currently associated with {@code key}, and returns the new value. + */ + public long decrementAndGet(K key) { + return addAndGet(key, -1); + } + + /** + * Adds {@code delta} to the value currently associated with {@code key}, and returns the new + * value. + */ + public long addAndGet(K key, long delta) { + return accumulateAndGet(key, delta, Long::sum); + } + + /** + * Increments by one the value currently associated with {@code key}, and returns the old value. + */ + public long getAndIncrement(K key) { + return getAndAdd(key, 1); + } + + /** + * Decrements by one the value currently associated with {@code key}, and returns the old value. + */ + public long getAndDecrement(K key) { + return getAndAdd(key, -1); + } + + /** + * Adds {@code delta} to the value currently associated with {@code key}, and returns the old + * value. + */ + public long getAndAdd(K key, long delta) { + return getAndAccumulate(key, delta, Long::sum); + } + + /** + * Updates the value currently associated with {@code key} with the specified function, and + * returns the new value. If there is not currently a value associated with {@code key}, the + * function is applied to {@code 0L}. + * + * @since 21.0 + */ + public long updateAndGet(K key, LongUnaryOperator updaterFunction) { + checkNotNull(updaterFunction); + return map.compute( + key, (k, value) -> updaterFunction.applyAsLong((value == null) ? 0L : value.longValue())); + } + + /** + * Updates the value currently associated with {@code key} with the specified function, and + * returns the old value. If there is not currently a value associated with {@code key}, the + * function is applied to {@code 0L}. + * + * @since 21.0 + */ + public long getAndUpdate(K key, LongUnaryOperator updaterFunction) { + checkNotNull(updaterFunction); + AtomicLong holder = new AtomicLong(); + map.compute( + key, + (k, value) -> { + long oldValue = (value == null) ? 0L : value.longValue(); + holder.set(oldValue); + return updaterFunction.applyAsLong(oldValue); + }); + return holder.get(); + } + + /** + * Updates the value currently associated with {@code key} by combining it with {@code x} via the + * specified accumulator function, returning the new value. The previous value associated with + * {@code key} (or zero, if there is none) is passed as the first argument to {@code + * accumulatorFunction}, and {@code x} is passed as the second argument. + * + * @since 21.0 + */ + public long accumulateAndGet(K key, long x, LongBinaryOperator accumulatorFunction) { + checkNotNull(accumulatorFunction); + return updateAndGet(key, oldValue -> accumulatorFunction.applyAsLong(oldValue, x)); + } + + /** + * Updates the value currently associated with {@code key} by combining it with {@code x} via the + * specified accumulator function, returning the old value. The previous value associated with + * {@code key} (or zero, if there is none) is passed as the first argument to {@code + * accumulatorFunction}, and {@code x} is passed as the second argument. + * + * @since 21.0 + */ + public long getAndAccumulate(K key, long x, LongBinaryOperator accumulatorFunction) { + checkNotNull(accumulatorFunction); + return getAndUpdate(key, oldValue -> accumulatorFunction.applyAsLong(oldValue, x)); + } + + /** + * Associates {@code newValue} with {@code key} in this map, and returns the value previously + * associated with {@code key}, or zero if there was no such value. + */ + public long put(K key, long newValue) { + return getAndUpdate(key, x -> newValue); + } + + /** + * Copies all of the mappings from the specified map to this map. The effect of this call is + * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key + * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined + * if the specified map is modified while the operation is in progress. + */ + public void putAll(Map m) { + m.forEach(this::put); + } + + /** + * Removes and returns the value associated with {@code key}. If {@code key} is not in the map, + * this method has no effect and returns zero. + */ + public long remove(K key) { + Long result = map.remove(key); + return (result == null) ? 0L : result.longValue(); + } + + /** + * If {@code (key, value)} is currently in the map, this method removes it and returns true; + * otherwise, this method returns false. + */ + boolean remove(K key, long value) { + return map.remove(key, value); + } + + /** + * Atomically remove {@code key} from the map iff its associated value is 0. + * + * @since 20.0 + */ + public boolean removeIfZero(K key) { + return remove(key, 0); + } + + /** + * Removes all mappings from this map whose values are zero. + * + *

This method is not atomic: the map may be visible in intermediate states, where some of the + * zero values have been removed and others have not. + */ + public void removeAllZeros() { + map.values().removeIf(x -> x == 0); + } + + /** + * Returns the sum of all values in this map. + * + *

This method is not atomic: the sum may or may not include other concurrent operations. + */ + public long sum() { + return map.values().stream().mapToLong(Long::longValue).sum(); + } + + private transient Map asMap; + + /** Returns a live, read-only view of the map backing this {@code AtomicLongMap}. */ + public Map asMap() { + Map result = asMap; + return (result == null) ? asMap = createAsMap() : result; + } + + private Map createAsMap() { + return Collections.unmodifiableMap(map); + } + + /** Returns true if this map contains a mapping for the specified key. */ + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + /** + * Returns the number of key-value mappings in this map. If the map contains more than {@code + * Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. + */ + public int size() { + return map.size(); + } + + /** Returns {@code true} if this map contains no key-value mappings. */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Removes all of the mappings from this map. The map will be empty after this call returns. + * + *

This method is not atomic: the map may not be empty after returning if there were concurrent + * writes. + */ + public void clear() { + map.clear(); + } + + @Override + public String toString() { + return map.toString(); + } + + /** + * If {@code key} is not already associated with a value or if {@code key} is associated with + * zero, associate it with {@code newValue}. Returns the previous value associated with {@code + * key}, or zero if there was no mapping for {@code key}. + */ + long putIfAbsent(K key, long newValue) { + AtomicBoolean noValue = new AtomicBoolean(false); + Long result = + map.compute( + key, + (k, oldValue) -> { + if (oldValue == null || oldValue == 0) { + noValue.set(true); + return newValue; + } else { + return oldValue; + } + }); + return noValue.get() ? 0L : result.longValue(); + } + + /** + * If {@code (key, expectedOldValue)} is currently in the map, this method replaces {@code + * expectedOldValue} with {@code newValue} and returns true; otherwise, this method returns false. + * + *

If {@code expectedOldValue} is zero, this method will succeed if {@code (key, zero)} is + * currently in the map, or if {@code key} is not in the map at all. + */ + boolean replace(K key, long expectedOldValue, long newValue) { + if (expectedOldValue == 0L) { + return putIfAbsent(key, newValue) == 0L; + } else { + return map.replace(key, expectedOldValue, newValue); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Atomics.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Atomics.java new file mode 100644 index 0000000000000..6d7d6fa3bcc92 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Atomics.java @@ -0,0 +1,78 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * Static utility methods pertaining to classes in the {@code java.util.concurrent.atomic} package. + * + * @author Kurt Alfred Kluever + * @since 10.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class Atomics { + private Atomics() {} + + /** + * Creates an {@code AtomicReference} instance with no initial value. + * + * @return a new {@code AtomicReference} with no initial value + */ + public static AtomicReference newReference() { + return new AtomicReference<>(); + } + + /** + * Creates an {@code AtomicReference} instance with the given initial value. + * + * @param initialValue the initial value + * @return a new {@code AtomicReference} with the given initial value + */ + public static AtomicReference newReference( + @ParametricNullness V initialValue) { + return new AtomicReference<>(initialValue); + } + + /** + * Creates an {@code AtomicReferenceArray} instance of given length. + * + * @param length the length of the array + * @return a new {@code AtomicReferenceArray} with the given length + */ + public static AtomicReferenceArray newReferenceArray(int length) { + return new AtomicReferenceArray<>(length); + } + + /** + * Creates an {@code AtomicReferenceArray} instance with the same length as, and all elements + * copied from, the given array. + * + * @param array the array to copy elements from + * @return a new {@code AtomicReferenceArray} copied from the given array + */ + public static AtomicReferenceArray newReferenceArray(E[] array) { + return new AtomicReferenceArray<>(array); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Callables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Callables.java new file mode 100644 index 0000000000000..bd5c2ee9ea358 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Callables.java @@ -0,0 +1,134 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Supplier; + +import java.util.concurrent.Callable; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to the {@link Callable} interface. + * + * @author Isaac Shum + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Callables { + private Callables() {} + + /** Creates a {@code Callable} which immediately returns a preset value each time it is called. */ + public static Callable returning(@ParametricNullness T value) { + return () -> value; + } + + /** + * Creates an {@link AsyncCallable} from a {@link Callable}. + * + *

The {@link AsyncCallable} returns the {@link ListenableFuture} resulting from {@link + * ListeningExecutorService#submit(Callable)}. + * + * @since 20.0 + */ + @J2ktIncompatible + @GwtIncompatible + public static AsyncCallable asAsyncCallable( + Callable callable, ListeningExecutorService listeningExecutorService) { + checkNotNull(callable); + checkNotNull(listeningExecutorService); + return () -> listeningExecutorService.submit(callable); + } + + /** + * Wraps the given callable such that for the duration of {@link Callable#call} the thread that is + * running will have the given name. + * + * @param callable The callable to wrap + * @param nameSupplier The supplier of thread names, {@link Supplier#get get} will be called once + * for each invocation of the wrapped callable. + */ + @J2ktIncompatible + @GwtIncompatible // threads + static Callable threadRenaming( + Callable callable, Supplier nameSupplier) { + checkNotNull(nameSupplier); + checkNotNull(callable); + return () -> { + Thread currentThread = Thread.currentThread(); + String oldName = currentThread.getName(); + boolean restoreName = trySetName(nameSupplier.get(), currentThread); + try { + return callable.call(); + } finally { + if (restoreName) { + boolean unused = trySetName(oldName, currentThread); + } + } + }; + } + + /** + * Wraps the given runnable such that for the duration of {@link Runnable#run} the thread that is + * running with have the given name. + * + * @param task The Runnable to wrap + * @param nameSupplier The supplier of thread names, {@link Supplier#get get} will be called once + * for each invocation of the wrapped callable. + */ + @J2ktIncompatible + @GwtIncompatible // threads + static Runnable threadRenaming(Runnable task, Supplier nameSupplier) { + checkNotNull(nameSupplier); + checkNotNull(task); + return () -> { + Thread currentThread = Thread.currentThread(); + String oldName = currentThread.getName(); + boolean restoreName = trySetName(nameSupplier.get(), currentThread); + try { + task.run(); + } finally { + if (restoreName) { + boolean unused = trySetName(oldName, currentThread); + } + } + }; + } + + /** Tries to set name of the given {@link Thread}, returns true if successful. */ + @J2ktIncompatible + @GwtIncompatible // threads + private static boolean trySetName(String threadName, Thread currentThread) { + /* + * setName should usually succeed, but the security manager can prohibit it. Is there a way to + * see if we have the modifyThread permission without catching an exception? + */ + try { + currentThread.setName(threadName); + return true; + } catch (SecurityException e) { + return false; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ClosingFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ClosingFuture.java new file mode 100644 index 0000000000000..a4d34225158cf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ClosingFuture.java @@ -0,0 +1,2287 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.collect.FluentIterable; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.ClosingFuture.Combiner.AsyncCombiningCallable; +import org.apache.iotdb.google.common.util.concurrent.ClosingFuture.Combiner.CombiningCallable; +import org.apache.iotdb.google.common.util.concurrent.Futures.FutureCombiner; + +import java.io.Closeable; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Logger; + +import static java.util.logging.Level.FINER; +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Level.WARNING; +import static org.apache.iotdb.google.common.base.Functions.constant; +import static org.apache.iotdb.google.common.base.MoreObjects.toStringHelper; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.collect.Lists.asList; +import static org.apache.iotdb.google.common.util.concurrent.ClosingFuture.State.CLOSED; +import static org.apache.iotdb.google.common.util.concurrent.ClosingFuture.State.CLOSING; +import static org.apache.iotdb.google.common.util.concurrent.ClosingFuture.State.OPEN; +import static org.apache.iotdb.google.common.util.concurrent.ClosingFuture.State.SUBSUMED; +import static org.apache.iotdb.google.common.util.concurrent.ClosingFuture.State.WILL_CLOSE; +import static org.apache.iotdb.google.common.util.concurrent.ClosingFuture.State.WILL_CREATE_VALUE_AND_CLOSER; +import static org.apache.iotdb.google.common.util.concurrent.Futures.getDone; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.nonCancellationPropagating; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** + * A step in a pipeline of an asynchronous computation. When the last step in the computation is + * complete, some objects captured during the computation are closed. + * + *

A pipeline of {@code ClosingFuture}s is a tree of steps. Each step represents either an + * asynchronously-computed intermediate value, or else an exception that indicates the failure or + * cancellation of the operation so far. The only way to extract the value or exception from a step + * is by declaring that step to be the last step of the pipeline. Nevertheless, we refer to the + * "value" of a successful step or the "result" (value or exception) of any step. + * + *

    + *
  1. A pipeline starts at its leaf step (or steps), which is created from either a callable + * block or a {@link ListenableFuture}. + *
  2. Each other step is derived from one or more input steps. At each step, zero or more objects + * can be captured for later closing. + *
  3. There is one last step (the root of the tree), from which you can extract the final result + * of the computation. After that result is available (or the computation fails), all objects + * captured by any of the steps in the pipeline are closed. + *
+ * + *

Starting a pipeline

+ * + * Start a {@code ClosingFuture} pipeline {@linkplain #submit(ClosingCallable, Executor) from a + * callable block} that may capture objects for later closing. To start a pipeline from a {@link + * ListenableFuture} that doesn't create resources that should be closed later, you can use {@link + * #from(ListenableFuture)} instead. + * + *

Derived steps

+ * + * A {@code ClosingFuture} step can be derived from one or more input {@code ClosingFuture} steps in + * ways similar to {@link FluentFuture}s: + * + *
    + *
  • by transforming the value from a successful input step, + *
  • by catching the exception from a failed input step, or + *
  • by combining the results of several input steps. + *
+ * + * Each derivation can capture the next value or any intermediate objects for later closing. + * + *

A step can be the input to at most one derived step. Once you transform its value, catch its + * exception, or combine it with others, you cannot do anything else with it, including declare it + * to be the last step of the pipeline. + * + *

Transforming

+ * + * To derive the next step by asynchronously applying a function to an input step's value, call + * {@link #transform(ClosingFunction, Executor)} or {@link #transformAsync(AsyncClosingFunction, + * Executor)} on the input step. + * + *

Catching

+ * + * To derive the next step from a failed input step, call {@link #catching(Class, ClosingFunction, + * Executor)} or {@link #catchingAsync(Class, AsyncClosingFunction, Executor)} on the input step. + * + *

Combining

+ * + * To derive a {@code ClosingFuture} from two or more input steps, pass the input steps to {@link + * #whenAllComplete(Iterable)} or {@link #whenAllSucceed(Iterable)} or its overloads. + * + *

Cancelling

+ * + * Any step in a pipeline can be {@linkplain #cancel(boolean) cancelled}, even after another step + * has been derived, with the same semantics as cancelling a {@link Future}. In addition, a + * successfully cancelled step will immediately start closing all objects captured for later closing + * by it and by its input steps. + * + *

Ending a pipeline

+ * + * Each {@code ClosingFuture} pipeline must be ended. To end a pipeline, decide whether you want to + * close the captured objects automatically or manually. + * + *

Automatically closing

+ * + * You can extract a {@link Future} that represents the result of the last step in the pipeline by + * calling {@link #finishToFuture()}. All objects the pipeline has captured for closing will begin + * to be closed asynchronously after the returned {@code Future} is done: the future + * completes before closing starts, rather than once it has finished. + * + *
{@code
+ * FluentFuture userName =
+ *     ClosingFuture.submit(
+ *             closer -> closer.eventuallyClose(database.newTransaction(), closingExecutor),
+ *             executor)
+ *         .transformAsync((closer, transaction) -> transaction.queryClosingFuture("..."), executor)
+ *         .transform((closer, result) -> result.get("userName"), directExecutor())
+ *         .catching(DBException.class, e -> "no user", directExecutor())
+ *         .finishToFuture();
+ * }
+ * + * In this example, when the {@code userName} {@link Future} is done, the transaction and the query + * result cursor will both be closed, even if the operation is cancelled or fails. + * + *

Manually closing

+ * + * If you want to close the captured objects manually, after you've used the final result, call + * {@link #finishToValueAndCloser(ValueAndCloserConsumer, Executor)} to get an object that holds the + * final result. You then call {@link ValueAndCloser#closeAsync()} to close the captured objects. + * + *
{@code
+ *     ClosingFuture.submit(
+ *             closer -> closer.eventuallyClose(database.newTransaction(), closingExecutor),
+ *             executor)
+ *     .transformAsync((closer, transaction) -> transaction.queryClosingFuture("..."), executor)
+ *     .transform((closer, result) -> result.get("userName"), directExecutor())
+ *     .catching(DBException.class, e -> "no user", directExecutor())
+ *     .finishToValueAndCloser(
+ *         valueAndCloser -> this.userNameValueAndCloser = valueAndCloser, executor);
+ *
+ * // later
+ * try { // get() will throw if the operation failed or was cancelled.
+ *   UserName userName = userNameValueAndCloser.get();
+ *   // do something with userName
+ * } finally {
+ *   userNameValueAndCloser.closeAsync();
+ * }
+ * }
+ * + * In this example, when {@code userNameValueAndCloser.closeAsync()} is called, the transaction and + * the query result cursor will both be closed, even if the operation is cancelled or fails. + * + *

Note that if you don't call {@code closeAsync()}, the captured objects will not be closed. The + * automatic-closing approach described above is safer. + * + * @param the type of the value of this step + * @since 30.0 + */ +// TODO(dpb): Consider reusing one CloseableList for the entire pipeline, modulo combinations. + +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +// TODO(dpb): GWT compatibility. +public final class ClosingFuture { + + private static final Logger logger = Logger.getLogger(ClosingFuture.class.getName()); + + /** + * An object that can capture objects to be closed later, when a {@link ClosingFuture} pipeline is + * done. + */ + public static final class DeferredCloser { + private final CloseableList list; + + DeferredCloser(CloseableList list) { + this.list = list; + } + + /** + * Captures an object to be closed when a {@link ClosingFuture} pipeline is done. + * + *

For users of the {@code -jre} flavor of Guava, the object can be any {@code + * AutoCloseable}. For users of the {@code -android} flavor, the object must be a {@code + * Closeable}. (For more about the flavors, see Adding Guava to your + * build.) + * + *

Be careful when targeting an older SDK than you are building against (most commonly when + * building for Android): Ensure that any object you pass implements the interface not just in + * your current SDK version but also at the oldest version you support. For example, API Level 16 is the first version + * in which {@code Cursor} is {@code Closeable}. To support older versions, pass a wrapper + * {@code Closeable} with a method reference like {@code cursor::close}. + * + *

Note that this method is still binary-compatible between flavors because the erasure of + * its parameter type is {@code Object}, not {@code AutoCloseable} or {@code Closeable}. + * + * @param closeable the object to be closed (see notes above) + * @param closingExecutor the object will be closed on this executor + * @return the first argument + */ + @ParametricNullness + public C eventuallyClose( + @ParametricNullness C closeable, Executor closingExecutor) { + checkNotNull(closingExecutor); + if (closeable != null) { + list.add(closeable, closingExecutor); + } + return closeable; + } + } + + /** + * An operation that computes a result. + * + * @param the type of the result + */ + @FunctionalInterface + public interface ClosingCallable { + /** + * Computes a result, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but + * not before this method completes), even if this method throws or the pipeline is cancelled. + */ + @ParametricNullness + V call(DeferredCloser closer) throws Exception; + } + + /** + * An operation that computes a {@link ClosingFuture} of a result. + * + * @param the type of the result + * @since 30.1 + */ + @FunctionalInterface + public interface AsyncClosingCallable { + /** + * Computes a result, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but + * not before this method completes), even if this method throws or the pipeline is cancelled. + */ + ClosingFuture call(DeferredCloser closer) throws Exception; + } + + /** + * A function from an input to a result. + * + * @param the type of the input to the function + * @param the type of the result of the function + */ + @FunctionalInterface + public interface ClosingFunction { + + /** + * Applies this function to an input, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but + * not before this method completes), even if this method throws or the pipeline is cancelled. + */ + @ParametricNullness + U apply(DeferredCloser closer, @ParametricNullness T input) throws Exception; + } + + /** + * A function from an input to a {@link ClosingFuture} of a result. + * + * @param the type of the input to the function + * @param the type of the result of the function + */ + @FunctionalInterface + public interface AsyncClosingFunction { + /** + * Applies this function to an input, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done (but + * not before this method completes), even if this method throws or the pipeline is cancelled. + */ + ClosingFuture apply(DeferredCloser closer, @ParametricNullness T input) throws Exception; + } + + /** + * An object that holds the final result of an asynchronous {@link ClosingFuture} operation and + * allows the user to close all the closeable objects that were captured during it for later + * closing. + * + *

The asynchronous operation will have completed before this object is created. + * + * @param the type of the value of a successful operation + * @see ClosingFuture#finishToValueAndCloser(ValueAndCloserConsumer, Executor) + */ + public static final class ValueAndCloser { + + private final ClosingFuture closingFuture; + + ValueAndCloser(ClosingFuture closingFuture) { + this.closingFuture = checkNotNull(closingFuture); + } + + /** + * Returns the final value of the associated {@link ClosingFuture}, or throws an exception as + * {@link Future#get()} would. + * + *

Because the asynchronous operation has already completed, this method is synchronous and + * returns immediately. + * + * @throws CancellationException if the computation was cancelled + * @throws ExecutionException if the computation threw an exception + */ + @ParametricNullness + public V get() throws ExecutionException { + return getDone(closingFuture.future); + } + + /** + * Starts closing all closeable objects captured during the {@link ClosingFuture}'s asynchronous + * operation on the {@link Executor}s specified by calls to {@link + * DeferredCloser#eventuallyClose(Object, Executor)}. + * + *

If any such calls specified {@link MoreExecutors#directExecutor()}, those objects will be + * closed synchronously. + * + *

Idempotent: objects will be closed at most once. + */ + public void closeAsync() { + closingFuture.close(); + } + } + + /** + * Represents an operation that accepts a {@link ValueAndCloser} for the last step in a {@link + * ClosingFuture} pipeline. + * + * @param the type of the final value of a successful pipeline + * @see ClosingFuture#finishToValueAndCloser(ValueAndCloserConsumer, Executor) + */ + @FunctionalInterface + public interface ValueAndCloserConsumer { + + /** Accepts a {@link ValueAndCloser} for the last step in a {@link ClosingFuture} pipeline. */ + void accept(ValueAndCloser valueAndCloser); + } + + /** + * Starts a {@link ClosingFuture} pipeline by submitting a callable block to an executor. + * + * @throws java.util.concurrent.RejectedExecutionException if the task cannot be scheduled for + * execution + */ + public static ClosingFuture submit( + ClosingCallable callable, Executor executor) { + return new ClosingFuture<>(callable, executor); + } + + /** + * Starts a {@link ClosingFuture} pipeline by submitting a callable block to an executor. + * + * @throws java.util.concurrent.RejectedExecutionException if the task cannot be scheduled for + * execution + * @since 30.1 + */ + public static ClosingFuture submitAsync( + AsyncClosingCallable callable, Executor executor) { + return new ClosingFuture<>(callable, executor); + } + + /** + * Starts a {@link ClosingFuture} pipeline with a {@link ListenableFuture}. + * + *

{@code future}'s value will not be closed when the pipeline is done even if {@code V} + * implements {@link Closeable}. In order to start a pipeline with a value that will be closed + * when the pipeline is done, use {@link #submit(ClosingCallable, Executor)} instead. + */ + public static ClosingFuture from(ListenableFuture future) { + return new ClosingFuture(future); + } + + /** + * Starts a {@link ClosingFuture} pipeline with a {@link ListenableFuture}. + * + *

If {@code future} succeeds, its value will be closed (using {@code closingExecutor)}) when + * the pipeline is done, even if the pipeline is canceled or fails. + * + *

Cancelling the pipeline will not cancel {@code future}, so that the pipeline can access its + * value in order to close it. + * + * @param future the future to create the {@code ClosingFuture} from. For discussion of the + * future's result type {@code C}, see {@link DeferredCloser#eventuallyClose(Object, + * Executor)}. + * @param closingExecutor the future's result will be closed on this executor + * @deprecated Creating {@link Future}s of closeable types is dangerous in general because the + * underlying value may never be closed if the {@link Future} is canceled after its operation + * begins. Consider replacing code that creates {@link ListenableFuture}s of closeable types, + * including those that pass them to this method, with {@link #submit(ClosingCallable, + * Executor)} in order to ensure that resources do not leak. Or, to start a pipeline with a + * {@link ListenableFuture} that doesn't create values that should be closed, use {@link + * ClosingFuture#from}. + */ + @Deprecated + public static ClosingFuture eventuallyClosing( + ListenableFuture future, final Executor closingExecutor) { + checkNotNull(closingExecutor); + final ClosingFuture closingFuture = new ClosingFuture<>(nonCancellationPropagating(future)); + Futures.addCallback( + future, + new FutureCallback() { + @Override + public void onSuccess(AutoCloseable result) { + closingFuture.closeables.closer.eventuallyClose(result, closingExecutor); + } + + @Override + public void onFailure(Throwable t) {} + }, + directExecutor()); + return closingFuture; + } + + /** + * Starts specifying how to combine {@link ClosingFuture}s into a single pipeline. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the {@code futures}, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static Combiner whenAllComplete(Iterable> futures) { + return new Combiner(false, futures); + } + + /** + * Starts specifying how to combine {@link ClosingFuture}s into a single pipeline. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the arguments, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static Combiner whenAllComplete( + ClosingFuture future1, ClosingFuture... moreFutures) { + return whenAllComplete(asList(future1, moreFutures)); + } + + /** + * Starts specifying how to combine {@link ClosingFuture}s into a single pipeline, assuming they + * all succeed. If any fail, the resulting pipeline will fail. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the {@code futures}, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static Combiner whenAllSucceed(Iterable> futures) { + return new Combiner(true, futures); + } + + /** + * Starts specifying how to combine two {@link ClosingFuture}s into a single pipeline, assuming + * they all succeed. If any fail, the resulting pipeline will fail. + * + *

Calling this method allows you to use lambdas or method references typed with the types of + * the input {@link ClosingFuture}s. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the arguments, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static Combiner2 whenAllSucceed( + ClosingFuture future1, ClosingFuture future2) { + return new Combiner2<>(future1, future2); + } + + /** + * Starts specifying how to combine three {@link ClosingFuture}s into a single pipeline, assuming + * they all succeed. If any fail, the resulting pipeline will fail. + * + *

Calling this method allows you to use lambdas or method references typed with the types of + * the input {@link ClosingFuture}s. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the arguments, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static + Combiner3 whenAllSucceed( + ClosingFuture future1, ClosingFuture future2, ClosingFuture future3) { + return new Combiner3<>(future1, future2, future3); + } + + /** + * Starts specifying how to combine four {@link ClosingFuture}s into a single pipeline, assuming + * they all succeed. If any fail, the resulting pipeline will fail. + * + *

Calling this method allows you to use lambdas or method references typed with the types of + * the input {@link ClosingFuture}s. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the arguments, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static + Combiner4 whenAllSucceed( + ClosingFuture future1, + ClosingFuture future2, + ClosingFuture future3, + ClosingFuture future4) { + return new Combiner4<>(future1, future2, future3, future4); + } + + /** + * Starts specifying how to combine five {@link ClosingFuture}s into a single pipeline, assuming + * they all succeed. If any fail, the resulting pipeline will fail. + * + *

Calling this method allows you to use lambdas or method references typed with the types of + * the input {@link ClosingFuture}s. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the arguments, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static < + V1 extends Object, + V2 extends Object, + V3 extends Object, + V4 extends Object, + V5 extends Object> + Combiner5 whenAllSucceed( + ClosingFuture future1, + ClosingFuture future2, + ClosingFuture future3, + ClosingFuture future4, + ClosingFuture future5) { + return new Combiner5<>(future1, future2, future3, future4, future5); + } + + /** + * Starts specifying how to combine {@link ClosingFuture}s into a single pipeline, assuming they + * all succeed. If any fail, the resulting pipeline will fail. + * + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from any of + * the arguments, or if any has already been {@linkplain #finishToFuture() finished} + */ + public static Combiner whenAllSucceed( + ClosingFuture future1, + ClosingFuture future2, + ClosingFuture future3, + ClosingFuture future4, + ClosingFuture future5, + ClosingFuture future6, + ClosingFuture... moreFutures) { + return whenAllSucceed( + FluentIterable.of(future1, future2, future3, future4, future5, future6) + .append(moreFutures)); + } + + private final AtomicReference state = new AtomicReference<>(OPEN); + private final CloseableList closeables = new CloseableList(); + private final FluentFuture future; + + private ClosingFuture(ListenableFuture future) { + this.future = FluentFuture.from(future); + } + + private ClosingFuture(final ClosingCallable callable, Executor executor) { + checkNotNull(callable); + TrustedListenableFutureTask task = + TrustedListenableFutureTask.create( + new Callable() { + @Override + @ParametricNullness + public V call() throws Exception { + return callable.call(closeables.closer); + } + + @Override + public String toString() { + return callable.toString(); + } + }); + executor.execute(task); + this.future = task; + } + + private ClosingFuture(final AsyncClosingCallable callable, Executor executor) { + checkNotNull(callable); + TrustedListenableFutureTask task = + TrustedListenableFutureTask.create( + new AsyncCallable() { + @Override + public ListenableFuture call() throws Exception { + CloseableList newCloseables = new CloseableList(); + try { + ClosingFuture closingFuture = callable.call(newCloseables.closer); + closingFuture.becomeSubsumedInto(closeables); + return closingFuture.future; + } finally { + closeables.add(newCloseables, directExecutor()); + } + } + + @Override + public String toString() { + return callable.toString(); + } + }); + executor.execute(task); + this.future = task; + } + + /** + * Returns a future that finishes when this step does. Calling {@code get()} on the returned + * future returns {@code null} if the step is successful or throws the same exception that would + * be thrown by calling {@code finishToFuture().get()} if this were the last step. Calling {@code + * cancel()} on the returned future has no effect on the {@code ClosingFuture} pipeline. + * + *

{@code statusFuture} differs from most methods on {@code ClosingFuture}: You can make calls + * to {@code statusFuture} in addition to the call you make to {@link #finishToFuture()} or + * a derivation method on the same instance. This is important because calling {@code + * statusFuture} alone does not provide a way to close the pipeline. + */ + public ListenableFuture statusFuture() { + return nonCancellationPropagating(future.transform(constant(null), directExecutor())); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from this one by applying a function + * to its value. The function can use a {@link DeferredCloser} to capture objects to be closed + * when the pipeline is done. + * + *

If this {@code ClosingFuture} fails, the function will not be called, and the derived {@code + * ClosingFuture} will be equivalent to this one. + * + *

If the function throws an exception, that exception is used as the result of the derived + * {@code ClosingFuture}. + * + *

Example usage: + * + *

{@code
+   * ClosingFuture> rowsFuture =
+   *     queryFuture.transform((closer, result) -> result.getRows(), executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link ListenableFuture#addListener} documentation. All its warnings + * about heavyweight listeners are also applicable to heavyweight functions passed to this method. + * + *

After calling this method, you may not call {@link #finishToFuture()}, {@link + * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on + * this {@code ClosingFuture}. + * + * @param function transforms the value of this step to the value of the derived step + * @param executor executor to run the function in + * @return the derived step + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from this + * one, or if this {@code ClosingFuture} has already been {@linkplain #finishToFuture() + * finished} + */ + public ClosingFuture transform( + final ClosingFunction function, Executor executor) { + checkNotNull(function); + AsyncFunction applyFunction = + new AsyncFunction() { + @Override + public ListenableFuture apply(V input) throws Exception { + return closeables.applyClosingFunction(function, input); + } + + @Override + public String toString() { + return function.toString(); + } + }; + // TODO(dpb): Switch to future.transformSync when that exists (passing a throwing function). + return derive(future.transformAsync(applyFunction, executor)); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from this one by applying a function + * that returns a {@code ClosingFuture} to its value. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done (other than those + * captured by the returned {@link ClosingFuture}). + * + *

If this {@code ClosingFuture} succeeds, the derived one will be equivalent to the one + * returned by the function. + * + *

If this {@code ClosingFuture} fails, the function will not be called, and the derived {@code + * ClosingFuture} will be equivalent to this one. + * + *

If the function throws an exception, that exception is used as the result of the derived + * {@code ClosingFuture}. But if the exception is thrown after the function creates a {@code + * ClosingFuture}, then none of the closeable objects in that {@code ClosingFuture} will be + * closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #transform(ClosingFunction, + * Executor)} instead, with a function that returns the next value directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
  • In case this step doesn't create new closeables, you can adapt an API that returns a + * {@link ListenableFuture} to return a {@code ClosingFuture} by wrapping it with a call to + * {@link #withoutCloser(AsyncFunction)} + *
+ * + *

Example usage: + * + *

{@code
+   * // Result.getRowsClosingFuture() returns a ClosingFuture.
+   * ClosingFuture> rowsFuture =
+   *     queryFuture.transformAsync((closer, result) -> result.getRowsClosingFuture(), executor);
+   *
+   * // Result.writeRowsToOutputStreamFuture() returns a ListenableFuture that resolves to the
+   * // number of written rows. openOutputFile() returns a FileOutputStream (which implements
+   * // Closeable).
+   * ClosingFuture rowsFuture2 =
+   *     queryFuture.transformAsync(
+   *         (closer, result) -> {
+   *           FileOutputStream fos = closer.eventuallyClose(openOutputFile(), closingExecutor);
+   *           return ClosingFuture.from(result.writeRowsToOutputStreamFuture(fos));
+   *      },
+   *      executor);
+   *
+   * // Result.getRowsFuture() returns a ListenableFuture (no new closeables are created).
+   * ClosingFuture> rowsFuture3 =
+   *     queryFuture.transformAsync(withoutCloser(Result::getRowsFuture), executor);
+   *
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link ListenableFuture#addListener} documentation. All its warnings + * about heavyweight listeners are also applicable to heavyweight functions passed to this method. + * (Specifically, {@code directExecutor} functions should avoid heavyweight operations inside + * {@code AsyncClosingFunction.apply}. Any heavyweight operations should occur in other threads + * responsible for completing the returned {@code ClosingFuture}.) + * + *

After calling this method, you may not call {@link #finishToFuture()}, {@link + * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on + * this {@code ClosingFuture}. + * + * @param function transforms the value of this step to a {@code ClosingFuture} with the value of + * the derived step + * @param executor executor to run the function in + * @return the derived step + * @throws IllegalStateException if a {@code ClosingFuture} has already been derived from this + * one, or if this {@code ClosingFuture} has already been {@linkplain #finishToFuture() + * finished} + */ + public ClosingFuture transformAsync( + final AsyncClosingFunction function, Executor executor) { + checkNotNull(function); + AsyncFunction applyFunction = + new AsyncFunction() { + @Override + public ListenableFuture apply(V input) throws Exception { + return closeables.applyAsyncClosingFunction(function, input); + } + + @Override + public String toString() { + return function.toString(); + } + }; + return derive(future.transformAsync(applyFunction, executor)); + } + + /** + * Returns an {@link AsyncClosingFunction} that applies an {@link AsyncFunction} to an input, + * ignoring the DeferredCloser and returning a {@code ClosingFuture} derived from the returned + * {@link ListenableFuture}. + * + *

Use this method to pass a transformation to {@link #transformAsync(AsyncClosingFunction, + * Executor)} or to {@link #catchingAsync(Class, AsyncClosingFunction, Executor)} as long as it + * meets these conditions: + * + *

    + *
  • It does not need to capture any {@link Closeable} objects by calling {@link + * DeferredCloser#eventuallyClose(Object, Executor)}. + *
  • It returns a {@link ListenableFuture}. + *
+ * + *

Example usage: + * + *

{@code
+   * // Result.getRowsFuture() returns a ListenableFuture.
+   * ClosingFuture> rowsFuture =
+   *     queryFuture.transformAsync(withoutCloser(Result::getRowsFuture), executor);
+   * }
+ * + * @param function transforms the value of a {@code ClosingFuture} step to a {@link + * ListenableFuture} with the value of a derived step + */ + public static AsyncClosingFunction withoutCloser( + final AsyncFunction function) { + checkNotNull(function); + return new AsyncClosingFunction() { + @Override + public ClosingFuture apply(DeferredCloser closer, V input) throws Exception { + return ClosingFuture.from(function.apply(input)); + } + }; + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from this one by applying a function + * to its exception if it is an instance of a given exception type. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done. + * + *

If this {@code ClosingFuture} succeeds or fails with a different exception type, the + * function will not be called, and the derived {@code ClosingFuture} will be equivalent to this + * one. + * + *

If the function throws an exception, that exception is used as the result of the derived + * {@code ClosingFuture}. + * + *

Example usage: + * + *

{@code
+   * ClosingFuture queryFuture =
+   *     queryFuture.catching(
+   *         QueryException.class, (closer, x) -> Query.emptyQueryResult(), executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link ListenableFuture#addListener} documentation. All its warnings + * about heavyweight listeners are also applicable to heavyweight functions passed to this method. + * + *

After calling this method, you may not call {@link #finishToFuture()}, {@link + * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on + * this {@code ClosingFuture}. + * + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception + * type is matched against this step's exception. "This step's exception" means the cause of + * the {@link ExecutionException} thrown by {@link Future#get()} on the {@link Future} + * underlying this step or, if {@code get()} throws a different kind of exception, that + * exception itself. To avoid hiding bugs and other unrecoverable errors, callers should + * prefer more specific types, avoiding {@code Throwable.class} in particular. + * @param fallback the function to be called if this step fails with the expected exception type. + * The function's argument is this step's exception. "This step's exception" means the cause + * of the {@link ExecutionException} thrown by {@link Future#get()} on the {@link Future} + * underlying this step or, if {@code get()} throws a different kind of exception, that + * exception itself. + * @param executor the executor that runs {@code fallback} if the input fails + */ + public ClosingFuture catching( + Class exceptionType, ClosingFunction fallback, Executor executor) { + return catchingMoreGeneric(exceptionType, fallback, executor); + } + + // Avoids generic type capture inconsistency problems where |? extends V| is incompatible with V. + private ClosingFuture catchingMoreGeneric( + Class exceptionType, final ClosingFunction fallback, Executor executor) { + checkNotNull(fallback); + AsyncFunction applyFallback = + new AsyncFunction() { + @Override + public ListenableFuture apply(X exception) throws Exception { + return closeables.applyClosingFunction(fallback, exception); + } + + @Override + public String toString() { + return fallback.toString(); + } + }; + // TODO(dpb): Switch to future.catchingSync when that exists (passing a throwing function). + return derive(future.catchingAsync(exceptionType, applyFallback, executor)); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from this one by applying a function + * that returns a {@code ClosingFuture} to its exception if it is an instance of a given exception + * type. The function can use a {@link DeferredCloser} to capture objects to be closed when the + * pipeline is done (other than those captured by the returned {@link ClosingFuture}). + * + *

If this {@code ClosingFuture} fails with an exception of the given type, the derived {@code + * ClosingFuture} will be equivalent to the one returned by the function. + * + *

If this {@code ClosingFuture} succeeds or fails with a different exception type, the + * function will not be called, and the derived {@code ClosingFuture} will be equivalent to this + * one. + * + *

If the function throws an exception, that exception is used as the result of the derived + * {@code ClosingFuture}. But if the exception is thrown after the function creates a {@code + * ClosingFuture}, then none of the closeable objects in that {@code ClosingFuture} will be + * closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #catching(Class, + * ClosingFunction, Executor)} instead, with a function that returns the next value + * directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
  • In case this step doesn't create new closeables, you can adapt an API that returns a + * {@link ListenableFuture} to return a {@code ClosingFuture} by wrapping it with a call to + * {@link #withoutCloser(AsyncFunction)} + *
+ * + *

Example usage: + * + *

{@code
+   * // Fall back to a secondary input stream in case of IOException.
+   * ClosingFuture inputFuture =
+   *     firstInputFuture.catchingAsync(
+   *         IOException.class, (closer, x) -> secondaryInputStreamClosingFuture(), executor);
+   * }
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link ListenableFuture#addListener} documentation. All its warnings + * about heavyweight listeners are also applicable to heavyweight functions passed to this method. + * (Specifically, {@code directExecutor} functions should avoid heavyweight operations inside + * {@code AsyncClosingFunction.apply}. Any heavyweight operations should occur in other threads + * responsible for completing the returned {@code ClosingFuture}.) + * + *

After calling this method, you may not call {@link #finishToFuture()}, {@link + * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, or any other derivation method on + * this {@code ClosingFuture}. + * + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception + * type is matched against this step's exception. "This step's exception" means the cause of + * the {@link ExecutionException} thrown by {@link Future#get()} on the {@link Future} + * underlying this step or, if {@code get()} throws a different kind of exception, that + * exception itself. To avoid hiding bugs and other unrecoverable errors, callers should + * prefer more specific types, avoiding {@code Throwable.class} in particular. + * @param fallback the function to be called if this step fails with the expected exception type. + * The function's argument is this step's exception. "This step's exception" means the cause + * of the {@link ExecutionException} thrown by {@link Future#get()} on the {@link Future} + * underlying this step or, if {@code get()} throws a different kind of exception, that + * exception itself. + * @param executor the executor that runs {@code fallback} if the input fails + */ + // TODO(dpb): Should this do something special if the function throws CancellationException or + // ExecutionException? + public ClosingFuture catchingAsync( + Class exceptionType, + AsyncClosingFunction fallback, + Executor executor) { + return catchingAsyncMoreGeneric(exceptionType, fallback, executor); + } + + // Avoids generic type capture inconsistency problems where |? extends V| is incompatible with V. + private ClosingFuture catchingAsyncMoreGeneric( + Class exceptionType, + final AsyncClosingFunction fallback, + Executor executor) { + checkNotNull(fallback); + AsyncFunction asyncFunction = + new AsyncFunction() { + @Override + public ListenableFuture apply(X exception) throws Exception { + return closeables.applyAsyncClosingFunction(fallback, exception); + } + + @Override + public String toString() { + return fallback.toString(); + } + }; + return derive(future.catchingAsync(exceptionType, asyncFunction, executor)); + } + + /** + * Marks this step as the last step in the {@code ClosingFuture} pipeline. + * + *

The returned {@link Future} is completed when the pipeline's computation completes, or when + * the pipeline is cancelled. + * + *

All objects the pipeline has captured for closing will begin to be closed asynchronously + * after the returned {@code Future} is done: the future completes before closing starts, + * rather than once it has finished. + * + *

After calling this method, you may not call {@link + * #finishToValueAndCloser(ValueAndCloserConsumer, Executor)}, this method, or any other + * derivation method on this {@code ClosingFuture}. + * + * @return a {@link Future} that represents the final value or exception of the pipeline + */ + public FluentFuture finishToFuture() { + if (compareAndUpdateState(OPEN, WILL_CLOSE)) { + logger.log(FINER, "will close {0}", this); + future.addListener( + new Runnable() { + @Override + public void run() { + checkAndUpdateState(WILL_CLOSE, CLOSING); + close(); + checkAndUpdateState(CLOSING, CLOSED); + } + }, + directExecutor()); + } else { + switch (state.get()) { + case SUBSUMED: + throw new IllegalStateException( + "Cannot call finishToFuture() after deriving another step"); + + case WILL_CREATE_VALUE_AND_CLOSER: + throw new IllegalStateException( + "Cannot call finishToFuture() after calling finishToValueAndCloser()"); + + case WILL_CLOSE: + case CLOSING: + case CLOSED: + throw new IllegalStateException("Cannot call finishToFuture() twice"); + + case OPEN: + throw new AssertionError(); + } + } + return future; + } + + /** + * Marks this step as the last step in the {@code ClosingFuture} pipeline. When this step is done, + * {@code receiver} will be called with an object that contains the result of the operation. The + * receiver can store the {@link ValueAndCloser} outside the receiver for later synchronous use. + * + *

After calling this method, you may not call {@link #finishToFuture()}, this method again, or + * any other derivation method on this {@code ClosingFuture}. + * + * @param consumer a callback whose method will be called (using {@code executor}) when this + * operation is done + */ + public void finishToValueAndCloser( + final ValueAndCloserConsumer consumer, Executor executor) { + checkNotNull(consumer); + if (!compareAndUpdateState(OPEN, WILL_CREATE_VALUE_AND_CLOSER)) { + switch (state.get()) { + case SUBSUMED: + throw new IllegalStateException( + "Cannot call finishToValueAndCloser() after deriving another step"); + + case WILL_CLOSE: + case CLOSING: + case CLOSED: + throw new IllegalStateException( + "Cannot call finishToValueAndCloser() after calling finishToFuture()"); + + case WILL_CREATE_VALUE_AND_CLOSER: + throw new IllegalStateException("Cannot call finishToValueAndCloser() twice"); + + case OPEN: + break; + } + throw new AssertionError(state); + } + future.addListener( + new Runnable() { + @Override + public void run() { + provideValueAndCloser(consumer, ClosingFuture.this); + } + }, + executor); + } + + private static void provideValueAndCloser( + ValueAndCloserConsumer consumer, ClosingFuture closingFuture) { + consumer.accept(new ValueAndCloser(closingFuture)); + } + + /** + * Attempts to cancel execution of this step. This attempt will fail if the step has already + * completed, has already been cancelled, or could not be cancelled for some other reason. If + * successful, and this step has not started when {@code cancel} is called, this step should never + * run. + * + *

If successful, causes the objects captured by this step (if already started) and its input + * step(s) for later closing to be closed on their respective {@link Executor}s. If any such calls + * specified {@link MoreExecutors#directExecutor()}, those objects will be closed synchronously. + * + * @param mayInterruptIfRunning {@code true} if the thread executing this task should be + * interrupted; otherwise, in-progress tasks are allowed to complete, but the step will be + * cancelled regardless + * @return {@code false} if the step could not be cancelled, typically because it has already + * completed normally; {@code true} otherwise + */ + public boolean cancel(boolean mayInterruptIfRunning) { + logger.log(FINER, "cancelling {0}", this); + boolean cancelled = future.cancel(mayInterruptIfRunning); + if (cancelled) { + close(); + } + return cancelled; + } + + private void close() { + logger.log(FINER, "closing {0}", this); + closeables.close(); + } + + private ClosingFuture derive(FluentFuture future) { + ClosingFuture derived = new ClosingFuture<>(future); + becomeSubsumedInto(derived.closeables); + return derived; + } + + private void becomeSubsumedInto(CloseableList otherCloseables) { + checkAndUpdateState(OPEN, SUBSUMED); + otherCloseables.add(closeables, directExecutor()); + } + + /** + * An object that can return the value of the {@link ClosingFuture}s that are passed to {@link + * #whenAllComplete(Iterable)} or {@link #whenAllSucceed(Iterable)}. + * + *

Only for use by a {@link CombiningCallable} or {@link AsyncCombiningCallable} object. + */ + public static final class Peeker { + private final ImmutableList> futures; + private volatile boolean beingCalled; + + private Peeker(ImmutableList> futures) { + this.futures = checkNotNull(futures); + } + + /** + * Returns the value of {@code closingFuture}. + * + * @throws ExecutionException if {@code closingFuture} is a failed step + * @throws CancellationException if the {@code closingFuture}'s future was cancelled + * @throws IllegalArgumentException if {@code closingFuture} is not one of the futures passed to + * {@link #whenAllComplete(Iterable)} or {@link #whenAllComplete(Iterable)} + * @throws IllegalStateException if called outside of a call to {@link + * CombiningCallable#call(DeferredCloser, Peeker)} or {@link + * AsyncCombiningCallable#call(DeferredCloser, Peeker)} + */ + @ParametricNullness + public final D getDone(ClosingFuture closingFuture) + throws ExecutionException { + checkState(beingCalled); + checkArgument(futures.contains(closingFuture)); + return Futures.getDone(closingFuture.future); + } + + @ParametricNullness + private V call(CombiningCallable combiner, CloseableList closeables) + throws Exception { + beingCalled = true; + CloseableList newCloseables = new CloseableList(); + try { + return combiner.call(newCloseables.closer, this); + } finally { + closeables.add(newCloseables, directExecutor()); + beingCalled = false; + } + } + + private FluentFuture callAsync( + AsyncCombiningCallable combiner, CloseableList closeables) throws Exception { + beingCalled = true; + CloseableList newCloseables = new CloseableList(); + try { + ClosingFuture closingFuture = combiner.call(newCloseables.closer, this); + closingFuture.becomeSubsumedInto(closeables); + return closingFuture.future; + } finally { + closeables.add(newCloseables, directExecutor()); + beingCalled = false; + } + } + } + + /** + * A builder of a {@link ClosingFuture} step that is derived from more than one input step. + * + *

See {@link #whenAllComplete(Iterable)} and {@link #whenAllSucceed(Iterable)} for how to + * instantiate this class. + * + *

Example: + * + *

{@code
+   * final ClosingFuture file1ReaderFuture = ...;
+   * final ClosingFuture file2ReaderFuture = ...;
+   * ListenableFuture numberOfDifferentLines =
+   *       ClosingFuture.whenAllSucceed(file1ReaderFuture, file2ReaderFuture)
+   *           .call(
+   *               (closer, peeker) -> {
+   *                 BufferedReader file1Reader = peeker.getDone(file1ReaderFuture);
+   *                 BufferedReader file2Reader = peeker.getDone(file2ReaderFuture);
+   *                 return countDifferentLines(file1Reader, file2Reader);
+   *               },
+   *               executor)
+   *           .closing(executor);
+   * }
+ */ + // TODO(cpovirk): Use simple name instead of fully qualified after we stop building with JDK 8. + + public static class Combiner { + + private final CloseableList closeables = new CloseableList(); + + /** + * An operation that returns a result and may throw an exception. + * + * @param the type of the result + */ + @FunctionalInterface + public interface CombiningCallable { + /** + * Computes a result, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + * + * @param peeker used to get the value of any of the input futures + */ + @ParametricNullness + V call(DeferredCloser closer, Peeker peeker) throws Exception; + } + + /** + * An operation that returns a {@link ClosingFuture} result and may throw an exception. + * + * @param the type of the result + */ + @FunctionalInterface + public interface AsyncCombiningCallable { + /** + * Computes a {@link ClosingFuture} result, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + * + * @param peeker used to get the value of any of the input futures + */ + ClosingFuture call(DeferredCloser closer, Peeker peeker) throws Exception; + } + + private final boolean allMustSucceed; + protected final ImmutableList> inputs; + + private Combiner(boolean allMustSucceed, Iterable> inputs) { + this.allMustSucceed = allMustSucceed; + this.inputs = ImmutableList.copyOf(inputs); + for (ClosingFuture input : inputs) { + input.becomeSubsumedInto(closeables); + } + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * combining function to their values. The function can use a {@link DeferredCloser} to capture + * objects to be closed when the pipeline is done. + * + *

If this combiner was returned by a {@link #whenAllSucceed} method and any of the inputs + * fail, so will the returned step. + * + *

If the combiningCallable throws a {@code CancellationException}, the pipeline will be + * cancelled. + * + *

If the combiningCallable throws an {@code ExecutionException}, the cause of the thrown + * {@code ExecutionException} will be extracted and used as the failure of the derived step. + */ + public ClosingFuture call( + final CombiningCallable combiningCallable, Executor executor) { + Callable callable = + new Callable() { + @Override + @ParametricNullness + public V call() throws Exception { + return new Peeker(inputs).call(combiningCallable, closeables); + } + + @Override + public String toString() { + return combiningCallable.toString(); + } + }; + ClosingFuture derived = new ClosingFuture<>(futureCombiner().call(callable, executor)); + derived.closeables.add(closeables, directExecutor()); + return derived; + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * {@code ClosingFuture}-returning function to their values. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done (other than those + * captured by the returned {@link ClosingFuture}). + * + *

If this combiner was returned by a {@link #whenAllSucceed} method and any of the inputs + * fail, so will the returned step. + * + *

If the combiningCallable throws a {@code CancellationException}, the pipeline will be + * cancelled. + * + *

If the combiningCallable throws an {@code ExecutionException}, the cause of the thrown + * {@code ExecutionException} will be extracted and used as the failure of the derived step. + * + *

If the combiningCallable throws any other exception, it will be used as the failure of the + * derived step. + * + *

If an exception is thrown after the combiningCallable creates a {@code ClosingFuture}, + * then none of the closeable objects in that {@code ClosingFuture} will be closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, + * Executor)} instead, with a function that returns the next value directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
+ * + *

The same warnings about doing heavyweight operations within {@link + * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. + */ + public ClosingFuture callAsync( + final AsyncCombiningCallable combiningCallable, Executor executor) { + AsyncCallable asyncCallable = + new AsyncCallable() { + @Override + public ListenableFuture call() throws Exception { + return new Peeker(inputs).callAsync(combiningCallable, closeables); + } + + @Override + public String toString() { + return combiningCallable.toString(); + } + }; + ClosingFuture derived = + new ClosingFuture<>(futureCombiner().callAsync(asyncCallable, executor)); + derived.closeables.add(closeables, directExecutor()); + return derived; + } + + private FutureCombiner futureCombiner() { + return allMustSucceed + ? Futures.whenAllSucceed(inputFutures()) + : Futures.whenAllComplete(inputFutures()); + } + + private ImmutableList> inputFutures() { + return FluentIterable.from(inputs) + .>transform(future -> future.future) + .toList(); + } + } + + /** + * A generic {@link Combiner} that lets you use a lambda or method reference to combine two {@link + * ClosingFuture}s. Use {@link #whenAllSucceed(ClosingFuture, ClosingFuture)} to start this + * combination. + * + * @param the type returned by the first future + * @param the type returned by the second future + */ + public static final class Combiner2 extends Combiner { + + /** + * A function that returns a value when applied to the values of the two futures passed to + * {@link #whenAllSucceed(ClosingFuture, ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the function + */ + @FunctionalInterface + public interface ClosingFunction2 { + + /** + * Applies this function to two inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + @ParametricNullness + U apply(DeferredCloser closer, @ParametricNullness V1 value1, @ParametricNullness V2 value2) + throws Exception; + } + + /** + * A function that returns a {@link ClosingFuture} when applied to the values of the two futures + * passed to {@link #whenAllSucceed(ClosingFuture, ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the function + */ + @FunctionalInterface + public interface AsyncClosingFunction2 { + + /** + * Applies this function to two inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + ClosingFuture apply( + DeferredCloser closer, @ParametricNullness V1 value1, @ParametricNullness V2 value2) + throws Exception; + } + + private final ClosingFuture future1; + private final ClosingFuture future2; + + private Combiner2(ClosingFuture future1, ClosingFuture future2) { + super(true, ImmutableList.of(future1, future2)); + this.future1 = future1; + this.future2 = future2; + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * combining function to their values. The function can use a {@link DeferredCloser} to capture + * objects to be closed when the pipeline is done. + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture)} and + * any of the inputs fail, so will the returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + */ + public ClosingFuture call( + final ClosingFunction2 function, Executor executor) { + return call( + new CombiningCallable() { + @Override + @ParametricNullness + public U call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply(closer, peeker.getDone(future1), peeker.getDone(future2)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * {@code ClosingFuture}-returning function to their values. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done (other than those + * captured by the returned {@link ClosingFuture}). + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture)} and + * any of the inputs fail, so will the returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + * + *

If the function throws any other exception, it will be used as the failure of the derived + * step. + * + *

If an exception is thrown after the function creates a {@code ClosingFuture}, then none of + * the closeable objects in that {@code ClosingFuture} will be closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, + * Executor)} instead, with a function that returns the next value directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
+ * + *

The same warnings about doing heavyweight operations within {@link + * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. + */ + public ClosingFuture callAsync( + final AsyncClosingFunction2 function, Executor executor) { + return callAsync( + new AsyncCombiningCallable() { + @Override + public ClosingFuture call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply(closer, peeker.getDone(future1), peeker.getDone(future2)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + } + + /** + * A generic {@link Combiner} that lets you use a lambda or method reference to combine three + * {@link ClosingFuture}s. Use {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture)} to start this combination. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + */ + public static final class Combiner3 + extends Combiner { + /** + * A function that returns a value when applied to the values of the three futures passed to + * {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the function + */ + @FunctionalInterface + public interface ClosingFunction3< + V1 extends Object, V2 extends Object, V3 extends Object, U extends Object> { + /** + * Applies this function to three inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + @ParametricNullness + U apply( + DeferredCloser closer, + @ParametricNullness V1 value1, + @ParametricNullness V2 value2, + @ParametricNullness V3 value3) + throws Exception; + } + + /** + * A function that returns a {@link ClosingFuture} when applied to the values of the three + * futures passed to {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the function + */ + @FunctionalInterface + public interface AsyncClosingFunction3< + V1 extends Object, V2 extends Object, V3 extends Object, U extends Object> { + /** + * Applies this function to three inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + ClosingFuture apply( + DeferredCloser closer, + @ParametricNullness V1 value1, + @ParametricNullness V2 value2, + @ParametricNullness V3 value3) + throws Exception; + } + + private final ClosingFuture future1; + private final ClosingFuture future2; + private final ClosingFuture future3; + + private Combiner3( + ClosingFuture future1, ClosingFuture future2, ClosingFuture future3) { + super(true, ImmutableList.of(future1, future2, future3)); + this.future1 = future1; + this.future2 = future2; + this.future3 = future3; + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * combining function to their values. The function can use a {@link DeferredCloser} to capture + * objects to be closed when the pipeline is done. + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture)} and any of the inputs fail, so will the returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + */ + public ClosingFuture call( + final ClosingFunction3 function, Executor executor) { + return call( + new CombiningCallable() { + @Override + @ParametricNullness + public U call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply( + closer, + peeker.getDone(future1), + peeker.getDone(future2), + peeker.getDone(future3)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * {@code ClosingFuture}-returning function to their values. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done (other than those + * captured by the returned {@link ClosingFuture}). + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture)} and any of the inputs fail, so will the returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + * + *

If the function throws any other exception, it will be used as the failure of the derived + * step. + * + *

If an exception is thrown after the function creates a {@code ClosingFuture}, then none of + * the closeable objects in that {@code ClosingFuture} will be closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, + * Executor)} instead, with a function that returns the next value directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
+ * + *

The same warnings about doing heavyweight operations within {@link + * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. + */ + public ClosingFuture callAsync( + final AsyncClosingFunction3 function, Executor executor) { + return callAsync( + new AsyncCombiningCallable() { + @Override + public ClosingFuture call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply( + closer, + peeker.getDone(future1), + peeker.getDone(future2), + peeker.getDone(future3)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + } + + /** + * A generic {@link Combiner} that lets you use a lambda or method reference to combine four + * {@link ClosingFuture}s. Use {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture, + * ClosingFuture)} to start this combination. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the fourth future + */ + public static final class Combiner4< + V1 extends Object, V2 extends Object, V3 extends Object, V4 extends Object> + extends Combiner { + /** + * A function that returns a value when applied to the values of the four futures passed to + * {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture, ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the fourth future + * @param the type returned by the function + */ + @FunctionalInterface + public interface ClosingFunction4< + V1 extends Object, + V2 extends Object, + V3 extends Object, + V4 extends Object, + U extends Object> { + /** + * Applies this function to four inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + @ParametricNullness + U apply( + DeferredCloser closer, + @ParametricNullness V1 value1, + @ParametricNullness V2 value2, + @ParametricNullness V3 value3, + @ParametricNullness V4 value4) + throws Exception; + } + + /** + * A function that returns a {@link ClosingFuture} when applied to the values of the four + * futures passed to {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture, + * ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the fourth future + * @param the type returned by the function + */ + @FunctionalInterface + public interface AsyncClosingFunction4< + V1 extends Object, + V2 extends Object, + V3 extends Object, + V4 extends Object, + U extends Object> { + /** + * Applies this function to four inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + ClosingFuture apply( + DeferredCloser closer, + @ParametricNullness V1 value1, + @ParametricNullness V2 value2, + @ParametricNullness V3 value3, + @ParametricNullness V4 value4) + throws Exception; + } + + private final ClosingFuture future1; + private final ClosingFuture future2; + private final ClosingFuture future3; + private final ClosingFuture future4; + + private Combiner4( + ClosingFuture future1, + ClosingFuture future2, + ClosingFuture future3, + ClosingFuture future4) { + super(true, ImmutableList.of(future1, future2, future3, future4)); + this.future1 = future1; + this.future2 = future2; + this.future3 = future3; + this.future4 = future4; + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * combining function to their values. The function can use a {@link DeferredCloser} to capture + * objects to be closed when the pipeline is done. + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture, ClosingFuture)} and any of the inputs fail, so will the returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + */ + public ClosingFuture call( + final ClosingFunction4 function, Executor executor) { + return call( + new CombiningCallable() { + @Override + @ParametricNullness + public U call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply( + closer, + peeker.getDone(future1), + peeker.getDone(future2), + peeker.getDone(future3), + peeker.getDone(future4)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * {@code ClosingFuture}-returning function to their values. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done (other than those + * captured by the returned {@link ClosingFuture}). + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture, ClosingFuture)} and any of the inputs fail, so will the returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + * + *

If the function throws any other exception, it will be used as the failure of the derived + * step. + * + *

If an exception is thrown after the function creates a {@code ClosingFuture}, then none of + * the closeable objects in that {@code ClosingFuture} will be closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, + * Executor)} instead, with a function that returns the next value directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
+ * + *

The same warnings about doing heavyweight operations within {@link + * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. + */ + public ClosingFuture callAsync( + final AsyncClosingFunction4 function, Executor executor) { + return callAsync( + new AsyncCombiningCallable() { + @Override + public ClosingFuture call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply( + closer, + peeker.getDone(future1), + peeker.getDone(future2), + peeker.getDone(future3), + peeker.getDone(future4)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + } + + /** + * A generic {@link Combiner} that lets you use a lambda or method reference to combine five + * {@link ClosingFuture}s. Use {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture, + * ClosingFuture, ClosingFuture)} to start this combination. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the fourth future + * @param the type returned by the fifth future + */ + public static final class Combiner5< + V1 extends Object, + V2 extends Object, + V3 extends Object, + V4 extends Object, + V5 extends Object> + extends Combiner { + /** + * A function that returns a value when applied to the values of the five futures passed to + * {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture, ClosingFuture, + * ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the fourth future + * @param the type returned by the fifth future + * @param the type returned by the function + */ + @FunctionalInterface + public interface ClosingFunction5< + V1 extends Object, + V2 extends Object, + V3 extends Object, + V4 extends Object, + V5 extends Object, + U extends Object> { + /** + * Applies this function to five inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + @ParametricNullness + U apply( + DeferredCloser closer, + @ParametricNullness V1 value1, + @ParametricNullness V2 value2, + @ParametricNullness V3 value3, + @ParametricNullness V4 value4, + @ParametricNullness V5 value5) + throws Exception; + } + + /** + * A function that returns a {@link ClosingFuture} when applied to the values of the five + * futures passed to {@link #whenAllSucceed(ClosingFuture, ClosingFuture, ClosingFuture, + * ClosingFuture, ClosingFuture)}. + * + * @param the type returned by the first future + * @param the type returned by the second future + * @param the type returned by the third future + * @param the type returned by the fourth future + * @param the type returned by the fifth future + * @param the type returned by the function + */ + @FunctionalInterface + public interface AsyncClosingFunction5< + V1 extends Object, + V2 extends Object, + V3 extends Object, + V4 extends Object, + V5 extends Object, + U extends Object> { + /** + * Applies this function to five inputs, or throws an exception if unable to do so. + * + *

Any objects that are passed to {@link DeferredCloser#eventuallyClose(Object, Executor) + * closer.eventuallyClose()} will be closed when the {@link ClosingFuture} pipeline is done + * (but not before this method completes), even if this method throws or the pipeline is + * cancelled. + */ + ClosingFuture apply( + DeferredCloser closer, + @ParametricNullness V1 value1, + @ParametricNullness V2 value2, + @ParametricNullness V3 value3, + @ParametricNullness V4 value4, + @ParametricNullness V5 value5) + throws Exception; + } + + private final ClosingFuture future1; + private final ClosingFuture future2; + private final ClosingFuture future3; + private final ClosingFuture future4; + private final ClosingFuture future5; + + private Combiner5( + ClosingFuture future1, + ClosingFuture future2, + ClosingFuture future3, + ClosingFuture future4, + ClosingFuture future5) { + super(true, ImmutableList.of(future1, future2, future3, future4, future5)); + this.future1 = future1; + this.future2 = future2; + this.future3 = future3; + this.future4 = future4; + this.future5 = future5; + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * combining function to their values. The function can use a {@link DeferredCloser} to capture + * objects to be closed when the pipeline is done. + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture, ClosingFuture, ClosingFuture)} and any of the inputs fail, so will the + * returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + */ + public ClosingFuture call( + final ClosingFunction5 function, Executor executor) { + return call( + new CombiningCallable() { + @Override + @ParametricNullness + public U call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply( + closer, + peeker.getDone(future1), + peeker.getDone(future2), + peeker.getDone(future3), + peeker.getDone(future4), + peeker.getDone(future5)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + + /** + * Returns a new {@code ClosingFuture} pipeline step derived from the inputs by applying a + * {@code ClosingFuture}-returning function to their values. The function can use a {@link + * DeferredCloser} to capture objects to be closed when the pipeline is done (other than those + * captured by the returned {@link ClosingFuture}). + * + *

If this combiner was returned by {@link #whenAllSucceed(ClosingFuture, ClosingFuture, + * ClosingFuture, ClosingFuture, ClosingFuture)} and any of the inputs fail, so will the + * returned step. + * + *

If the function throws a {@code CancellationException}, the pipeline will be cancelled. + * + *

If the function throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and used as the failure of the derived step. + * + *

If the function throws any other exception, it will be used as the failure of the derived + * step. + * + *

If an exception is thrown after the function creates a {@code ClosingFuture}, then none of + * the closeable objects in that {@code ClosingFuture} will be closed. + * + *

Usage guidelines for this method: + * + *

    + *
  • Use this method only when calling an API that returns a {@link ListenableFuture} or a + * {@code ClosingFuture}. If possible, prefer calling {@link #call(CombiningCallable, + * Executor)} instead, with a function that returns the next value directly. + *
  • Call {@link DeferredCloser#eventuallyClose(Object, Executor) closer.eventuallyClose()} + * for every closeable object this step creates in order to capture it for later closing. + *
  • Return a {@code ClosingFuture}. To turn a {@link ListenableFuture} into a {@code + * ClosingFuture} call {@link #from(ListenableFuture)}. + *
+ * + *

The same warnings about doing heavyweight operations within {@link + * ClosingFuture#transformAsync(AsyncClosingFunction, Executor)} apply here. + */ + public ClosingFuture callAsync( + final AsyncClosingFunction5 function, Executor executor) { + return callAsync( + new AsyncCombiningCallable() { + @Override + public ClosingFuture call(DeferredCloser closer, Peeker peeker) throws Exception { + return function.apply( + closer, + peeker.getDone(future1), + peeker.getDone(future2), + peeker.getDone(future3), + peeker.getDone(future4), + peeker.getDone(future5)); + } + + @Override + public String toString() { + return function.toString(); + } + }, + executor); + } + } + + @Override + public String toString() { + // TODO(dpb): Better toString, in the style of Futures.transform etc. + return toStringHelper(this).add("state", state.get()).addValue(future).toString(); + } + + @Override + protected void finalize() { + if (state.get().equals(OPEN)) { + logger.log(SEVERE, "Uh oh! An open ClosingFuture has leaked and will close: {0}", this); + FluentFuture unused = finishToFuture(); + } + } + + private static void closeQuietly(final AutoCloseable closeable, Executor executor) { + if (closeable == null) { + return; + } + try { + executor.execute( + () -> { + try { + closeable.close(); + } catch (Exception e) { + restoreInterruptIfIsInterruptedException(e); + logger.log(WARNING, "thrown by close()", e); + } + }); + } catch (RejectedExecutionException e) { + if (logger.isLoggable(WARNING)) { + logger.log( + WARNING, String.format("while submitting close to %s; will close inline", executor), e); + } + closeQuietly(closeable, directExecutor()); + } + } + + private void checkAndUpdateState(State oldState, State newState) { + checkState( + compareAndUpdateState(oldState, newState), + "Expected state to be %s, but it was %s", + oldState, + newState); + } + + private boolean compareAndUpdateState(State oldState, State newState) { + return state.compareAndSet(oldState, newState); + } + + // TODO(dpb): Should we use a pair of ArrayLists instead of an IdentityHashMap? + private static final class CloseableList extends IdentityHashMap + implements Closeable { + private final DeferredCloser closer = new DeferredCloser(this); + private volatile boolean closed; + private volatile CountDownLatch whenClosed; + + ListenableFuture applyClosingFunction( + ClosingFunction transformation, @ParametricNullness V input) + throws Exception { + // TODO(dpb): Consider ways to defer closing without creating a separate CloseableList. + CloseableList newCloseables = new CloseableList(); + try { + return immediateFuture(transformation.apply(newCloseables.closer, input)); + } finally { + add(newCloseables, directExecutor()); + } + } + + FluentFuture applyAsyncClosingFunction( + AsyncClosingFunction transformation, @ParametricNullness V input) throws Exception { + // TODO(dpb): Consider ways to defer closing without creating a separate CloseableList. + CloseableList newCloseables = new CloseableList(); + try { + ClosingFuture closingFuture = transformation.apply(newCloseables.closer, input); + closingFuture.becomeSubsumedInto(newCloseables); + return closingFuture.future; + } finally { + add(newCloseables, directExecutor()); + } + } + + @Override + public void close() { + if (closed) { + return; + } + synchronized (this) { + if (closed) { + return; + } + closed = true; + } + for (Map.Entry entry : entrySet()) { + closeQuietly(entry.getKey(), entry.getValue()); + } + clear(); + if (whenClosed != null) { + whenClosed.countDown(); + } + } + + void add(AutoCloseable closeable, Executor executor) { + checkNotNull(executor); + if (closeable == null) { + return; + } + synchronized (this) { + if (!closed) { + put(closeable, executor); + return; + } + } + closeQuietly(closeable, executor); + } + + /** + * Returns a latch that reaches zero when this objects' deferred closeables have been closed. + */ + CountDownLatch whenClosedCountDown() { + if (closed) { + return new CountDownLatch(0); + } + synchronized (this) { + if (closed) { + return new CountDownLatch(0); + } + checkState(whenClosed == null); + return whenClosed = new CountDownLatch(1); + } + } + } + + /** + * Returns an object that can be used to wait until this objects' deferred closeables have all had + * {@link Runnable}s that close them submitted to each one's closing {@link Executor}. + */ + @VisibleForTesting + CountDownLatch whenClosedCountDown() { + return closeables.whenClosedCountDown(); + } + + /** The state of a {@link CloseableList}. */ + enum State { + /** The {@link CloseableList} has not been subsumed or closed. */ + OPEN, + + /** + * The {@link CloseableList} has been subsumed into another. It may not be closed or subsumed + * into any other. + */ + SUBSUMED, + + /** + * Some {@link ListenableFuture} has a callback attached that will close the {@link + * CloseableList}, but it has not yet run. The {@link CloseableList} may not be subsumed. + */ + WILL_CLOSE, + + /** + * The callback that closes the {@link CloseableList} is running, but it has not completed. The + * {@link CloseableList} may not be subsumed. + */ + CLOSING, + + /** The {@link CloseableList} has been closed. It may not be further subsumed. */ + CLOSED, + + /** + * {@link ClosingFuture#finishToValueAndCloser(ValueAndCloserConsumer, Executor)} has been + * called. The step may not be further subsumed, nor may {@link #finishToFuture()} be called. + */ + WILL_CREATE_VALUE_AND_CLOSER, + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CollectionFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CollectionFuture.java new file mode 100644 index 0000000000000..0faf69c3768ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CollectionFuture.java @@ -0,0 +1,113 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableCollection; +import org.apache.iotdb.google.common.collect.Lists; + +import java.util.Collections; +import java.util.List; + +import static java.util.Collections.unmodifiableList; +import static org.apache.iotdb.google.common.collect.Lists.newArrayListWithCapacity; + +/** Aggregate future that collects (stores) results of each future. */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +abstract class CollectionFuture extends AggregateFuture { + /* + * We access this field racily but safely. For discussion of a similar situation, see the comments + * on the fields of TimeoutFuture. This field is slightly different from the fields discussed + * there: cancel() never reads this field, only writes to it. That makes the race here completely + * harmless, rather than just 99.99% harmless. + */ + private List> values; + + CollectionFuture( + ImmutableCollection> futures, + boolean allMustSucceed) { + super(futures, allMustSucceed, true); + + List> values = + futures.isEmpty() + ? Collections.>emptyList() + : Lists.>newArrayListWithCapacity(futures.size()); + + // Populate the results list with null initially. + for (int i = 0; i < futures.size(); ++i) { + values.add(null); + } + + this.values = values; + } + + @Override + final void collectOneValue(int index, @ParametricNullness V returnValue) { + List> localValues = values; + if (localValues != null) { + localValues.set(index, new Present<>(returnValue)); + } + } + + @Override + final void handleAllCompleted() { + List> localValues = values; + if (localValues != null) { + set(combine(localValues)); + } + } + + @Override + void releaseResources(ReleaseResourcesReason reason) { + super.releaseResources(reason); + this.values = null; + } + + abstract C combine(List> values); + + /** Used for {@link Futures#allAsList} and {@link Futures#successfulAsList}. */ + static final class ListFuture extends CollectionFuture> { + ListFuture( + ImmutableCollection> futures, + boolean allMustSucceed) { + super(futures, allMustSucceed); + init(); + } + + @Override + public List combine(List> values) { + List result = newArrayListWithCapacity(values.size()); + for (Present element : values) { + result.add(element != null ? element.value : null); + } + return unmodifiableList(result); + } + } + + /** The result of a successful {@code Future}. */ + private static final class Present { + @ParametricNullness final V value; + + Present(@ParametricNullness V value) { + this.value = value; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CombinedFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CombinedFuture.java new file mode 100644 index 0000000000000..fad63789e6aa7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CombinedFuture.java @@ -0,0 +1,208 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableCollection; + +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.AggregateFuture.ReleaseResourcesReason.OUTPUT_FUTURE_DONE; + +/** Aggregate future that computes its value by calling a callable. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class CombinedFuture extends AggregateFuture { + private CombinedFutureInterruptibleTask task; + + CombinedFuture( + ImmutableCollection> futures, + boolean allMustSucceed, + Executor listenerExecutor, + AsyncCallable callable) { + super(futures, allMustSucceed, false); + this.task = new AsyncCallableInterruptibleTask(callable, listenerExecutor); + init(); + } + + CombinedFuture( + ImmutableCollection> futures, + boolean allMustSucceed, + Executor listenerExecutor, + Callable callable) { + super(futures, allMustSucceed, false); + this.task = new CallableInterruptibleTask(callable, listenerExecutor); + init(); + } + + @Override + void collectOneValue(int index, Object returnValue) {} + + @Override + void handleAllCompleted() { + CombinedFutureInterruptibleTask localTask = task; + if (localTask != null) { + localTask.execute(); + } + } + + @Override + void releaseResources(ReleaseResourcesReason reason) { + super.releaseResources(reason); + /* + * If the output future is done, then it won't need to interrupt the task later, so it can clear + * its reference to it. + * + * If the output future is *not* done, then the task field will be cleared after the task runs + * or after the output future is done, whichever comes first. + */ + if (reason == OUTPUT_FUTURE_DONE) { + this.task = null; + } + } + + @Override + protected void interruptTask() { + CombinedFutureInterruptibleTask localTask = task; + if (localTask != null) { + localTask.interruptTask(); + } + } + + private abstract class CombinedFutureInterruptibleTask + extends InterruptibleTask { + private final Executor listenerExecutor; + + CombinedFutureInterruptibleTask(Executor listenerExecutor) { + this.listenerExecutor = checkNotNull(listenerExecutor); + } + + @Override + final boolean isDone() { + return CombinedFuture.this.isDone(); + } + + final void execute() { + try { + listenerExecutor.execute(this); + } catch (RejectedExecutionException e) { + CombinedFuture.this.setException(e); + } + } + + @Override + final void afterRanInterruptiblySuccess(@ParametricNullness T result) { + /* + * The future no longer needs to interrupt this task, so it no longer needs a reference to it. + * + * TODO(cpovirk): It might be nice for our InterruptibleTask subclasses to null out their + * `callable` fields automatically. That would make it less important for us to null out the + * reference to `task` here (though it's still nice to do so in case our reference to the + * executor keeps it alive). Ideally, nulling out `callable` would be the responsibility of + * InterruptibleTask itself so that its other subclasses also benefit. (Handling `callable` in + * InterruptibleTask itself might also eliminate some of the existing boilerplate for, e.g., + * pendingToString().) + */ + CombinedFuture.this.task = null; + + setValue(result); + } + + @Override + final void afterRanInterruptiblyFailure(Throwable error) { + // See afterRanInterruptiblySuccess. + CombinedFuture.this.task = null; + + if (error instanceof ExecutionException) { + /* + * Cast to ExecutionException to satisfy our nullness checker, which (unsoundly but + * *usually* safely) assumes that getCause() returns non-null on an ExecutionException. + */ + CombinedFuture.this.setException(((ExecutionException) error).getCause()); + } else if (error instanceof CancellationException) { + cancel(false); + } else { + CombinedFuture.this.setException(error); + } + } + + abstract void setValue(@ParametricNullness T value); + } + + private final class AsyncCallableInterruptibleTask + extends CombinedFutureInterruptibleTask> { + private final AsyncCallable callable; + + AsyncCallableInterruptibleTask(AsyncCallable callable, Executor listenerExecutor) { + super(listenerExecutor); + this.callable = checkNotNull(callable); + } + + @Override + ListenableFuture runInterruptibly() throws Exception { + ListenableFuture result = callable.call(); + return checkNotNull( + result, + "AsyncCallable.call returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)? %s", + callable); + } + + @Override + void setValue(ListenableFuture value) { + CombinedFuture.this.setFuture(value); + } + + @Override + String toPendingString() { + return callable.toString(); + } + } + + private final class CallableInterruptibleTask extends CombinedFutureInterruptibleTask { + private final Callable callable; + + CallableInterruptibleTask(Callable callable, Executor listenerExecutor) { + super(listenerExecutor); + this.callable = checkNotNull(callable); + } + + @Override + @ParametricNullness + V runInterruptibly() throws Exception { + return callable.call(); + } + + @Override + void setValue(@ParametricNullness V value) { + CombinedFuture.this.set(value); + } + + @Override + String toPendingString() { + return callable.toString(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CycleDetectingLockFactory.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CycleDetectingLockFactory.java new file mode 100644 index 0000000000000..2a56039e9373a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/CycleDetectingLockFactory.java @@ -0,0 +1,982 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.MapMaker; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.Sets; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock} instances and {@link + * ReentrantReadWriteLock} instances that detect potential deadlock by checking for cycles in lock + * acquisition order. + * + *

Potential deadlocks detected when calling the {@code lock()}, {@code lockInterruptibly()}, or + * {@code tryLock()} methods will result in the execution of the {@link Policy} specified when + * creating the factory. The currently available policies are: + * + *

    + *
  • DISABLED + *
  • WARN + *
  • THROW + *
+ * + *

The locks created by a factory instance will detect lock acquisition cycles with locks created + * by other {@code CycleDetectingLockFactory} instances (except those with {@code Policy.DISABLED}). + * A lock's behavior when a cycle is detected, however, is defined by the {@code Policy} of the + * factory that created it. This allows detection of cycles across components while delegating + * control over lock behavior to individual components. + * + *

Applications are encouraged to use a {@code CycleDetectingLockFactory} to create any locks for + * which external/unmanaged code is executed while the lock is held. (See caveats under + * Performance). + * + *

Cycle Detection + * + *

Deadlocks can arise when locks are acquired in an order that forms a cycle. In a simple + * example involving two locks and two threads, deadlock occurs when one thread acquires Lock A, and + * then Lock B, while another thread acquires Lock B, and then Lock A: + * + *

+ * Thread1: acquire(LockA) --X acquire(LockB)
+ * Thread2: acquire(LockB) --X acquire(LockA)
+ * 
+ * + *

Neither thread will progress because each is waiting for the other. In more complex + * applications, cycles can arise from interactions among more than 2 locks: + * + *

+ * Thread1: acquire(LockA) --X acquire(LockB)
+ * Thread2: acquire(LockB) --X acquire(LockC)
+ * ...
+ * ThreadN: acquire(LockN) --X acquire(LockA)
+ * 
+ * + *

The implementation detects cycles by constructing a directed graph in which each lock + * represents a node and each edge represents an acquisition ordering between two locks. + * + *

    + *
  • Each lock adds (and removes) itself to/from a ThreadLocal Set of acquired locks when the + * Thread acquires its first hold (and releases its last remaining hold). + *
  • Before the lock is acquired, the lock is checked against the current set of acquired + * locks---to each of the acquired locks, an edge from the soon-to-be-acquired lock is either + * verified or created. + *
  • If a new edge needs to be created, the outgoing edges of the acquired locks are traversed + * to check for a cycle that reaches the lock to be acquired. If no cycle is detected, a new + * "safe" edge is created. + *
  • If a cycle is detected, an "unsafe" (cyclic) edge is created to represent a potential + * deadlock situation, and the appropriate Policy is executed. + *
+ * + *

Note that detection of potential deadlock does not necessarily indicate that deadlock will + * happen, as it is possible that higher level application logic prevents the cyclic lock + * acquisition from occurring. One example of a false positive is: + * + *

+ * LockA -> LockB -> LockC
+ * LockA -> LockC -> LockB
+ * 
+ * + *

ReadWriteLocks + * + *

While {@code ReadWriteLock} instances have different properties and can form cycles without + * potential deadlock, this class treats {@code ReadWriteLock} instances as equivalent to + * traditional exclusive locks. Although this increases the false positives that the locks detect + * (i.e. cycles that will not actually result in deadlock), it simplifies the algorithm and + * implementation considerably. The assumption is that a user of this factory wishes to eliminate + * any cyclic acquisition ordering. + * + *

Explicit Lock Acquisition Ordering + * + *

The {@link CycleDetectingLockFactory.WithExplicitOrdering} class can be used to enforce an + * application-specific ordering in addition to performing general cycle detection. + * + *

Garbage Collection + * + *

In order to allow proper garbage collection of unused locks, the edges of the lock graph are + * weak references. + * + *

Performance + * + *

The extra bookkeeping done by cycle detecting locks comes at some cost to performance. + * Benchmarks (as of December 2011) show that: + * + *

    + *
  • for an unnested {@code lock()} and {@code unlock()}, a cycle detecting lock takes 38ns as + * opposed to the 24ns taken by a plain lock. + *
  • for nested locking, the cost increases with the depth of the nesting: + *
      + *
    • 2 levels: average of 64ns per lock()/unlock() + *
    • 3 levels: average of 77ns per lock()/unlock() + *
    • 4 levels: average of 99ns per lock()/unlock() + *
    • 5 levels: average of 103ns per lock()/unlock() + *
    • 10 levels: average of 184ns per lock()/unlock() + *
    • 20 levels: average of 393ns per lock()/unlock() + *
    + *
+ * + *

As such, the CycleDetectingLockFactory may not be suitable for performance-critical + * applications which involve tightly-looped or deeply-nested locking algorithms. + * + * @author Darick Tong + * @since 13.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public class CycleDetectingLockFactory { + + /** + * Encapsulates the action to be taken when a potential deadlock is encountered. Clients can use + * one of the predefined {@link Policies} or specify a custom implementation. Implementations must + * be thread-safe. + * + * @since 13.0 + */ + public interface Policy { + + /** + * Called when a potential deadlock is encountered. Implementations can throw the given {@code + * exception} and/or execute other desired logic. + * + *

Note that the method will be called even upon an invocation of {@code tryLock()}. Although + * {@code tryLock()} technically recovers from deadlock by eventually timing out, this behavior + * is chosen based on the assumption that it is the application's wish to prohibit any cyclical + * lock acquisitions. + */ + void handlePotentialDeadlock(PotentialDeadlockException exception); + } + + /** + * Pre-defined {@link Policy} implementations. + * + * @since 13.0 + */ + public enum Policies implements Policy { + /** + * When potential deadlock is detected, this policy results in the throwing of the {@code + * PotentialDeadlockException} indicating the potential deadlock, which includes stack traces + * illustrating the cycle in lock acquisition order. + */ + THROW { + @Override + public void handlePotentialDeadlock(PotentialDeadlockException e) { + throw e; + } + }, + + /** + * When potential deadlock is detected, this policy results in the logging of a {@link + * Level#SEVERE} message indicating the potential deadlock, which includes stack traces + * illustrating the cycle in lock acquisition order. + */ + WARN { + @Override + public void handlePotentialDeadlock(PotentialDeadlockException e) { + logger.log(Level.SEVERE, "Detected potential deadlock", e); + } + }, + + /** + * Disables cycle detection. This option causes the factory to return unmodified lock + * implementations provided by the JDK, and is provided to allow applications to easily + * parameterize when cycle detection is enabled. + * + *

Note that locks created by a factory with this policy will not participate the + * cycle detection performed by locks created by other factories. + */ + DISABLED { + @Override + public void handlePotentialDeadlock(PotentialDeadlockException e) {} + }; + } + + /** Creates a new factory with the specified policy. */ + public static CycleDetectingLockFactory newInstance(Policy policy) { + return new CycleDetectingLockFactory(policy); + } + + /** Equivalent to {@code newReentrantLock(lockName, false)}. */ + public ReentrantLock newReentrantLock(String lockName) { + return newReentrantLock(lockName, false); + } + + /** + * Creates a {@link ReentrantLock} with the given fairness policy. The {@code lockName} is used in + * the warning or exception output to help identify the locks involved in the detected deadlock. + */ + public ReentrantLock newReentrantLock(String lockName, boolean fair) { + return policy == Policies.DISABLED + ? new ReentrantLock(fair) + : new CycleDetectingReentrantLock(new LockGraphNode(lockName), fair); + } + + /** Equivalent to {@code newReentrantReadWriteLock(lockName, false)}. */ + public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) { + return newReentrantReadWriteLock(lockName, false); + } + + /** + * Creates a {@link ReentrantReadWriteLock} with the given fairness policy. The {@code lockName} + * is used in the warning or exception output to help identify the locks involved in the detected + * deadlock. + */ + public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName, boolean fair) { + return policy == Policies.DISABLED + ? new ReentrantReadWriteLock(fair) + : new CycleDetectingReentrantReadWriteLock(new LockGraphNode(lockName), fair); + } + + // A static mapping from an Enum type to its set of LockGraphNodes. + private static final ConcurrentMap< + Class>, Map, LockGraphNode>> + lockGraphNodesPerType = new MapMaker().weakKeys().makeMap(); + + /** Creates a {@code CycleDetectingLockFactory.WithExplicitOrdering}. */ + public static > WithExplicitOrdering newInstanceWithExplicitOrdering( + Class enumClass, Policy policy) { + // createNodes maps each enumClass to a Map with the corresponding enum key + // type. + checkNotNull(enumClass); + checkNotNull(policy); + @SuppressWarnings("unchecked") + Map lockGraphNodes = (Map) getOrCreateNodes(enumClass); + return new WithExplicitOrdering<>(policy, lockGraphNodes); + } + + @SuppressWarnings("unchecked") + private static > Map getOrCreateNodes( + Class clazz) { + Map existing = (Map) lockGraphNodesPerType.get(clazz); + if (existing != null) { + return existing; + } + Map created = createNodes(clazz); + existing = (Map) lockGraphNodesPerType.putIfAbsent(clazz, created); + return MoreObjects.firstNonNull(existing, created); + } + + /** + * For a given Enum type, creates an immutable map from each of the Enum's values to a + * corresponding LockGraphNode, with the {@code allowedPriorLocks} and {@code + * disallowedPriorLocks} prepopulated with nodes according to the natural ordering of the + * associated Enum values. + */ + @VisibleForTesting + static > Map createNodes(Class clazz) { + EnumMap map = Maps.newEnumMap(clazz); + E[] keys = clazz.getEnumConstants(); + int numKeys = keys.length; + ArrayList nodes = Lists.newArrayListWithCapacity(numKeys); + // Create a LockGraphNode for each enum value. + for (E key : keys) { + LockGraphNode node = new LockGraphNode(getLockName(key)); + nodes.add(node); + map.put(key, node); + } + // Pre-populate all allowedPriorLocks with nodes of smaller ordinal. + for (int i = 1; i < numKeys; i++) { + nodes.get(i).checkAcquiredLocks(Policies.THROW, nodes.subList(0, i)); + } + // Pre-populate all disallowedPriorLocks with nodes of larger ordinal. + for (int i = 0; i < numKeys - 1; i++) { + nodes.get(i).checkAcquiredLocks(Policies.DISABLED, nodes.subList(i + 1, numKeys)); + } + return Collections.unmodifiableMap(map); + } + + /** + * For the given Enum value {@code rank}, returns the value's {@code "EnumClass.name"}, which is + * used in exception and warning output. + */ + private static String getLockName(Enum rank) { + return rank.getDeclaringClass().getSimpleName() + "." + rank.name(); + } + + /** + * A {@code CycleDetectingLockFactory.WithExplicitOrdering} provides the additional enforcement of + * an application-specified ordering of lock acquisitions. The application defines the allowed + * ordering with an {@code Enum} whose values each correspond to a lock type. The order in which + * the values are declared dictates the allowed order of lock acquisition. In other words, locks + * corresponding to smaller values of {@link Enum#ordinal()} should only be acquired before locks + * with larger ordinals. Example: + * + *

{@code
+   * enum MyLockOrder {
+   *   FIRST, SECOND, THIRD;
+   * }
+   *
+   * CycleDetectingLockFactory.WithExplicitOrdering factory =
+   *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(Policies.THROW);
+   *
+   * Lock lock1 = factory.newReentrantLock(MyLockOrder.FIRST);
+   * Lock lock2 = factory.newReentrantLock(MyLockOrder.SECOND);
+   * Lock lock3 = factory.newReentrantLock(MyLockOrder.THIRD);
+   *
+   * lock1.lock();
+   * lock3.lock();
+   * lock2.lock();  // will throw an IllegalStateException
+   * }
+ * + *

As with all locks created by instances of {@code CycleDetectingLockFactory} explicitly + * ordered locks participate in general cycle detection with all other cycle detecting locks, and + * a lock's behavior when detecting a cyclic lock acquisition is defined by the {@code Policy} of + * the factory that created it. + * + *

Note, however, that although multiple locks can be created for a given Enum value, whether + * it be through separate factory instances or through multiple calls to the same factory, + * attempting to acquire multiple locks with the same Enum value (within the same thread) will + * result in an IllegalStateException regardless of the factory's policy. For example: + * + *

{@code
+   * CycleDetectingLockFactory.WithExplicitOrdering factory1 =
+   *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
+   * CycleDetectingLockFactory.WithExplicitOrdering factory2 =
+   *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
+   *
+   * Lock lockA = factory1.newReentrantLock(MyLockOrder.FIRST);
+   * Lock lockB = factory1.newReentrantLock(MyLockOrder.FIRST);
+   * Lock lockC = factory2.newReentrantLock(MyLockOrder.FIRST);
+   *
+   * lockA.lock();
+   *
+   * lockB.lock();  // will throw an IllegalStateException
+   * lockC.lock();  // will throw an IllegalStateException
+   *
+   * lockA.lock();  // reentrant acquisition is okay
+   * }
+ * + *

It is the responsibility of the application to ensure that multiple lock instances with the + * same rank are never acquired in the same thread. + * + * @param The Enum type representing the explicit lock ordering. + * @since 13.0 + */ + public static final class WithExplicitOrdering> + extends CycleDetectingLockFactory { + + private final Map lockGraphNodes; + + @VisibleForTesting + WithExplicitOrdering(Policy policy, Map lockGraphNodes) { + super(policy); + this.lockGraphNodes = lockGraphNodes; + } + + /** Equivalent to {@code newReentrantLock(rank, false)}. */ + public ReentrantLock newReentrantLock(E rank) { + return newReentrantLock(rank, false); + } + + /** + * Creates a {@link ReentrantLock} with the given fairness policy and rank. The values returned + * by {@link Enum#getDeclaringClass()} and {@link Enum#name()} are used to describe the lock in + * warning or exception output. + * + * @throws IllegalStateException If the factory has already created a {@code Lock} with the + * specified rank. + */ + public ReentrantLock newReentrantLock(E rank, boolean fair) { + return policy == Policies.DISABLED + ? new ReentrantLock(fair) + // requireNonNull is safe because createNodes inserts an entry for every E. + // (If the caller passes `null` for the `rank` parameter, this will throw, but that's OK.) + : new CycleDetectingReentrantLock(requireNonNull(lockGraphNodes.get(rank)), fair); + } + + /** Equivalent to {@code newReentrantReadWriteLock(rank, false)}. */ + public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) { + return newReentrantReadWriteLock(rank, false); + } + + /** + * Creates a {@link ReentrantReadWriteLock} with the given fairness policy and rank. The values + * returned by {@link Enum#getDeclaringClass()} and {@link Enum#name()} are used to describe the + * lock in warning or exception output. + * + * @throws IllegalStateException If the factory has already created a {@code Lock} with the + * specified rank. + */ + public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) { + return policy == Policies.DISABLED + ? new ReentrantReadWriteLock(fair) + // requireNonNull is safe because createNodes inserts an entry for every E. + // (If the caller passes `null` for the `rank` parameter, this will throw, but that's OK.) + : new CycleDetectingReentrantReadWriteLock( + requireNonNull(lockGraphNodes.get(rank)), fair); + } + } + + //////// Implementation ///////// + + private static final Logger logger = Logger.getLogger(CycleDetectingLockFactory.class.getName()); + + final Policy policy; + + private CycleDetectingLockFactory(Policy policy) { + this.policy = checkNotNull(policy); + } + + /** + * Tracks the currently acquired locks for each Thread, kept up to date by calls to {@link + * #aboutToAcquire(CycleDetectingLock)} and {@link #lockStateChanged(CycleDetectingLock)}. + */ + // This is logically a Set, but an ArrayList is used to minimize the amount + // of allocation done on lock()/unlock(). + private static final ThreadLocal> acquiredLocks = + new ThreadLocal>() { + @Override + protected ArrayList initialValue() { + return Lists.newArrayListWithCapacity(3); + } + }; + + /** + * A Throwable used to record a stack trace that illustrates an example of a specific lock + * acquisition ordering. The top of the stack trace is truncated such that it starts with the + * acquisition of the lock in question, e.g. + * + *

+   * com...ExampleStackTrace: LockB -> LockC
+   *   at com...CycleDetectingReentrantLock.lock(CycleDetectingLockFactory.java:443)
+   *   at ...
+   *   at ...
+   *   at com...MyClass.someMethodThatAcquiresLockB(MyClass.java:123)
+   * 
+ */ + private static class ExampleStackTrace extends IllegalStateException { + + static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; + + static final ImmutableSet EXCLUDED_CLASS_NAMES = + ImmutableSet.of( + CycleDetectingLockFactory.class.getName(), + ExampleStackTrace.class.getName(), + LockGraphNode.class.getName()); + + ExampleStackTrace(LockGraphNode node1, LockGraphNode node2) { + super(node1.getLockName() + " -> " + node2.getLockName()); + StackTraceElement[] origStackTrace = getStackTrace(); + for (int i = 0, n = origStackTrace.length; i < n; i++) { + if (WithExplicitOrdering.class.getName().equals(origStackTrace[i].getClassName())) { + // For pre-populated disallowedPriorLocks edges, omit the stack trace. + setStackTrace(EMPTY_STACK_TRACE); + break; + } + if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) { + setStackTrace(Arrays.copyOfRange(origStackTrace, i, n)); + break; + } + } + } + } + + /** + * Represents a detected cycle in lock acquisition ordering. The exception includes a causal chain + * of {@code ExampleStackTrace} instances to illustrate the cycle, e.g. + * + *
+   * com....PotentialDeadlockException: Potential Deadlock from LockC -> ReadWriteA
+   *   at ...
+   *   at ...
+   * Caused by: com...ExampleStackTrace: LockB -> LockC
+   *   at ...
+   *   at ...
+   * Caused by: com...ExampleStackTrace: ReadWriteA -> LockB
+   *   at ...
+   *   at ...
+   * 
+ * + *

Instances are logged for the {@code Policies.WARN}, and thrown for {@code Policies.THROW}. + * + * @since 13.0 + */ + public static final class PotentialDeadlockException extends ExampleStackTrace { + + private final ExampleStackTrace conflictingStackTrace; + + private PotentialDeadlockException( + LockGraphNode node1, LockGraphNode node2, ExampleStackTrace conflictingStackTrace) { + super(node1, node2); + this.conflictingStackTrace = conflictingStackTrace; + initCause(conflictingStackTrace); + } + + public ExampleStackTrace getConflictingStackTrace() { + return conflictingStackTrace; + } + + /** + * Appends the chain of messages from the {@code conflictingStackTrace} to the original {@code + * message}. + */ + @Override + public String getMessage() { + // requireNonNull is safe because ExampleStackTrace sets a non-null message. + StringBuilder message = new StringBuilder(requireNonNull(super.getMessage())); + for (Throwable t = conflictingStackTrace; t != null; t = t.getCause()) { + message.append(", ").append(t.getMessage()); + } + return message.toString(); + } + } + + /** + * Internal Lock implementations implement the {@code CycleDetectingLock} interface, allowing the + * detection logic to treat all locks in the same manner. + */ + private interface CycleDetectingLock { + + /** + * @return the {@link LockGraphNode} associated with this lock. + */ + LockGraphNode getLockGraphNode(); + + /** + * @return {@code true} if the current thread has acquired this lock. + */ + boolean isAcquiredByCurrentThread(); + } + + /** + * A {@code LockGraphNode} associated with each lock instance keeps track of the directed edges in + * the lock acquisition graph. + */ + private static class LockGraphNode { + + /** + * The map tracking the locks that are known to be acquired before this lock, each associated + * with an example stack trace. Locks are weakly keyed to allow proper garbage collection when + * they are no longer referenced. + */ + final Map allowedPriorLocks = + new MapMaker().weakKeys().makeMap(); + + /** + * The map tracking lock nodes that can cause a lock acquisition cycle if acquired before this + * node. + */ + final Map disallowedPriorLocks = + new MapMaker().weakKeys().makeMap(); + + final String lockName; + + LockGraphNode(String lockName) { + this.lockName = Preconditions.checkNotNull(lockName); + } + + String getLockName() { + return lockName; + } + + void checkAcquiredLocks(Policy policy, List acquiredLocks) { + for (LockGraphNode acquiredLock : acquiredLocks) { + checkAcquiredLock(policy, acquiredLock); + } + } + + /** + * Checks the acquisition-ordering between {@code this}, which is about to be acquired, and the + * specified {@code acquiredLock}. + * + *

When this method returns, the {@code acquiredLock} should be in either the {@code + * preAcquireLocks} map, for the case in which it is safe to acquire {@code this} after the + * {@code acquiredLock}, or in the {@code disallowedPriorLocks} map, in which case it is not + * safe. + */ + void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) { + // checkAcquiredLock() should never be invoked by a lock that has already + // been acquired. For unordered locks, aboutToAcquire() ensures this by + // checking isAcquiredByCurrentThread(). For ordered locks, however, this + // can happen because multiple locks may share the same LockGraphNode. In + // this situation, throw an IllegalStateException as defined by contract + // described in the documentation of WithExplicitOrdering. + Preconditions.checkState( + this != acquiredLock, + "Attempted to acquire multiple locks with the same rank %s", + acquiredLock.getLockName()); + + if (allowedPriorLocks.containsKey(acquiredLock)) { + // The acquisition ordering from "acquiredLock" to "this" has already + // been verified as safe. In a properly written application, this is + // the common case. + return; + } + PotentialDeadlockException previousDeadlockException = disallowedPriorLocks.get(acquiredLock); + if (previousDeadlockException != null) { + // Previously determined to be an unsafe lock acquisition. + // Create a new PotentialDeadlockException with the same causal chain + // (the example cycle) as that of the cached exception. + PotentialDeadlockException exception = + new PotentialDeadlockException( + acquiredLock, this, previousDeadlockException.getConflictingStackTrace()); + policy.handlePotentialDeadlock(exception); + return; + } + // Otherwise, it's the first time seeing this lock relationship. Look for + // a path from the acquiredLock to this. + Set seen = Sets.newIdentityHashSet(); + ExampleStackTrace path = acquiredLock.findPathTo(this, seen); + + if (path == null) { + // this can be safely acquired after the acquiredLock. + // + // Note that there is a race condition here which can result in missing + // a cyclic edge: it's possible for two threads to simultaneous find + // "safe" edges which together form a cycle. Preventing this race + // condition efficiently without _introducing_ deadlock is probably + // tricky. For now, just accept the race condition---missing a warning + // now and then is still better than having no deadlock detection. + allowedPriorLocks.put(acquiredLock, new ExampleStackTrace(acquiredLock, this)); + } else { + // Unsafe acquisition order detected. Create and cache a + // PotentialDeadlockException. + PotentialDeadlockException exception = + new PotentialDeadlockException(acquiredLock, this, path); + disallowedPriorLocks.put(acquiredLock, exception); + policy.handlePotentialDeadlock(exception); + } + } + + /** + * Performs a depth-first traversal of the graph edges defined by each node's {@code + * allowedPriorLocks} to find a path between {@code this} and the specified {@code lock}. + * + * @return If a path was found, a chained {@link ExampleStackTrace} illustrating the path to the + * {@code lock}, or {@code null} if no path was found. + */ + private ExampleStackTrace findPathTo(LockGraphNode node, Set seen) { + if (!seen.add(this)) { + return null; // Already traversed this node. + } + ExampleStackTrace found = allowedPriorLocks.get(node); + if (found != null) { + return found; // Found a path ending at the node! + } + // Recurse the edges. + for (Entry entry : allowedPriorLocks.entrySet()) { + LockGraphNode preAcquiredLock = entry.getKey(); + found = preAcquiredLock.findPathTo(node, seen); + if (found != null) { + // One of this node's allowedPriorLocks found a path. Prepend an + // ExampleStackTrace(preAcquiredLock, this) to the returned chain of + // ExampleStackTraces. + ExampleStackTrace path = new ExampleStackTrace(preAcquiredLock, this); + path.setStackTrace(entry.getValue().getStackTrace()); + path.initCause(found); + return path; + } + } + return null; + } + } + + /** + * CycleDetectingLock implementations must call this method before attempting to acquire the lock. + */ + private void aboutToAcquire(CycleDetectingLock lock) { + if (!lock.isAcquiredByCurrentThread()) { + ArrayList acquiredLockList = acquiredLocks.get(); + LockGraphNode node = lock.getLockGraphNode(); + node.checkAcquiredLocks(policy, acquiredLockList); + acquiredLockList.add(node); + } + } + + /** + * CycleDetectingLock implementations must call this method in a {@code finally} clause after any + * attempt to change the lock state, including both lock and unlock attempts. Failure to do so can + * result in corrupting the acquireLocks set. + */ + private static void lockStateChanged(CycleDetectingLock lock) { + if (!lock.isAcquiredByCurrentThread()) { + ArrayList acquiredLockList = acquiredLocks.get(); + LockGraphNode node = lock.getLockGraphNode(); + // Iterate in reverse because locks are usually locked/unlocked in a + // LIFO order. + for (int i = acquiredLockList.size() - 1; i >= 0; i--) { + if (acquiredLockList.get(i) == node) { + acquiredLockList.remove(i); + break; + } + } + } + } + + final class CycleDetectingReentrantLock extends ReentrantLock implements CycleDetectingLock { + + private final LockGraphNode lockGraphNode; + + private CycleDetectingReentrantLock(LockGraphNode lockGraphNode, boolean fair) { + super(fair); + this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode); + } + + ///// CycleDetectingLock methods. ///// + + @Override + public LockGraphNode getLockGraphNode() { + return lockGraphNode; + } + + @Override + public boolean isAcquiredByCurrentThread() { + return isHeldByCurrentThread(); + } + + ///// Overridden ReentrantLock methods. ///// + + @Override + public void lock() { + aboutToAcquire(this); + try { + super.lock(); + } finally { + lockStateChanged(this); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + aboutToAcquire(this); + try { + super.lockInterruptibly(); + } finally { + lockStateChanged(this); + } + } + + @Override + public boolean tryLock() { + aboutToAcquire(this); + try { + return super.tryLock(); + } finally { + lockStateChanged(this); + } + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + aboutToAcquire(this); + try { + return super.tryLock(timeout, unit); + } finally { + lockStateChanged(this); + } + } + + @Override + public void unlock() { + try { + super.unlock(); + } finally { + lockStateChanged(this); + } + } + } + + final class CycleDetectingReentrantReadWriteLock extends ReentrantReadWriteLock + implements CycleDetectingLock { + + // These ReadLock/WriteLock implementations shadow those in the + // ReentrantReadWriteLock superclass. They are simply wrappers around the + // internal Sync object, so this is safe since the shadowed locks are never + // exposed or used. + private final CycleDetectingReentrantReadLock readLock; + private final CycleDetectingReentrantWriteLock writeLock; + + private final LockGraphNode lockGraphNode; + + private CycleDetectingReentrantReadWriteLock(LockGraphNode lockGraphNode, boolean fair) { + super(fair); + this.readLock = new CycleDetectingReentrantReadLock(this); + this.writeLock = new CycleDetectingReentrantWriteLock(this); + this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode); + } + + ///// Overridden ReentrantReadWriteLock methods. ///// + + @Override + public ReadLock readLock() { + return readLock; + } + + @Override + public WriteLock writeLock() { + return writeLock; + } + + ///// CycleDetectingLock methods. ///// + + @Override + public LockGraphNode getLockGraphNode() { + return lockGraphNode; + } + + @Override + public boolean isAcquiredByCurrentThread() { + return isWriteLockedByCurrentThread() || getReadHoldCount() > 0; + } + } + + private class CycleDetectingReentrantReadLock extends ReentrantReadWriteLock.ReadLock { + + final CycleDetectingReentrantReadWriteLock readWriteLock; + + CycleDetectingReentrantReadLock(CycleDetectingReentrantReadWriteLock readWriteLock) { + super(readWriteLock); + this.readWriteLock = readWriteLock; + } + + @Override + public void lock() { + aboutToAcquire(readWriteLock); + try { + super.lock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + super.lockInterruptibly(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock() { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(timeout, unit); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void unlock() { + try { + super.unlock(); + } finally { + lockStateChanged(readWriteLock); + } + } + } + + private class CycleDetectingReentrantWriteLock extends ReentrantReadWriteLock.WriteLock { + + final CycleDetectingReentrantReadWriteLock readWriteLock; + + CycleDetectingReentrantWriteLock(CycleDetectingReentrantReadWriteLock readWriteLock) { + super(readWriteLock); + this.readWriteLock = readWriteLock; + } + + @Override + public void lock() { + aboutToAcquire(readWriteLock); + try { + super.lock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + super.lockInterruptibly(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock() { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + aboutToAcquire(readWriteLock); + try { + return super.tryLock(timeout, unit); + } finally { + lockStateChanged(readWriteLock); + } + } + + @Override + public void unlock() { + try { + super.unlock(); + } finally { + lockStateChanged(readWriteLock); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/DirectExecutor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/DirectExecutor.java new file mode 100644 index 0000000000000..3d75b7094af23 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/DirectExecutor.java @@ -0,0 +1,44 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.Executor; + +/** + * An {@link Executor} that runs each task in the thread that invokes {@link Executor#execute + * execute}. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +enum DirectExecutor implements Executor { + INSTANCE; + + @Override + public void execute(Runnable command) { + command.run(); + } + + @Override + public String toString() { + return "MoreExecutors.directExecutor()"; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..42e8c816da9f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionError.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionError.java new file mode 100644 index 0000000000000..01717295ed1d8 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionError.java @@ -0,0 +1,62 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * {@link Error} variant of {@link java.util.concurrent.ExecutionException}. As with {@code + * ExecutionException}, the error's {@linkplain #getCause() cause} comes from a failed task, + * possibly run in another thread. That cause should itself be an {@code Error}; if not, use {@code + * ExecutionException} or {@link UncheckedExecutionException}. This allows the client code to + * continue to distinguish between exceptions and errors, even when they come from other threads. + * + * @author Chris Povirk + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public class ExecutionError extends Error { + /* + * Ideally, this class would have exposed only constructors that require a non-null cause. We + * might try to move in that direction, but there are complications. See + * https://github.com/jspecify/jspecify-reference-checker/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecTransfer.java#L789 + */ + + /** Creates a new instance with {@code null} as its detail message. */ + protected ExecutionError() {} + + /** Creates a new instance with the given detail message. */ + protected ExecutionError(String message) { + super(message); + } + + /** Creates a new instance with the given detail message and cause. */ + public ExecutionError(String message, Error cause) { + super(message, cause); + } + + /** Creates a new instance with the given cause. */ + public ExecutionError(Error cause) { + super(cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionList.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionList.java new file mode 100644 index 0000000000000..d37c507a07796 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionList.java @@ -0,0 +1,169 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A support class for {@code ListenableFuture} implementations to manage their listeners. An + * instance contains a list of listeners, each with an associated {@code Executor}, and guarantees + * that every {@code Runnable} that is {@linkplain #add added} will be executed after {@link + * #execute()} is called. Any {@code Runnable} added after the call to {@code execute} is still + * guaranteed to execute. There is no guarantee, however, that listeners will be executed in the + * order that they are added. + * + *

Exceptions thrown by a listener will be propagated up to the executor. Any exception thrown + * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an exception + * thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught and logged. + * + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class ExecutionList { + /** Logger to log exceptions caught when running runnables. */ + private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + + /** + * The runnable, executor pairs to execute. This acts as a stack threaded through the {@link + * RunnableExecutorPair#next} field. + */ + private RunnableExecutorPair runnables; + + private boolean executed; + + /** Creates a new, empty {@link ExecutionList}. */ + public ExecutionList() {} + + /** + * Adds the {@code Runnable} and accompanying {@code Executor} to the list of listeners to + * execute. If execution has already begun, the listener is executed immediately. + * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} + * documentation. + */ + public void add(Runnable runnable, Executor executor) { + // Fail fast on a null. We throw NPE here because the contract of Executor states that it throws + // NPE on null listener, so we propagate that contract up into the add method as well. + checkNotNull(runnable, "Runnable was null."); + checkNotNull(executor, "Executor was null."); + + // Lock while we check state. We must maintain the lock while adding the new pair so that + // another thread can't run the list out from under us. We only add to the list if we have not + // yet started execution. + synchronized (this) { + if (!executed) { + runnables = new RunnableExecutorPair(runnable, executor, runnables); + return; + } + } + // Execute the runnable immediately. Because of scheduling this may end up getting called before + // some of the previously added runnables, but we're OK with that. If we want to change the + // contract to guarantee ordering among runnables we'd have to modify the logic here to allow + // it. + executeListener(runnable, executor); + } + + /** + * Runs this execution list, executing all existing pairs in the order they were added. However, + * note that listeners added after this point may be executed before those previously added, and + * note that the execution order of all listeners is ultimately chosen by the implementations of + * the supplied executors. + * + *

This method is idempotent. Calling it several times in parallel is semantically equivalent + * to calling it exactly once. + * + * @since 10.0 (present in 1.0 as {@code run}) + */ + public void execute() { + // Lock while we update our state so the add method above will finish adding any listeners + // before we start to run them. + RunnableExecutorPair list; + synchronized (this) { + if (executed) { + return; + } + executed = true; + list = runnables; + runnables = null; // allow GC to free listeners even if this stays around for a while. + } + // If we succeeded then list holds all the runnables we to execute. The pairs in the stack are + // in the opposite order from how they were added so we need to reverse the list to fulfill our + // contract. + // This is somewhat annoying, but turns out to be very fast in practice. Alternatively, we could + // drop the contract on the method that enforces this queue like behavior since depending on it + // is likely to be a bug anyway. + + // N.B. All writes to the list and the next pointers must have happened before the above + // synchronized block, so we can iterate the list without the lock held here. + RunnableExecutorPair reversedList = null; + while (list != null) { + RunnableExecutorPair tmp = list; + list = list.next; + tmp.next = reversedList; + reversedList = tmp; + } + while (reversedList != null) { + executeListener(reversedList.runnable, reversedList.executor); + reversedList = reversedList.next; + } + } + + /** + * Submits the given runnable to the given {@link Executor} catching and logging all {@linkplain + * RuntimeException runtime exceptions} thrown by the executor. + */ + private static void executeListener(Runnable runnable, Executor executor) { + try { + executor.execute(runnable); + } catch (RuntimeException e) { + // Log it and keep going -- bad runnable and/or executor. Don't punish the other runnables if + // we're given a bad one. We only catch RuntimeException because we want Errors to propagate + // up. + log.log( + Level.SEVERE, + "RuntimeException while executing runnable " + runnable + " with executor " + executor, + e); + } + } + + private static final class RunnableExecutorPair { + final Runnable runnable; + final Executor executor; + RunnableExecutorPair next; + + RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) { + this.runnable = runnable; + this.executor = executor; + this.next = next; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionSequencer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionSequencer.java new file mode 100644 index 0000000000000..4c4d8e9a482cd --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ExecutionSequencer.java @@ -0,0 +1,456 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.util.concurrent.ExecutionSequencer.RunningState.CANCELLED; +import static org.apache.iotdb.google.common.util.concurrent.ExecutionSequencer.RunningState.NOT_RUN; +import static org.apache.iotdb.google.common.util.concurrent.ExecutionSequencer.RunningState.STARTED; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateCancelledFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateFuture; +import static org.apache.iotdb.google.common.util.concurrent.Futures.immediateVoidFuture; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; + +/** + * Serializes execution of tasks, somewhat like an "asynchronous {@code synchronized} block." Each + * {@linkplain #submit enqueued} callable will not be submitted to its associated executor until the + * previous callable has returned -- and, if the previous callable was an {@link AsyncCallable}, not + * until the {@code Future} it returned is {@linkplain Future#isDone done} (successful, failed, or + * cancelled). + * + *

This class serializes execution of submitted tasks but not any listeners of + * those tasks. + * + *

Submitted tasks have a happens-before order as defined in the Java Language Specification. + * Tasks execute with the same happens-before order that the function calls to {@link #submit} and + * {@link #submitAsync} that submitted those tasks had. + * + *

This class has limited support for cancellation and other "early completions": + * + *

    + *
  • While calls to {@code submit} and {@code submitAsync} return a {@code Future} that can be + * cancelled, cancellation never propagates to a task that has started to run -- neither to + * the callable itself nor to any {@code Future} returned by an {@code AsyncCallable}. + * (However, cancellation can prevent an unstarted task from running.) Therefore, the + * next task will wait for any running callable (or pending {@code Future} returned by an + * {@code AsyncCallable}) to complete, without interrupting it (and without calling {@code + * cancel} on the {@code Future}). So beware: Even if you cancel every preceding {@code + * Future} returned by this class, the next task may still have to wait.. + *
  • Once an {@code AsyncCallable} returns a {@code Future}, this class considers that task to + * be "done" as soon as that {@code Future} completes in any way. Notably, a {@code + * Future} is "completed" even if it is cancelled while its underlying work continues on a + * thread, an RPC, etc. The {@code Future} is also "completed" if it fails "early" -- for + * example, if the deadline expires on a {@code Future} returned from {@link + * Futures#withTimeout} while the {@code Future} it wraps continues its underlying work. So + * beware: Your {@code AsyncCallable} should not complete its {@code Future} until it is + * safe for the next task to start. + *
+ * + *

This class is similar to {@link MoreExecutors#newSequentialExecutor}. This class is different + * in a few ways: + * + *

    + *
  • Each task may be associated with a different executor. + *
  • Tasks may be of type {@code AsyncCallable}. + *
  • Running tasks cannot be interrupted. (Note that {@code newSequentialExecutor} does + * not return {@code Future} objects, so it doesn't support interruption directly, either. + * However, utilities that use that executor have the ability to interrupt tasks + * running on it. This class, by contrast, does not expose an {@code Executor} API.) + *
+ * + *

If you don't need the features of this class, you may prefer {@code newSequentialExecutor} for + * its simplicity and ability to accommodate interruption. + * + * @since 26.0 + */ +@ElementTypesAreNonnullByDefault +@J2ktIncompatible +public final class ExecutionSequencer { + + private ExecutionSequencer() {} + + /** Creates a new instance. */ + public static ExecutionSequencer create() { + return new ExecutionSequencer(); + } + + /** This reference acts as a pointer tracking the head of a linked list of ListenableFutures. */ + private final AtomicReference> ref = + new AtomicReference<>(immediateVoidFuture()); + + private ThreadConfinedTaskQueue latestTaskQueue = new ThreadConfinedTaskQueue(); + + /** + * This object is unsafely published, but avoids problematic races by relying exclusively on the + * identity equality of its Thread field so that the task field is only accessed by a single + * thread. + */ + private static final class ThreadConfinedTaskQueue { + /** + * This field is only used for identity comparisons with the current thread. Field assignments + * are atomic, but do not provide happens-before ordering; however: + * + *

    + *
  • If this field's value == currentThread, we know that it's up to date, because write + * operations in a thread always happen-before subsequent read operations in the same + * thread + *
  • If this field's value == null because of unsafe publication, we know that it isn't the + * object associated with our thread, because if it was the publication wouldn't have been + * unsafe and we'd have seen our thread as the value. This state is also why a new + * ThreadConfinedTaskQueue object must be created for each inline execution, because + * observing a null thread does not mean the object is safe to reuse. + *
  • If this field's value is some other thread object, we know that it's not our thread. + *
  • If this field's value == null because it originally belonged to another thread and that + * thread cleared it, we still know that it's not associated with our thread + *
  • If this field's value == null because it was associated with our thread and was + * cleared, we know that we're not executing inline any more + *
+ * + * All the states where thread != currentThread are identical for our purposes, and so even + * though it's racy, we don't care which of those values we get, so no need to synchronize. + */ + Thread thread; + + /** Only used by the thread associated with this object */ + Runnable nextTask; + + /** Only used by the thread associated with this object */ + Executor nextExecutor; + } + + /** + * Enqueues a task to run when the previous task (if any) completes. + * + *

Cancellation does not propagate from the output future to a callable that has begun to + * execute, but if the output future is cancelled before {@link Callable#call()} is invoked, + * {@link Callable#call()} will not be invoked. + */ + public ListenableFuture submit(Callable callable, Executor executor) { + checkNotNull(callable); + checkNotNull(executor); + return submitAsync( + new AsyncCallable() { + @Override + public ListenableFuture call() throws Exception { + return immediateFuture(callable.call()); + } + + @Override + public String toString() { + return callable.toString(); + } + }, + executor); + } + + /** + * Enqueues a task to run when the previous task (if any) completes. + * + *

Cancellation does not propagate from the output future to the future returned from {@code + * callable} or a callable that has begun to execute, but if the output future is cancelled before + * {@link AsyncCallable#call()} is invoked, {@link AsyncCallable#call()} will not be invoked. + */ + public ListenableFuture submitAsync( + AsyncCallable callable, Executor executor) { + checkNotNull(callable); + checkNotNull(executor); + TaskNonReentrantExecutor taskExecutor = new TaskNonReentrantExecutor(executor, this); + AsyncCallable task = + new AsyncCallable() { + @Override + public ListenableFuture call() throws Exception { + if (!taskExecutor.trySetStarted()) { + return immediateCancelledFuture(); + } + return callable.call(); + } + + @Override + public String toString() { + return callable.toString(); + } + }; + /* + * Four futures are at play here: + * taskFuture is the future tracking the result of the callable. + * newFuture is a future that completes after this and all prior tasks are done. + * oldFuture is the previous task's newFuture. + * outputFuture is the future we return to the caller, a nonCancellationPropagating taskFuture. + * + * newFuture is guaranteed to only complete once all tasks previously submitted to this instance + * have completed - namely after oldFuture is done, and taskFuture has either completed or been + * cancelled before the callable started execution. + */ + SettableFuture newFuture = SettableFuture.create(); + + ListenableFuture oldFuture = ref.getAndSet(newFuture); + + // Invoke our task once the previous future completes. + TrustedListenableFutureTask taskFuture = TrustedListenableFutureTask.create(task); + oldFuture.addListener(taskFuture, taskExecutor); + + ListenableFuture outputFuture = Futures.nonCancellationPropagating(taskFuture); + + // newFuture's lifetime is determined by taskFuture, which can't complete before oldFuture + // unless taskFuture is cancelled, in which case it falls back to oldFuture. This ensures that + // if the future we return is cancelled, we don't begin execution of the next task until after + // oldFuture completes. + Runnable listener = + () -> { + if (taskFuture.isDone()) { + // Since the value of oldFuture can only ever be immediateFuture(null) or setFuture of + // a future that eventually came from immediateFuture(null), this doesn't leak + // throwables or completion values. + newFuture.setFuture(oldFuture); + } else if (outputFuture.isCancelled() && taskExecutor.trySetCancelled()) { + // If this CAS succeeds, we know that the provided callable will never be invoked, + // so when oldFuture completes it is safe to allow the next submitted task to + // proceed. Doing this immediately here lets the next task run without waiting for + // the cancelled task's executor to run the noop AsyncCallable. + // + // --- + // + // If the CAS fails, the provided callable already started running (or it is about + // to). Our contract promises: + // + // 1. not to execute a new callable until the old one has returned + // + // If we were to cancel taskFuture, that would let the next task start while the old + // one is still running. + // + // Now, maybe we could tweak our implementation to not start the next task until the + // callable actually completes. (We could detect completion in our wrapper + // `AsyncCallable task`.) However, our contract also promises: + // + // 2. not to cancel any Future the user returned from an AsyncCallable + // + // We promise this because, once we cancel that Future, we would no longer be able to + // tell when any underlying work it is doing is done. Thus, we might start a new task + // while that underlying work is still running. + // + // So that is why we cancel only in the case of CAS success. + taskFuture.cancel(false); + } + }; + // Adding the listener to both futures guarantees that newFuture will always be set. Adding to + // taskFuture guarantees completion if the callable is invoked, and adding to outputFuture + // propagates cancellation if the callable has not yet been invoked. + outputFuture.addListener(listener, directExecutor()); + taskFuture.addListener(listener, directExecutor()); + + return outputFuture; + } + + enum RunningState { + NOT_RUN, + CANCELLED, + STARTED, + } + + /** + * This class helps avoid a StackOverflowError when large numbers of tasks are submitted with + * {@link MoreExecutors#directExecutor}. Normally, when the first future completes, all the other + * tasks would be called recursively. Here, we detect that the delegate executor is executing + * inline, and maintain a queue to dispatch tasks iteratively. There is one instance of this class + * per call to submit() or submitAsync(), and each instance supports only one call to execute(). + * + *

This class would certainly be simpler and easier to reason about if it were built with + * ThreadLocal; however, ThreadLocal is not well optimized for the case where the ThreadLocal is + * non-static, and is initialized/removed frequently - this causes churn in the Thread specific + * hashmaps. Using a static ThreadLocal to avoid that overhead would mean that different + * ExecutionSequencer objects interfere with each other, which would be undesirable, in addition + * to increasing the memory footprint of every thread that interacted with it. In order to release + * entries in thread-specific maps when the ThreadLocal object itself is no longer referenced, + * ThreadLocal is usually implemented with a WeakReference, which can have negative performance + * properties; for example, calling WeakReference.get() on Android will block during an + * otherwise-concurrent GC cycle. + */ + private static final class TaskNonReentrantExecutor extends AtomicReference + implements Executor, Runnable { + + /** + * Used to update and read the latestTaskQueue field. Set to null once the runnable has been run + * or queued. + */ + ExecutionSequencer sequencer; + + /** + * Executor the task was set to run on. Set to null when the task has been queued, run, or + * cancelled. + */ + Executor delegate; + + /** + * Set before calling delegate.execute(); set to null once run, so that it can be GCed; this + * object may live on after, if submitAsync returns an incomplete future. + */ + Runnable task; + + /** Thread that called execute(). Set in execute, cleared when delegate.execute() returns. */ + Thread submitting; + + private TaskNonReentrantExecutor(Executor delegate, ExecutionSequencer sequencer) { + super(NOT_RUN); + this.delegate = delegate; + this.sequencer = sequencer; + } + + @Override + public void execute(Runnable task) { + // If this operation was successfully cancelled already, calling the runnable will be a noop. + // This also avoids a race where if outputFuture is cancelled, it will call taskFuture.cancel, + // which will call newFuture.setFuture(oldFuture), to allow the next task in the queue to run + // without waiting for the user's executor to run our submitted Runnable. However, this can + // interact poorly with the reentrancy-avoiding behavior of this executor - when the operation + // before the cancelled future completes, it will synchronously complete both the newFuture + // from the cancelled operation and its own. This can cause one runnable to queue two tasks, + // breaking the invariant this method relies on to iteratively run the next task after the + // previous one completes. + if (get() == RunningState.CANCELLED) { + delegate = null; + sequencer = null; + return; + } + submitting = Thread.currentThread(); + + try { + /* + * requireNonNull is safe because we don't null out `sequencer` except: + * + * - above, where we return (in which case we never get here) + * + * - in `run`, which can't run until this Runnable is submitted to an executor, which + * doesn't happen until below. (And this Executor -- yes, the object is both a Runnable + * and an Executor -- is used for only a single `execute` call.) + */ + ThreadConfinedTaskQueue submittingTaskQueue = requireNonNull(sequencer).latestTaskQueue; + if (submittingTaskQueue.thread == submitting) { + sequencer = null; + // Submit from inside a reentrant submit. We don't know if this one will be reentrant (and + // can't know without submitting something to the executor) so queue to run iteratively. + // Task must be null, since each execution on this executor can only produce one more + // execution. + checkState(submittingTaskQueue.nextTask == null); + submittingTaskQueue.nextTask = task; + // requireNonNull(delegate) is safe for reasons similar to requireNonNull(sequencer). + submittingTaskQueue.nextExecutor = requireNonNull(delegate); + delegate = null; + } else { + // requireNonNull(delegate) is safe for reasons similar to requireNonNull(sequencer). + Executor localDelegate = requireNonNull(delegate); + delegate = null; + this.task = task; + localDelegate.execute(this); + } + } finally { + // Important to null this out here - if we did *not* execute inline, we might still + // run() on the same thread that called execute() - such as in a thread pool, and think + // that it was happening inline. As a side benefit, avoids holding on to the Thread object + // longer than necessary. + submitting = null; + } + } + + @SuppressWarnings("ShortCircuitBoolean") + @Override + public void run() { + Thread currentThread = Thread.currentThread(); + if (currentThread != submitting) { + /* + * requireNonNull is safe because we set `task` before submitting this Runnable to an + * Executor, and we don't null it out until here. + */ + Runnable localTask = requireNonNull(task); + task = null; + localTask.run(); + return; + } + // Executor called reentrantly! Make sure that further calls don't overflow stack. Further + // reentrant calls will see that their current thread is the same as the one set in + // latestTaskQueue, and queue rather than calling execute() directly. + ThreadConfinedTaskQueue executingTaskQueue = new ThreadConfinedTaskQueue(); + executingTaskQueue.thread = currentThread; + /* + * requireNonNull is safe because we don't null out `sequencer` except: + * + * - after the requireNonNull call below. (And this object has its Runnable.run override + * called only once, just as it has its Executor.execute override called only once.) + * + * - if we return immediately from `execute` (in which case we never get here) + * + * - in the "reentrant submit" case of `execute` (in which case we must have started running a + * user task -- which means that we already got past this code (or else we exited early + * above)) + */ + // Unconditionally set; there is no risk of throwing away a queued task from another thread, + // because in order for the current task to run on this executor the previous task must have + // already started execution. Because each task on a TaskNonReentrantExecutor can only produce + // one execute() call to another instance from the same ExecutionSequencer, we know by + // induction that the task that launched this one must not have added any other runnables to + // that thread's queue, and thus we cannot be replacing a TaskAndThread object that would + // otherwise have another task queued on to it. Note the exception to this, cancellation, is + // specially handled in execute() - execute() calls triggered by cancellation are no-ops, and + // thus don't count. + requireNonNull(sequencer).latestTaskQueue = executingTaskQueue; + sequencer = null; + try { + // requireNonNull is safe, as discussed above. + Runnable localTask = requireNonNull(task); + task = null; + localTask.run(); + // Now check if our task attempted to reentrantly execute the next task. + Runnable queuedTask; + Executor queuedExecutor; + // Intentionally using non-short-circuit operator + while ((queuedTask = executingTaskQueue.nextTask) != null + && (queuedExecutor = executingTaskQueue.nextExecutor) != null) { + executingTaskQueue.nextTask = null; + executingTaskQueue.nextExecutor = null; + queuedExecutor.execute(queuedTask); + } + } finally { + // Null out the thread field, so that we don't leak a reference to Thread, and so that + // future `thread == currentThread()` calls from this thread don't incorrectly queue instead + // of executing. Don't null out the latestTaskQueue field, because the work done here + // may have scheduled more operations on another thread, and if those operations then + // trigger reentrant calls that thread will have updated the latestTaskQueue field, and + // we'd be interfering with their operation. + executingTaskQueue.thread = null; + } + } + + private boolean trySetStarted() { + return compareAndSet(NOT_RUN, STARTED); + } + + private boolean trySetCancelled() { + return compareAndSet(NOT_RUN, CANCELLED); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FakeTimeLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FakeTimeLimiter.java new file mode 100644 index 0000000000000..f5307b29620b7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FakeTimeLimiter.java @@ -0,0 +1,101 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** + * A TimeLimiter implementation which actually does not attempt to limit time at all. This may be + * desirable to use in some unit tests. More importantly, attempting to debug a call which is + * time-limited would be extremely annoying, so this gives you a time-limiter you can easily swap in + * for your real time-limiter while you're debugging. + * + * @author Kevin Bourrillion + * @author Jens Nyman + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class FakeTimeLimiter implements TimeLimiter { + // TODO(kak): consider removing this + @Override + public T newProxy( + T target, Class interfaceType, long timeoutDuration, TimeUnit timeoutUnit) { + checkNotNull(target); + checkNotNull(interfaceType); + checkNotNull(timeoutUnit); + return target; // ha ha + } + + // TODO(kak): consider removing this + @Override + @ParametricNullness + public T callWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit) throws ExecutionException { + checkNotNull(callable); + checkNotNull(timeoutUnit); + try { + return callable.call(); + } catch (RuntimeException e) { + throw new UncheckedExecutionException(e); + } catch (Exception e) { + restoreInterruptIfIsInterruptedException(e); + throw new ExecutionException(e); + } catch (Error e) { + throw new ExecutionError(e); + } + } + + // TODO(kak): consider removing this + @Override + @ParametricNullness + public T callUninterruptiblyWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit) throws ExecutionException { + return callWithTimeout(callable, timeoutDuration, timeoutUnit); + } + + @Override + public void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) { + checkNotNull(runnable); + checkNotNull(timeoutUnit); + try { + runnable.run(); + } catch (RuntimeException e) { + throw new UncheckedExecutionException(e); + } catch (Error e) { + throw new ExecutionError(e); + } + } + + @Override + public void runUninterruptiblyWithTimeout( + Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) { + runWithTimeout(runnable, timeoutDuration, timeoutUnit); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FluentFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FluentFuture.java new file mode 100644 index 0000000000000..592a8390c034c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FluentFuture.java @@ -0,0 +1,414 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Function; + +import java.time.Duration; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * A {@link ListenableFuture} that supports fluent chains of operations. For example: + * + *

{@code
+ * ListenableFuture adminIsLoggedIn =
+ *     FluentFuture.from(usersDatabase.getAdminUser())
+ *         .transform(User::getId, directExecutor())
+ *         .transform(ActivityService::isLoggedIn, threadPool)
+ *         .catching(RpcException.class, e -> false, directExecutor());
+ * }
+ * + *

Alternatives

+ * + *

Frameworks

+ * + *

When chaining together a graph of asynchronous operations, you will often find it easier to + * use a framework. Frameworks automate the process, often adding features like monitoring, + * debugging, and cancellation. Examples of frameworks include: + * + *

+ * + *

{@link java.util.concurrent.CompletableFuture} / {@link java.util.concurrent.CompletionStage} + *

+ * + *

Users of {@code CompletableFuture} will likely want to continue using {@code + * CompletableFuture}. {@code FluentFuture} is targeted at people who use {@code ListenableFuture}, + * who can't use Java 8, or who want an API more focused than {@code CompletableFuture}. (If you + * need to adapt between {@code CompletableFuture} and {@code ListenableFuture}, consider Future Converter.) + * + *

Extension

+ * + * If you want a class like {@code FluentFuture} but with extra methods, we recommend declaring your + * own subclass of {@link ListenableFuture}, complete with a method like {@link #from} to adapt an + * existing {@code ListenableFuture}, implemented atop a {@link ForwardingListenableFuture} that + * forwards to that future and adds the desired methods. + * + * @since 23.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public abstract class FluentFuture + extends GwtFluentFutureCatchingSpecialization { + + /** + * A less abstract subclass of AbstractFuture. This can be used to optimize setFuture by ensuring + * that {@link #get} calls exactly the implementation of {@link AbstractFuture#get}. + */ + abstract static class TrustedFuture extends FluentFuture + implements AbstractFuture.Trusted { + @Override + @ParametricNullness + public final V get() throws InterruptedException, ExecutionException { + return super.get(); + } + + @Override + @ParametricNullness + public final V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return super.get(timeout, unit); + } + + @Override + public final boolean isDone() { + return super.isDone(); + } + + @Override + public final boolean isCancelled() { + return super.isCancelled(); + } + + @Override + public final void addListener(Runnable listener, Executor executor) { + super.addListener(listener, executor); + } + + @Override + public final boolean cancel(boolean mayInterruptIfRunning) { + return super.cancel(mayInterruptIfRunning); + } + } + + FluentFuture() {} + + /** + * Converts the given {@code ListenableFuture} to an equivalent {@code FluentFuture}. + * + *

If the given {@code ListenableFuture} is already a {@code FluentFuture}, it is returned + * directly. If not, it is wrapped in a {@code FluentFuture} that delegates all calls to the + * original {@code ListenableFuture}. + */ + public static FluentFuture from(ListenableFuture future) { + return future instanceof FluentFuture + ? (FluentFuture) future + : new ForwardingFluentFuture(future); + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 28.0 + */ + @Deprecated + public static FluentFuture from(FluentFuture future) { + return checkNotNull(future); + } + + /** + * Returns a {@code Future} whose result is taken from this {@code Future} or, if this {@code + * Future} fails with the given {@code exceptionType}, from the result provided by the {@code + * fallback}. {@link Function#apply} is not invoked until the primary input has failed, so if the + * primary input succeeds, it is never invoked. If, during the invocation of {@code fallback}, an + * exception is thrown, this exception is used as the result of the output {@code Future}. + * + *

Usage example: + * + *

{@code
+   * // Falling back to a zero counter in case an exception happens when processing the RPC to fetch
+   * // counters.
+   * ListenableFuture faultTolerantFuture =
+   *     fetchCounters().catching(FetchException.class, x -> 0, directExecutor());
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link #addListener} documentation. All its warnings about heavyweight + * listeners are also applicable to heavyweight functions passed to this method. + * + *

This method is similar to {@link java.util.concurrent.CompletableFuture#exceptionally}. It + * can also serve some of the use cases of {@link java.util.concurrent.CompletableFuture#handle} + * and {@link java.util.concurrent.CompletableFuture#handleAsync} when used along with {@link + * #transform}. + * + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception + * type is matched against the input's exception. "The input's exception" means the cause of + * the {@link ExecutionException} thrown by {@code input.get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other + * unrecoverable errors, callers should prefer more specific types, avoiding {@code + * Throwable.class} in particular. + * @param fallback the {@link Function} to be called if the input fails with the expected + * exception type. The function's argument is the input's exception. "The input's exception" + * means the cause of the {@link ExecutionException} thrown by {@code this.get()} or, if + * {@code get()} throws a different kind of exception, that exception itself. + * @param executor the executor that runs {@code fallback} if the input fails + */ + @J2ktIncompatible + @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") + public final FluentFuture catching( + Class exceptionType, Function fallback, Executor executor) { + return (FluentFuture) Futures.catching(this, exceptionType, fallback, executor); + } + + /** + * Returns a {@code Future} whose result is taken from this {@code Future} or, if this {@code + * Future} fails with the given {@code exceptionType}, from the result provided by the {@code + * fallback}. {@link AsyncFunction#apply} is not invoked until the primary input has failed, so if + * the primary input succeeds, it is never invoked. If, during the invocation of {@code fallback}, + * an exception is thrown, this exception is used as the result of the output {@code Future}. + * + *

Usage examples: + * + *

{@code
+   * // Falling back to a zero counter in case an exception happens when processing the RPC to fetch
+   * // counters.
+   * ListenableFuture faultTolerantFuture =
+   *     fetchCounters().catchingAsync(
+   *         FetchException.class, x -> immediateFuture(0), directExecutor());
+   * }
+ * + *

The fallback can also choose to propagate the original exception when desired: + * + *

{@code
+   * // Falling back to a zero counter only in case the exception was a
+   * // TimeoutException.
+   * ListenableFuture faultTolerantFuture =
+   *     fetchCounters().catchingAsync(
+   *         FetchException.class,
+   *         e -> {
+   *           if (omitDataOnFetchFailure) {
+   *             return immediateFuture(0);
+   *           }
+   *           throw e;
+   *         },
+   *         directExecutor());
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link #addListener} documentation. All its warnings about heavyweight + * listeners are also applicable to heavyweight functions passed to this method. (Specifically, + * {@code directExecutor} functions should avoid heavyweight operations inside {@code + * AsyncFunction.apply}. Any heavyweight operations should occur in other threads responsible for + * completing the returned {@code Future}.) + * + *

This method is similar to {@link java.util.concurrent.CompletableFuture#exceptionally}. It + * can also serve some of the use cases of {@link java.util.concurrent.CompletableFuture#handle} + * and {@link java.util.concurrent.CompletableFuture#handleAsync} when used along with {@link + * #transform}. + * + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception + * type is matched against the input's exception. "The input's exception" means the cause of + * the {@link ExecutionException} thrown by {@code this.get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other + * unrecoverable errors, callers should prefer more specific types, avoiding {@code + * Throwable.class} in particular. + * @param fallback the {@link AsyncFunction} to be called if the input fails with the expected + * exception type. The function's argument is the input's exception. "The input's exception" + * means the cause of the {@link ExecutionException} thrown by {@code input.get()} or, if + * {@code get()} throws a different kind of exception, that exception itself. + * @param executor the executor that runs {@code fallback} if the input fails + */ + @J2ktIncompatible + @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") + public final FluentFuture catchingAsync( + Class exceptionType, AsyncFunction fallback, Executor executor) { + return (FluentFuture) Futures.catchingAsync(this, exceptionType, fallback, executor); + } + + /** + * Returns a future that delegates to this future but will finish early (via a {@link + * TimeoutException} wrapped in an {@link ExecutionException}) if the specified timeout expires. + * If the timeout expires, not only will the output future finish, but also the input future + * ({@code this}) will be cancelled and interrupted. + * + * @param timeout when to time out the future + * @param scheduledExecutor The executor service to enforce the timeout. + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // ScheduledExecutorService + public final FluentFuture withTimeout( + Duration timeout, ScheduledExecutorService scheduledExecutor) { + return withTimeout(toNanosSaturated(timeout), TimeUnit.NANOSECONDS, scheduledExecutor); + } + + /** + * Returns a future that delegates to this future but will finish early (via a {@link + * TimeoutException} wrapped in an {@link ExecutionException}) if the specified timeout expires. + * If the timeout expires, not only will the output future finish, but also the input future + * ({@code this}) will be cancelled and interrupted. + * + * @param timeout when to time out the future + * @param unit the time unit of the time parameter + * @param scheduledExecutor The executor service to enforce the timeout. + */ + @J2ktIncompatible + @GwtIncompatible // ScheduledExecutorService + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public final FluentFuture withTimeout( + long timeout, TimeUnit unit, ScheduledExecutorService scheduledExecutor) { + return (FluentFuture) Futures.withTimeout(this, timeout, unit, scheduledExecutor); + } + + /** + * Returns a new {@code Future} whose result is asynchronously derived from the result of this + * {@code Future}. If the input {@code Future} fails, the returned {@code Future} fails with the + * same exception (and the function is not invoked). + * + *

More precisely, the returned {@code Future} takes its result from a {@code Future} produced + * by applying the given {@code AsyncFunction} to the result of the original {@code Future}. + * Example usage: + * + *

{@code
+   * FluentFuture rowKeyFuture = FluentFuture.from(indexService.lookUp(query));
+   * ListenableFuture queryFuture =
+   *     rowKeyFuture.transformAsync(dataService::readFuture, executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link #addListener} documentation. All its warnings about heavyweight + * listeners are also applicable to heavyweight functions passed to this method. (Specifically, + * {@code directExecutor} functions should avoid heavyweight operations inside {@code + * AsyncFunction.apply}. Any heavyweight operations should occur in other threads responsible for + * completing the returned {@code Future}.) + * + *

The returned {@code Future} attempts to keep its cancellation state in sync with that of the + * input future and that of the future returned by the chain function. That is, if the returned + * {@code Future} is cancelled, it will attempt to cancel the other two, and if either of the + * other two is cancelled, the returned {@code Future} will receive a callback in which it will + * attempt to cancel itself. + * + *

This method is similar to {@link java.util.concurrent.CompletableFuture#thenCompose} and + * {@link java.util.concurrent.CompletableFuture#thenComposeAsync}. It can also serve some of the + * use cases of {@link java.util.concurrent.CompletableFuture#handle} and {@link + * java.util.concurrent.CompletableFuture#handleAsync} when used along with {@link #catching}. + * + * @param function A function to transform the result of this future to the result of the output + * future + * @param executor Executor to run the function in. + * @return A future that holds result of the function (if the input succeeded) or the original + * input's failure (if not) + */ + public final FluentFuture transformAsync( + AsyncFunction function, Executor executor) { + return (FluentFuture) Futures.transformAsync(this, function, executor); + } + + /** + * Returns a new {@code Future} whose result is derived from the result of this {@code Future}. If + * this input {@code Future} fails, the returned {@code Future} fails with the same exception (and + * the function is not invoked). Example usage: + * + *

{@code
+   * ListenableFuture> rowsFuture =
+   *     queryFuture.transform(QueryResult::getRows, executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link #addListener} documentation. All its warnings about heavyweight + * listeners are also applicable to heavyweight functions passed to this method. + * + *

The returned {@code Future} attempts to keep its cancellation state in sync with that of the + * input future. That is, if the returned {@code Future} is cancelled, it will attempt to cancel + * the input, and if the input is cancelled, the returned {@code Future} will receive a callback + * in which it will attempt to cancel itself. + * + *

An example use of this method is to convert a serializable object returned from an RPC into + * a POJO. + * + *

This method is similar to {@link java.util.concurrent.CompletableFuture#thenApply} and + * {@link java.util.concurrent.CompletableFuture#thenApplyAsync}. It can also serve some of the + * use cases of {@link java.util.concurrent.CompletableFuture#handle} and {@link + * java.util.concurrent.CompletableFuture#handleAsync} when used along with {@link #catching}. + * + * @param function A Function to transform the results of this future to the results of the + * returned future. + * @param executor Executor to run the function in. + * @return A future that holds result of the transformation. + */ + public final FluentFuture transform( + Function function, Executor executor) { + return (FluentFuture) Futures.transform(this, function, executor); + } + + /** + * Registers separate success and failure callbacks to be run when this {@code Future}'s + * computation is {@linkplain java.util.concurrent.Future#isDone() complete} or, if the + * computation is already complete, immediately. + * + *

The callback is run on {@code executor}. There is no guaranteed ordering of execution of + * callbacks, but any callback added through this method is guaranteed to be called once the + * computation is complete. + * + *

Example: + * + *

{@code
+   * future.addCallback(
+   *     new FutureCallback() {
+   *       public void onSuccess(QueryResult result) {
+   *         storeInCache(result);
+   *       }
+   *       public void onFailure(Throwable t) {
+   *         reportError(t);
+   *       }
+   *     }, executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link #addListener} documentation. All its warnings about heavyweight + * listeners are also applicable to heavyweight callbacks passed to this method. + * + *

For a more general interface to attach a completion listener, see {@link #addListener}. + * + *

This method is similar to {@link java.util.concurrent.CompletableFuture#whenComplete} and + * {@link java.util.concurrent.CompletableFuture#whenCompleteAsync}. It also serves the use case + * of {@link java.util.concurrent.CompletableFuture#thenAccept} and {@link + * java.util.concurrent.CompletableFuture#thenAcceptAsync}. + * + * @param callback The callback to invoke when this {@code Future} is completed. + * @param executor The executor to run {@code callback} when the future completes. + */ + public final void addCallback(FutureCallback callback, Executor executor) { + Futures.addCallback(this, callback, executor); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingBlockingDeque.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingBlockingDeque.java new file mode 100644 index 0000000000000..61b60b231a6d1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingBlockingDeque.java @@ -0,0 +1,138 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ForwardingDeque; + +import java.util.Collection; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.TimeUnit; + +/** + * A {@link BlockingDeque} which forwards all its method calls to another {@code BlockingDeque}. + * Subclasses should override one or more methods to modify the behavior of the backing deque as + * desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingBlockingDeque} forward indiscriminately + * to the methods of the delegate. For example, overriding {@link #add} alone will not change + * the behaviour of {@link #offer} which can lead to unexpected behaviour. In this case, you should + * override {@code offer} as well, either providing your own implementation, or delegating to the + * provided {@code standardOffer} method. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingBlockingDeque}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even when all of the + * methods that they depend on are thread-safe. + * + * @author Emily Soldal + * @since 21.0 (since 14.0 as {@link + * org.apache.iotdb.google.common.collect.ForwardingBlockingDeque}) + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingBlockingDeque extends ForwardingDeque + implements BlockingDeque { + + /** Constructor for use by subclasses. */ + protected ForwardingBlockingDeque() {} + + @Override + protected abstract BlockingDeque delegate(); + + @Override + public int remainingCapacity() { + return delegate().remainingCapacity(); + } + + @Override + public void putFirst(E e) throws InterruptedException { + delegate().putFirst(e); + } + + @Override + public void putLast(E e) throws InterruptedException { + delegate().putLast(e); + } + + @Override + public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offerFirst(e, timeout, unit); + } + + @Override + public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offerLast(e, timeout, unit); + } + + @Override + public E takeFirst() throws InterruptedException { + return delegate().takeFirst(); + } + + @Override + public E takeLast() throws InterruptedException { + return delegate().takeLast(); + } + + @Override + public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().pollFirst(timeout, unit); + } + + @Override + public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().pollLast(timeout, unit); + } + + @Override + public void put(E e) throws InterruptedException { + delegate().put(e); + } + + @Override + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offer(e, timeout, unit); + } + + @Override + public E take() throws InterruptedException { + return delegate().take(); + } + + @Override + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().poll(timeout, unit); + } + + @Override + public int drainTo(Collection c) { + return delegate().drainTo(c); + } + + @Override + public int drainTo(Collection c, int maxElements) { + return delegate().drainTo(c, maxElements); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingBlockingQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingBlockingQueue.java new file mode 100644 index 0000000000000..a60cabd8ec79a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingBlockingQueue.java @@ -0,0 +1,93 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ForwardingQueue; + +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * A {@link BlockingQueue} which forwards all its method calls to another {@link BlockingQueue}. + * Subclasses should override one or more methods to modify the behavior of the backing collection + * as desired per the decorator + * pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingBlockingQueue}. + * + * @author Raimundo Mirisola + * @param the type of elements held in this collection + * @since 4.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingBlockingQueue extends ForwardingQueue + implements BlockingQueue { + + /** Constructor for use by subclasses. */ + protected ForwardingBlockingQueue() {} + + @Override + protected abstract BlockingQueue delegate(); + + @Override + public int drainTo(Collection c, int maxElements) { + return delegate().drainTo(c, maxElements); + } + + @Override + public int drainTo(Collection c) { + return delegate().drainTo(c); + } + + // TODO(kak): consider removing this + @Override + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return delegate().offer(e, timeout, unit); + } + + // TODO(kak): consider removing this + @Override + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().poll(timeout, unit); + } + + @Override + public void put(E e) throws InterruptedException { + delegate().put(e); + } + + @Override + public int remainingCapacity() { + return delegate().remainingCapacity(); + } + + // TODO(kak): consider removing this + @Override + public E take() throws InterruptedException { + return delegate().take(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingCondition.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingCondition.java new file mode 100644 index 0000000000000..64ab7a195843d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingCondition.java @@ -0,0 +1,68 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; + +/** Forwarding wrapper around a {@code Condition}. */ +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +abstract class ForwardingCondition implements Condition { + abstract Condition delegate(); + + @Override + public void await() throws InterruptedException { + delegate().await(); + } + + @Override + public boolean await(long time, TimeUnit unit) throws InterruptedException { + return delegate().await(time, unit); + } + + @Override + public void awaitUninterruptibly() { + delegate().awaitUninterruptibly(); + } + + @Override + public long awaitNanos(long nanosTimeout) throws InterruptedException { + return delegate().awaitNanos(nanosTimeout); + } + + @Override + public boolean awaitUntil(Date deadline) throws InterruptedException { + return delegate().awaitUntil(deadline); + } + + @Override + public void signal() { + delegate().signal(); + } + + @Override + public void signalAll() { + delegate().signalAll(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingExecutorService.java new file mode 100644 index 0000000000000..d5249fb486bb2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingExecutorService.java @@ -0,0 +1,128 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ForwardingObject; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * An executor service which forwards all its method calls to another executor service. Subclasses + * should override one or more methods to modify the behavior of the backing executor service as + * desired per the decorator pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingExecutorService}. + * + * @author Kurt Alfred Kluever + * @since 10.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingExecutorService extends ForwardingObject + implements ExecutorService { + /** Constructor for use by subclasses. */ + protected ForwardingExecutorService() {} + + @Override + protected abstract ExecutorService delegate(); + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegate().awaitTermination(timeout, unit); + } + + @Override + public List> invokeAll(Collection> tasks) + throws InterruptedException { + return delegate().invokeAll(tasks); + } + + @Override + public List> invokeAll( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate().invokeAll(tasks, timeout, unit); + } + + @Override + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + return delegate().invokeAny(tasks); + } + + @Override + public T invokeAny( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate().invokeAny(tasks, timeout, unit); + } + + @Override + public boolean isShutdown() { + return delegate().isShutdown(); + } + + @Override + public boolean isTerminated() { + return delegate().isTerminated(); + } + + @Override + public void shutdown() { + delegate().shutdown(); + } + + @Override + public List shutdownNow() { + return delegate().shutdownNow(); + } + + @Override + public void execute(Runnable command) { + delegate().execute(command); + } + + @Override + public Future submit(Callable task) { + return delegate().submit(task); + } + + @Override + public Future submit(Runnable task) { + return delegate().submit(task); + } + + @Override + public Future submit(Runnable task, @ParametricNullness T result) { + return delegate().submit(task, result); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingFluentFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingFluentFuture.java new file mode 100644 index 0000000000000..e1401491ff0e1 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingFluentFuture.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * {@link FluentFuture} that forwards all calls to a delegate. + * + *

Extension

+ * + * If you want a class like {@code FluentFuture} but with extra methods, we recommend declaring your + * own subclass of {@link ListenableFuture}, complete with a method like {@link #from} to adapt an + * existing {@code ListenableFuture}, implemented atop a {@link ForwardingListenableFuture} that + * forwards to that future and adds the desired methods. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class ForwardingFluentFuture extends FluentFuture { + private final ListenableFuture delegate; + + ForwardingFluentFuture(ListenableFuture delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + public void addListener(Runnable listener, Executor executor) { + delegate.addListener(listener, executor); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return delegate.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return delegate.isCancelled(); + } + + @Override + public boolean isDone() { + return delegate.isDone(); + } + + @Override + @ParametricNullness + public V get() throws InterruptedException, ExecutionException { + return delegate.get(); + } + + @Override + @ParametricNullness + public V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate.get(timeout, unit); + } + + @Override + public String toString() { + return delegate.toString(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingFuture.java new file mode 100644 index 0000000000000..6a2f558070362 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingFuture.java @@ -0,0 +1,99 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.ForwardingObject; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A {@link Future} which forwards all its method calls to another future. Subclasses should + * override one or more methods to modify the behavior of the backing future as desired per the decorator pattern. + * + *

Most subclasses can just use {@link SimpleForwardingFuture}. + * + * @author Sven Mawson + * @since 1.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingFuture extends ForwardingObject + implements Future { + /** Constructor for use by subclasses. */ + protected ForwardingFuture() {} + + @Override + protected abstract Future delegate(); + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return delegate().cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return delegate().isCancelled(); + } + + @Override + public boolean isDone() { + return delegate().isDone(); + } + + @Override + @ParametricNullness + public V get() throws InterruptedException, ExecutionException { + return delegate().get(); + } + + @Override + @ParametricNullness + public V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate().get(timeout, unit); + } + + // TODO(cpovirk): Use standard Javadoc form for SimpleForwarding* class and constructor + /** + * A simplified version of {@link ForwardingFuture} where subclasses can pass in an already + * constructed {@link Future} as the delegate. + * + * @since 9.0 + */ + public abstract static class SimpleForwardingFuture + extends ForwardingFuture { + private final Future delegate; + + protected SimpleForwardingFuture(Future delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final Future delegate() { + return delegate; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingListenableFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingListenableFuture.java new file mode 100644 index 0000000000000..29dddf0dbaa25 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingListenableFuture.java @@ -0,0 +1,73 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.util.concurrent.Executor; + +/** + * A {@link ListenableFuture} which forwards all its method calls to another future. Subclasses + * should override one or more methods to modify the behavior of the backing future as desired per + * the decorator pattern. + * + *

Most subclasses can just use {@link SimpleForwardingListenableFuture}. + * + * @author Shardul Deo + * @since 4.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingListenableFuture extends ForwardingFuture + implements ListenableFuture { + + /** Constructor for use by subclasses. */ + protected ForwardingListenableFuture() {} + + @Override + protected abstract ListenableFuture delegate(); + + @Override + public void addListener(Runnable listener, Executor exec) { + delegate().addListener(listener, exec); + } + + // TODO(cpovirk): Use standard Javadoc form for SimpleForwarding* class and constructor + /** + * A simplified version of {@link ForwardingListenableFuture} where subclasses can pass in an + * already constructed {@link ListenableFuture} as the delegate. + * + * @since 9.0 + */ + public abstract static class SimpleForwardingListenableFuture + extends ForwardingListenableFuture { + private final ListenableFuture delegate; + + protected SimpleForwardingListenableFuture(ListenableFuture delegate) { + this.delegate = Preconditions.checkNotNull(delegate); + } + + @Override + protected final ListenableFuture delegate() { + return delegate; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingListeningExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingListeningExecutorService.java new file mode 100644 index 0000000000000..7fb9b6c1e90e3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingListeningExecutorService.java @@ -0,0 +1,66 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.Callable; + +/** + * A listening executor service which forwards all its method calls to another listening executor + * service. Subclasses should override one or more methods to modify the behavior of the backing + * executor service as desired per the decorator pattern. + * + *

{@code default} method warning: This class does not forward calls to {@code + * default} methods. Instead, it inherits their default implementations. When those implementations + * invoke methods, they invoke methods on the {@code ForwardingListeningExecutorService}. + * + * @author Isaac Shum + * @since 10.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class ForwardingListeningExecutorService extends ForwardingExecutorService + implements ListeningExecutorService { + /** Constructor for use by subclasses. */ + protected ForwardingListeningExecutorService() {} + + @Override + protected abstract ListeningExecutorService delegate(); + + @Override + public ListenableFuture submit(Callable task) { + return delegate().submit(task); + } + + @Override + public ListenableFuture submit(Runnable task) { + return delegate().submit(task); + } + + @Override + public ListenableFuture submit( + Runnable task, @ParametricNullness T result) { + return delegate().submit(task, result); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingLock.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingLock.java new file mode 100644 index 0000000000000..f9aa1708ce45b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ForwardingLock.java @@ -0,0 +1,63 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +/** Forwarding wrapper around a {@code Lock}. */ +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +abstract class ForwardingLock implements Lock { + abstract Lock delegate(); + + @Override + public void lock() { + delegate().lock(); + } + + @Override + public void lockInterruptibly() throws InterruptedException { + delegate().lockInterruptibly(); + } + + @Override + public boolean tryLock() { + return delegate().tryLock(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + return delegate().tryLock(time, unit); + } + + @Override + public void unlock() { + delegate().unlock(); + } + + @Override + public Condition newCondition() { + return delegate().newCondition(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FutureCallback.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FutureCallback.java new file mode 100644 index 0000000000000..1b690f31be2c4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FutureCallback.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * A callback for accepting the results of a {@link java.util.concurrent.Future} computation + * asynchronously. + * + *

To attach to a {@link ListenableFuture} use {@link Futures#addCallback}. + * + * @author Anthony Zana + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface FutureCallback { + /** Invoked with the result of the {@code Future} computation when it is successful. */ + void onSuccess(@ParametricNullness V result); + + /** + * Invoked when a {@code Future} computation fails or is canceled. + * + *

If the future's {@link Future#get() get} method throws an {@link ExecutionException}, then + * the cause is passed to this method. Any other thrown object is passed unaltered. + */ + void onFailure(Throwable t); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Futures.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Futures.java new file mode 100644 index 0000000000000..e411019cdf2ff --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Futures.java @@ -0,0 +1,1391 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.util.concurrent.CollectionFuture.ListFuture; +import org.apache.iotdb.google.common.util.concurrent.ImmediateFuture.ImmediateCancelledFuture; +import org.apache.iotdb.google.common.util.concurrent.ImmediateFuture.ImmediateFailedFuture; +import org.apache.iotdb.google.common.util.concurrent.internal.InternalFutureFailureAccess; +import org.apache.iotdb.google.common.util.concurrent.internal.InternalFutures; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; + +/** + * Static utility methods pertaining to the {@link Future} interface. + * + *

Many of these methods use the {@link ListenableFuture} API; consult the Guava User Guide + * article on {@code + * ListenableFuture}. + * + *

The main purpose of {@code ListenableFuture} is to help you chain together a graph of + * asynchronous operations. You can chain them together manually with calls to methods like {@link + * Futures#transform(ListenableFuture, Function, Executor) Futures.transform}, but you will often + * find it easier to use a framework. Frameworks automate the process, often adding features like + * monitoring, debugging, and cancellation. Examples of frameworks include: + * + *

+ * + *

If you do chain your operations manually, you may want to use {@link FluentFuture}. + * + * @author Kevin Bourrillion + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Futures extends GwtFuturesCatchingSpecialization { + + // A note on memory visibility. + // Many of the utilities in this class (transform, withFallback, withTimeout, asList, combine) + // have two requirements that significantly complicate their design. + // 1. Cancellation should propagate from the returned future to the input future(s). + // 2. The returned futures shouldn't unnecessarily 'pin' their inputs after completion. + // + // A consequence of these requirements is that the delegate futures cannot be stored in + // final fields. + // + // For simplicity the rest of this description will discuss Futures.catching since it is the + // simplest instance, though very similar descriptions apply to many other classes in this file. + // + // In the constructor of AbstractCatchingFuture, the delegate future is assigned to a field + // 'inputFuture'. That field is non-final and non-volatile. There are 2 places where the + // 'inputFuture' field is read and where we will have to consider visibility of the write + // operation in the constructor. + // + // 1. In the listener that performs the callback. In this case it is fine since inputFuture is + // assigned prior to calling addListener, and addListener happens-before any invocation of the + // listener. Notably, this means that 'volatile' is unnecessary to make 'inputFuture' visible + // to the listener. + // + // 2. In done() where we may propagate cancellation to the input. In this case it is _not_ fine. + // There is currently nothing that enforces that the write to inputFuture in the constructor is + // visible to done(). This is because there is no happens before edge between the write and a + // (hypothetical) unsafe read by our caller. Note: adding 'volatile' does not fix this issue, + // it would just add an edge such that if done() observed non-null, then it would also + // definitely observe all earlier writes, but we still have no guarantee that done() would see + // the initial write (just stronger guarantees if it does). + // + // See: http://cs.oswego.edu/pipermail/concurrency-interest/2015-January/013800.html + // For a (long) discussion about this specific issue and the general futility of life. + // + // For the time being we are OK with the problem discussed above since it requires a caller to + // introduce a very specific kind of data-race. And given the other operations performed by these + // methods that involve volatile read/write operations, in practice there is no issue. Also, the + // way in such a visibility issue would surface is most likely as a failure of cancel() to + // propagate to the input. Cancellation propagation is fundamentally racy so this is fine. + // + // Future versions of the JMM may revise safe construction semantics in such a way that we can + // safely publish these objects and we won't need this whole discussion. + // TODO(user,lukes): consider adding volatile to all these fields since in current known JVMs + // that should resolve the issue. This comes at the cost of adding more write barriers to the + // implementations. + + private Futures() {} + + /** + * Creates a {@code ListenableFuture} which has its value set immediately upon construction. The + * getters just return the value. This {@code Future} can't be canceled or timed out and its + * {@code isDone()} method always returns {@code true}. + */ + public static ListenableFuture immediateFuture( + @ParametricNullness V value) { + if (value == null) { + // This cast is safe because null is assignable to V for all V (i.e. it is bivariant) + @SuppressWarnings("unchecked") + ListenableFuture typedNull = (ListenableFuture) ImmediateFuture.NULL; + return typedNull; + } + return new ImmediateFuture<>(value); + } + + /** + * Returns a successful {@code ListenableFuture}. This method is equivalent to {@code + * immediateFuture(null)} except that it is restricted to produce futures of type {@code Void}. + * + * @since 29.0 + */ + @SuppressWarnings("unchecked") + public static ListenableFuture immediateVoidFuture() { + return (ListenableFuture) ImmediateFuture.NULL; + } + + /** + * Returns a {@code ListenableFuture} which has an exception set immediately upon construction. + * + *

The returned {@code Future} can't be cancelled, and its {@code isDone()} method always + * returns {@code true}. Calling {@code get()} will immediately throw the provided {@code + * Throwable} wrapped in an {@code ExecutionException}. + */ + public static ListenableFuture immediateFailedFuture(Throwable throwable) { + checkNotNull(throwable); + return new ImmediateFailedFuture(throwable); + } + + /** + * Creates a {@code ListenableFuture} which is cancelled immediately upon construction, so that + * {@code isCancelled()} always returns {@code true}. + * + * @since 14.0 + */ + public static ListenableFuture immediateCancelledFuture() { + ListenableFuture instance = ImmediateCancelledFuture.INSTANCE; + if (instance != null) { + return (ListenableFuture) instance; + } + return new ImmediateCancelledFuture<>(); + } + + /** + * Executes {@code callable} on the specified {@code executor}, returning a {@code Future}. + * + * @throws RejectedExecutionException if the task cannot be scheduled for execution + * @since 28.2 + */ + public static ListenableFuture submit( + Callable callable, Executor executor) { + TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); + executor.execute(task); + return task; + } + + /** + * Executes {@code runnable} on the specified {@code executor}, returning a {@code Future} that + * will complete after execution. + * + * @throws RejectedExecutionException if the task cannot be scheduled for execution + * @since 28.2 + */ + public static ListenableFuture submit(Runnable runnable, Executor executor) { + TrustedListenableFutureTask task = TrustedListenableFutureTask.create(runnable, null); + executor.execute(task); + return task; + } + + /** + * Executes {@code callable} on the specified {@code executor}, returning a {@code Future}. + * + * @throws RejectedExecutionException if the task cannot be scheduled for execution + * @since 23.0 + */ + public static ListenableFuture submitAsync( + AsyncCallable callable, Executor executor) { + TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); + executor.execute(task); + return task; + } + + /** + * Schedules {@code callable} on the specified {@code executor}, returning a {@code Future}. + * + * @throws RejectedExecutionException if the task cannot be scheduled for execution + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.util.concurrent.ScheduledExecutorService + // TODO(cpovirk): Return ListenableScheduledFuture? + public static ListenableFuture scheduleAsync( + AsyncCallable callable, Duration delay, ScheduledExecutorService executorService) { + return scheduleAsync(callable, toNanosSaturated(delay), TimeUnit.NANOSECONDS, executorService); + } + + /** + * Schedules {@code callable} on the specified {@code executor}, returning a {@code Future}. + * + * @throws RejectedExecutionException if the task cannot be scheduled for execution + * @since 23.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.util.concurrent.ScheduledExecutorService + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + // TODO(cpovirk): Return ListenableScheduledFuture? + public static ListenableFuture scheduleAsync( + AsyncCallable callable, + long delay, + TimeUnit timeUnit, + ScheduledExecutorService executorService) { + TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); + Future scheduled = executorService.schedule(task, delay, timeUnit); + /* + * Even when the user interrupts the task, we pass `false` to `cancel` so that we don't + * interrupt a second time after the interruption performed by TrustedListenableFutureTask. + */ + task.addListener(() -> scheduled.cancel(false), directExecutor()); + return task; + } + + /** + * Returns a {@code Future} whose result is taken from the given primary {@code input} or, if the + * primary input fails with the given {@code exceptionType}, from the result provided by the + * {@code fallback}. {@link Function#apply} is not invoked until the primary input has failed, so + * if the primary input succeeds, it is never invoked. If, during the invocation of {@code + * fallback}, an exception is thrown, this exception is used as the result of the output {@code + * Future}. + * + *

Usage example: + * + *

{@code
+   * ListenableFuture fetchCounterFuture = ...;
+   *
+   * // Falling back to a zero counter in case an exception happens when
+   * // processing the RPC to fetch counters.
+   * ListenableFuture faultTolerantFuture = Futures.catching(
+   *     fetchCounterFuture, FetchException.class, x -> 0, directExecutor());
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the warnings the {@link MoreExecutors#directExecutor} documentation. + * + * @param input the primary input {@code Future} + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception + * type is matched against the input's exception. "The input's exception" means the cause of + * the {@link ExecutionException} thrown by {@code input.get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other + * unrecoverable errors, callers should prefer more specific types, avoiding {@code + * Throwable.class} in particular. + * @param fallback the {@link Function} to be called if {@code input} fails with the expected + * exception type. The function's argument is the input's exception. "The input's exception" + * means the cause of the {@link ExecutionException} thrown by {@code input.get()} or, if + * {@code get()} throws a different kind of exception, that exception itself. + * @param executor the executor that runs {@code fallback} if {@code input} fails + * @since 19.0 + */ + @J2ktIncompatible + @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") + public static ListenableFuture catching( + ListenableFuture input, + Class exceptionType, + Function fallback, + Executor executor) { + return AbstractCatchingFuture.create(input, exceptionType, fallback, executor); + } + + /** + * Returns a {@code Future} whose result is taken from the given primary {@code input} or, if the + * primary input fails with the given {@code exceptionType}, from the result provided by the + * {@code fallback}. {@link AsyncFunction#apply} is not invoked until the primary input has + * failed, so if the primary input succeeds, it is never invoked. If, during the invocation of + * {@code fallback}, an exception is thrown, this exception is used as the result of the output + * {@code Future}. + * + *

Usage examples: + * + *

{@code
+   * ListenableFuture fetchCounterFuture = ...;
+   *
+   * // Falling back to a zero counter in case an exception happens when
+   * // processing the RPC to fetch counters.
+   * ListenableFuture faultTolerantFuture = Futures.catchingAsync(
+   *     fetchCounterFuture, FetchException.class, x -> immediateFuture(0), directExecutor());
+   * }
+ * + *

The fallback can also choose to propagate the original exception when desired: + * + *

{@code
+   * ListenableFuture fetchCounterFuture = ...;
+   *
+   * // Falling back to a zero counter only in case the exception was a
+   * // TimeoutException.
+   * ListenableFuture faultTolerantFuture = Futures.catchingAsync(
+   *     fetchCounterFuture,
+   *     FetchException.class,
+   *     e -> {
+   *       if (omitDataOnFetchFailure) {
+   *         return immediateFuture(0);
+   *       }
+   *       throw e;
+   *     },
+   *     directExecutor());
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the warnings the {@link MoreExecutors#directExecutor} documentation. + * + * @param input the primary input {@code Future} + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception + * type is matched against the input's exception. "The input's exception" means the cause of + * the {@link ExecutionException} thrown by {@code input.get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other + * unrecoverable errors, callers should prefer more specific types, avoiding {@code + * Throwable.class} in particular. + * @param fallback the {@link AsyncFunction} to be called if {@code input} fails with the expected + * exception type. The function's argument is the input's exception. "The input's exception" + * means the cause of the {@link ExecutionException} thrown by {@code input.get()} or, if + * {@code get()} throws a different kind of exception, that exception itself. + * @param executor the executor that runs {@code fallback} if {@code input} fails + * @since 19.0 (similar functionality in 14.0 as {@code withFallback}) + */ + @J2ktIncompatible + @Partially.GwtIncompatible("AVAILABLE but requires exceptionType to be Throwable.class") + public static ListenableFuture catchingAsync( + ListenableFuture input, + Class exceptionType, + AsyncFunction fallback, + Executor executor) { + return AbstractCatchingFuture.create(input, exceptionType, fallback, executor); + } + + /** + * Returns a future that delegates to another but will finish early (via a {@link + * TimeoutException} wrapped in an {@link ExecutionException}) if the specified duration expires. + * + *

The delegate future is interrupted and cancelled if it times out. + * + * @param delegate The future to delegate to. + * @param time when to time out the future + * @param scheduledExecutor The executor service to enforce the timeout. + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.util.concurrent.ScheduledExecutorService + public static ListenableFuture withTimeout( + ListenableFuture delegate, Duration time, ScheduledExecutorService scheduledExecutor) { + return withTimeout(delegate, toNanosSaturated(time), TimeUnit.NANOSECONDS, scheduledExecutor); + } + + /** + * Returns a future that delegates to another but will finish early (via a {@link + * TimeoutException} wrapped in an {@link ExecutionException}) if the specified duration expires. + * + *

The delegate future is interrupted and cancelled if it times out. + * + * @param delegate The future to delegate to. + * @param time when to time out the future + * @param unit the time unit of the time parameter + * @param scheduledExecutor The executor service to enforce the timeout. + * @since 19.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.util.concurrent.ScheduledExecutorService + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static ListenableFuture withTimeout( + ListenableFuture delegate, + long time, + TimeUnit unit, + ScheduledExecutorService scheduledExecutor) { + if (delegate.isDone()) { + return delegate; + } + return TimeoutFuture.create(delegate, time, unit, scheduledExecutor); + } + + /** + * Returns a new {@code Future} whose result is asynchronously derived from the result of the + * given {@code Future}. If the given {@code Future} fails, the returned {@code Future} fails with + * the same exception (and the function is not invoked). + * + *

More precisely, the returned {@code Future} takes its result from a {@code Future} produced + * by applying the given {@code AsyncFunction} to the result of the original {@code Future}. + * Example usage: + * + *

{@code
+   * ListenableFuture rowKeyFuture = indexService.lookUp(query);
+   * ListenableFuture queryFuture =
+   *     transformAsync(rowKeyFuture, dataService::readFuture, executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the warnings the {@link MoreExecutors#directExecutor} documentation. + * + *

The returned {@code Future} attempts to keep its cancellation state in sync with that of the + * input future and that of the future returned by the chain function. That is, if the returned + * {@code Future} is cancelled, it will attempt to cancel the other two, and if either of the + * other two is cancelled, the returned {@code Future} will receive a callback in which it will + * attempt to cancel itself. + * + * @param input The future to transform + * @param function A function to transform the result of the input future to the result of the + * output future + * @param executor Executor to run the function in. + * @return A future that holds result of the function (if the input succeeded) or the original + * input's failure (if not) + * @since 19.0 (in 11.0 as {@code transform}) + */ + public static ListenableFuture transformAsync( + ListenableFuture input, + AsyncFunction function, + Executor executor) { + return AbstractTransformFuture.create(input, function, executor); + } + + /** + * Returns a new {@code Future} whose result is derived from the result of the given {@code + * Future}. If {@code input} fails, the returned {@code Future} fails with the same exception (and + * the function is not invoked). Example usage: + * + *

{@code
+   * ListenableFuture queryFuture = ...;
+   * ListenableFuture> rowsFuture =
+   *     transform(queryFuture, QueryResult::getRows, executor);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the warnings the {@link MoreExecutors#directExecutor} documentation. + * + *

The returned {@code Future} attempts to keep its cancellation state in sync with that of the + * input future. That is, if the returned {@code Future} is cancelled, it will attempt to cancel + * the input, and if the input is cancelled, the returned {@code Future} will receive a callback + * in which it will attempt to cancel itself. + * + *

An example use of this method is to convert a serializable object returned from an RPC into + * a POJO. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future to the results of + * the returned future. + * @param executor Executor to run the function in. + * @return A future that holds result of the transformation. + * @since 9.0 (in 2.0 as {@code compose}) + */ + public static ListenableFuture transform( + ListenableFuture input, Function function, Executor executor) { + return AbstractTransformFuture.create(input, function, executor); + } + + /** + * Like {@link #transform(ListenableFuture, Function, Executor)} except that the transformation + * {@code function} is invoked on each call to {@link Future#get() get()} on the returned future. + * + *

The returned {@code Future} reflects the input's cancellation state directly, and any + * attempt to cancel the returned Future is likewise passed through to the input Future. + * + *

Note that calls to {@linkplain Future#get(long, TimeUnit) timed get} only apply the timeout + * to the execution of the underlying {@code Future}, not to the execution of the + * transformation function. + * + *

The primary audience of this method is callers of {@code transform} who don't have a {@code + * ListenableFuture} available and do not mind repeated, lazy function evaluation. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future to the results of + * the returned future. + * @return A future that returns the result of the transformation. + * @since 10.0 + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static Future lazyTransform( + final Future input, final Function function) { + checkNotNull(input); + checkNotNull(function); + return new Future() { + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return input.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return input.isCancelled(); + } + + @Override + public boolean isDone() { + return input.isDone(); + } + + @Override + public O get() throws InterruptedException, ExecutionException { + return applyTransformation(input.get()); + } + + @Override + public O get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return applyTransformation(input.get(timeout, unit)); + } + + private O applyTransformation(I input) throws ExecutionException { + try { + return function.apply(input); + } catch (RuntimeException | Error t) { + throw new ExecutionException(t); + } + } + }; + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the values of all its + * input futures, if all succeed. + * + *

The list of results is in the same order as the input list. + * + *

This differs from {@link #successfulAsList(ListenableFuture[])} in that it will return a + * failed future if any of the items fails. + * + *

Canceling this future will attempt to cancel all the component futures, and if any of the + * provided futures fails or is canceled, this one is, too. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component futures + * @since 10.0 + */ + @SafeVarargs + public static ListenableFuture> allAsList( + ListenableFuture... futures) { + ListenableFuture> nullable = new ListFuture(ImmutableList.copyOf(futures), true); + // allAsList ensures that it fills the output list with V instances. + @SuppressWarnings("nullness") + ListenableFuture> nonNull = nullable; + return nonNull; + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the values of all its + * input futures, if all succeed. + * + *

The list of results is in the same order as the input list. + * + *

This differs from {@link #successfulAsList(Iterable)} in that it will return a failed future + * if any of the items fails. + * + *

Canceling this future will attempt to cancel all the component futures, and if any of the + * provided futures fails or is canceled, this one is, too. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component futures + * @since 10.0 + */ + public static ListenableFuture> allAsList( + Iterable> futures) { + ListenableFuture> nullable = new ListFuture(ImmutableList.copyOf(futures), true); + // allAsList ensures that it fills the output list with V instances. + @SuppressWarnings("nullness") + ListenableFuture> nonNull = nullable; + return nonNull; + } + + /** + * Creates a {@link FutureCombiner} that processes the completed futures whether or not they're + * successful. + * + *

Any failures from the input futures will not be propagated to the returned future. + * + * @since 20.0 + */ + @SafeVarargs + public static FutureCombiner whenAllComplete( + ListenableFuture... futures) { + return new FutureCombiner(false, ImmutableList.copyOf(futures)); + } + + /** + * Creates a {@link FutureCombiner} that processes the completed futures whether or not they're + * successful. + * + *

Any failures from the input futures will not be propagated to the returned future. + * + * @since 20.0 + */ + public static FutureCombiner whenAllComplete( + Iterable> futures) { + return new FutureCombiner(false, ImmutableList.copyOf(futures)); + } + + /** + * Creates a {@link FutureCombiner} requiring that all passed in futures are successful. + * + *

If any input fails, the returned future fails immediately. + * + * @since 20.0 + */ + @SafeVarargs + public static FutureCombiner whenAllSucceed( + ListenableFuture... futures) { + return new FutureCombiner(true, ImmutableList.copyOf(futures)); + } + + /** + * Creates a {@link FutureCombiner} requiring that all passed in futures are successful. + * + *

If any input fails, the returned future fails immediately. + * + * @since 20.0 + */ + public static FutureCombiner whenAllSucceed( + Iterable> futures) { + return new FutureCombiner(true, ImmutableList.copyOf(futures)); + } + + /** + * A helper to create a new {@code ListenableFuture} whose result is generated from a combination + * of input futures. + * + *

See {@link #whenAllComplete} and {@link #whenAllSucceed} for how to instantiate this class. + * + *

Example: + * + *

{@code
+   * final ListenableFuture loginDateFuture =
+   *     loginService.findLastLoginDate(username);
+   * final ListenableFuture> recentCommandsFuture =
+   *     recentCommandsService.findRecentCommands(username);
+   * ListenableFuture usageFuture =
+   *     Futures.whenAllSucceed(loginDateFuture, recentCommandsFuture)
+   *         .call(
+   *             () ->
+   *                 new UsageHistory(
+   *                     username,
+   *                     Futures.getDone(loginDateFuture),
+   *                     Futures.getDone(recentCommandsFuture)),
+   *             executor);
+   * }
+ * + * @since 20.0 + */ + @GwtCompatible + public static final class FutureCombiner { + private final boolean allMustSucceed; + private final ImmutableList> futures; + + private FutureCombiner( + boolean allMustSucceed, ImmutableList> futures) { + this.allMustSucceed = allMustSucceed; + this.futures = futures; + } + + /** + * Creates the {@link ListenableFuture} which will return the result of calling {@link + * AsyncCallable#call} in {@code combiner} when all futures complete, using the specified {@code + * executor}. + * + *

If the combiner throws a {@code CancellationException}, the returned future will be + * cancelled. + * + *

If the combiner throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and returned as the cause of the new {@code + * ExecutionException} that gets thrown by the returned combined future. + * + *

Canceling this future will attempt to cancel all the component futures. + * + * @return a future whose result is based on {@code combiner} (or based on the input futures + * passed to {@code whenAllSucceed}, if that is the method you used to create this {@code + * FutureCombiner}). Even if you don't care about the value of the future, you should + * typically check whether it failed: See https://errorprone.info/bugpattern/FutureReturnValueIgnored. + */ + public ListenableFuture callAsync( + AsyncCallable combiner, Executor executor) { + return new CombinedFuture(futures, allMustSucceed, executor, combiner); + } + + /** + * Creates the {@link ListenableFuture} which will return the result of calling {@link + * Callable#call} in {@code combiner} when all futures complete, using the specified {@code + * executor}. + * + *

If the combiner throws a {@code CancellationException}, the returned future will be + * cancelled. + * + *

If the combiner throws an {@code ExecutionException}, the cause of the thrown {@code + * ExecutionException} will be extracted and returned as the cause of the new {@code + * ExecutionException} that gets thrown by the returned combined future. + * + *

Canceling this future will attempt to cancel all the component futures. + * + * @return a future whose result is based on {@code combiner} (or based on the input futures + * passed to {@code whenAllSucceed}, if that is the method you used to create this {@code + * FutureCombiner}). Even if you don't care about the value of the future, you should + * typically check whether it failed: See https://errorprone.info/bugpattern/FutureReturnValueIgnored. + */ + public ListenableFuture call(Callable combiner, Executor executor) { + return new CombinedFuture(futures, allMustSucceed, executor, combiner); + } + + /** + * Creates the {@link ListenableFuture} which will return the result of running {@code combiner} + * when all Futures complete. {@code combiner} will run using {@code executor}. + * + *

If the combiner throws a {@code CancellationException}, the returned future will be + * cancelled. + * + *

Canceling this Future will attempt to cancel all the component futures. + * + * @since 23.6 + * @return a future whose result is based on {@code combiner} (or based on the input futures + * passed to {@code whenAllSucceed}, if that is the method you used to create this {@code + * FutureCombiner}). Even though the future never produces a value other than {@code null}, + * you should typically check whether it failed: See https://errorprone.info/bugpattern/FutureReturnValueIgnored. + */ + public ListenableFuture run(final Runnable combiner, Executor executor) { + return call( + new Callable() { + @Override + public Void call() throws Exception { + combiner.run(); + return null; + } + }, + executor); + } + } + + /** + * Returns a {@code ListenableFuture} whose result is set from the supplied future when it + * completes. Cancelling the supplied future will also cancel the returned future, but cancelling + * the returned future will have no effect on the supplied future. + * + * @since 15.0 + */ + public static ListenableFuture nonCancellationPropagating( + ListenableFuture future) { + if (future.isDone()) { + return future; + } + NonCancellationPropagatingFuture output = new NonCancellationPropagatingFuture<>(future); + future.addListener(output, directExecutor()); + return output; + } + + /** A wrapped future that does not propagate cancellation to its delegate. */ + private static final class NonCancellationPropagatingFuture + extends AbstractFuture.TrustedFuture implements Runnable { + private ListenableFuture delegate; + + NonCancellationPropagatingFuture(final ListenableFuture delegate) { + this.delegate = delegate; + } + + @Override + public void run() { + // This prevents cancellation from propagating because we don't call setFuture(delegate) until + // delegate is already done, so calling cancel() on this future won't affect it. + ListenableFuture localDelegate = delegate; + if (localDelegate != null) { + setFuture(localDelegate); + } + } + + @Override + protected String pendingToString() { + ListenableFuture localDelegate = delegate; + if (localDelegate != null) { + return "delegate=[" + localDelegate + "]"; + } + return null; + } + + @Override + protected void afterDone() { + delegate = null; + } + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the values of all its + * successful input futures. The list of results is in the same order as the input list, and if + * any of the provided futures fails or is canceled, its corresponding position will contain + * {@code null} (which is indistinguishable from the future having a successful value of {@code + * null}). + * + *

The list of results is in the same order as the input list. + * + *

This differs from {@link #allAsList(ListenableFuture[])} in that it's tolerant of failed + * futures for any of the items, representing them as {@code null} in the result list. + * + *

Canceling this future will attempt to cancel all the component futures. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component futures + * @since 10.0 + */ + @SafeVarargs + public static ListenableFuture> successfulAsList( + ListenableFuture... futures) { + /* + * Another way to express this signature would be to bound by @NonNull and accept + * LF. That might be better: There's currently no difference between the + * outputs users get when calling this with and calling it with <@Nullable Foo>. The only + * difference is that calling it with won't work when an input Future has a @Nullable + * type. So why even make that error possible by giving callers the choice? + * + * On the other hand, the current signature is consistent with the similar allAsList method. And + * eventually this method may go away entirely in favor of an API like + * whenAllComplete().collectSuccesses(). That API would have a signature more like the current + * one. + */ + return new ListFuture(ImmutableList.copyOf(futures), false); + } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the values of all its + * successful input futures. The list of results is in the same order as the input list, and if + * any of the provided futures fails or is canceled, its corresponding position will contain + * {@code null} (which is indistinguishable from the future having a successful value of {@code + * null}). + * + *

The list of results is in the same order as the input list. + * + *

This differs from {@link #allAsList(Iterable)} in that it's tolerant of failed futures for + * any of the items, representing them as {@code null} in the result list. + * + *

Canceling this future will attempt to cancel all the component futures. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component futures + * @since 10.0 + */ + public static ListenableFuture> successfulAsList( + Iterable> futures) { + return new ListFuture(ImmutableList.copyOf(futures), false); + } + + /** + * Returns a list of delegate futures that correspond to the futures received in the order that + * they complete. Delegate futures return the same value or throw the same exception as the + * corresponding input future returns/throws. + * + *

"In the order that they complete" means, for practical purposes, about what you would + * expect, but there are some subtleties. First, we do guarantee that, if the output future at + * index n is done, the output future at index n-1 is also done. (But as usual with futures, some + * listeners for future n may complete before some for future n-1.) However, it is possible, if + * one input completes with result X and another later with result Y, for Y to come before X in + * the output future list. (Such races are impossible to solve without global synchronization of + * all future completions. And they should have little practical impact.) + * + *

Cancelling a delegate future propagates to input futures once all the delegates complete, + * either from cancellation or because an input future has completed. If N futures are passed in, + * and M delegates are cancelled, the remaining M input futures will be cancelled once N - M of + * the input futures complete. If all the delegates are cancelled, all the input futures will be + * too. + * + * @since 17.0 + */ + public static ImmutableList> inCompletionOrder( + Iterable> futures) { + ListenableFuture[] copy = gwtCompatibleToArray(futures); + final InCompletionOrderState state = new InCompletionOrderState<>(copy); + ImmutableList.Builder> delegatesBuilder = + ImmutableList.builderWithExpectedSize(copy.length); + for (int i = 0; i < copy.length; i++) { + delegatesBuilder.add(new InCompletionOrderFuture(state)); + } + + final ImmutableList> delegates = delegatesBuilder.build(); + for (int i = 0; i < copy.length; i++) { + final int localI = i; + copy[i].addListener(() -> state.recordInputCompletion(delegates, localI), directExecutor()); + } + + @SuppressWarnings("unchecked") + ImmutableList> delegatesCast = (ImmutableList) delegates; + return delegatesCast; + } + + /** Can't use Iterables.toArray because it's not gwt compatible */ + @SuppressWarnings("unchecked") + private static ListenableFuture[] gwtCompatibleToArray( + Iterable> futures) { + final Collection> collection; + if (futures instanceof Collection) { + collection = (Collection>) futures; + } else { + collection = ImmutableList.copyOf(futures); + } + return (ListenableFuture[]) collection.toArray(new ListenableFuture[0]); + } + + // This can't be a TrustedFuture, because TrustedFuture has clever optimizations that + // mean cancel won't be called if this Future is passed into setFuture, and then + // cancelled. + private static final class InCompletionOrderFuture extends AbstractFuture { + private InCompletionOrderState state; + + private InCompletionOrderFuture(InCompletionOrderState state) { + this.state = state; + } + + @Override + public boolean cancel(boolean interruptIfRunning) { + InCompletionOrderState localState = state; + if (super.cancel(interruptIfRunning)) { + /* + * requireNonNull is generally safe: If cancel succeeded, then this Future was still + * pending, so its `state` field hasn't been nulled out yet. + * + * OK, it's technically possible for this to fail in the presence of unsafe publishing, as + * discussed in the comments in TimeoutFuture. TODO(cpovirk): Maybe check for null before + * calling recordOutputCancellation? + */ + requireNonNull(localState).recordOutputCancellation(interruptIfRunning); + return true; + } + return false; + } + + @Override + protected void afterDone() { + state = null; + } + + @Override + protected String pendingToString() { + InCompletionOrderState localState = state; + if (localState != null) { + // Don't print the actual array! We don't want inCompletionOrder(list).toString() to have + // quadratic output. + return "inputCount=[" + + localState.inputFutures.length + + "], remaining=[" + + localState.incompleteOutputCount.get() + + "]"; + } + return null; + } + } + + private static final class InCompletionOrderState { + // A happens-before edge between the writes of these fields and their reads exists, because + // in order to read these fields, the corresponding write to incompleteOutputCount must have + // been read. + private boolean wasCancelled = false; + private boolean shouldInterrupt = true; + private final AtomicInteger incompleteOutputCount; + // We set the elements of the array to null as they complete. + private final ListenableFuture[] inputFutures; + private volatile int delegateIndex = 0; + + private InCompletionOrderState(ListenableFuture[] inputFutures) { + this.inputFutures = inputFutures; + incompleteOutputCount = new AtomicInteger(inputFutures.length); + } + + private void recordOutputCancellation(boolean interruptIfRunning) { + wasCancelled = true; + // If all the futures were cancelled with interruption, cancel the input futures + // with interruption; otherwise cancel without + if (!interruptIfRunning) { + shouldInterrupt = false; + } + recordCompletion(); + } + + private void recordInputCompletion( + ImmutableList> delegates, int inputFutureIndex) { + /* + * requireNonNull is safe because we accepted an Iterable of non-null Future instances, and we + * don't overwrite an element in the array until after reading it. + */ + ListenableFuture inputFuture = requireNonNull(inputFutures[inputFutureIndex]); + // Null out our reference to this future, so it can be GCed + inputFutures[inputFutureIndex] = null; + for (int i = delegateIndex; i < delegates.size(); i++) { + if (delegates.get(i).setFuture(inputFuture)) { + recordCompletion(); + // this is technically unnecessary, but should speed up later accesses + delegateIndex = i + 1; + return; + } + } + // If all the delegates were complete, no reason for the next listener to have to + // go through the whole list. Avoids O(n^2) behavior when the entire output list is + // cancelled. + delegateIndex = delegates.size(); + } + + private void recordCompletion() { + if (incompleteOutputCount.decrementAndGet() == 0 && wasCancelled) { + for (ListenableFuture toCancel : inputFutures) { + if (toCancel != null) { + toCancel.cancel(shouldInterrupt); + } + } + } + } + } + + /** + * Registers separate success and failure callbacks to be run when the {@code Future}'s + * computation is {@linkplain java.util.concurrent.Future#isDone() complete} or, if the + * computation is already complete, immediately. + * + *

The callback is run on {@code executor}. There is no guaranteed ordering of execution of + * callbacks, but any callback added through this method is guaranteed to be called once the + * computation is complete. + * + *

Exceptions thrown by a {@code callback} will be propagated up to the executor. Any exception + * thrown during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an + * exception thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught + * and logged. + * + *

Example: + * + *

{@code
+   * ListenableFuture future = ...;
+   * Executor e = ...
+   * addCallback(future,
+   *     new FutureCallback() {
+   *       public void onSuccess(QueryResult result) {
+   *         storeInCache(result);
+   *       }
+   *       public void onFailure(Throwable t) {
+   *         reportError(t);
+   *       }
+   *     }, e);
+   * }
+ * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the warnings the {@link MoreExecutors#directExecutor} documentation. + * + *

For a more general interface to attach a completion listener to a {@code Future}, see {@link + * ListenableFuture#addListener addListener}. + * + * @param future The future attach the callback to. + * @param callback The callback to invoke when {@code future} is completed. + * @param executor The executor to run {@code callback} when the future completes. + * @since 10.0 + */ + public static void addCallback( + final ListenableFuture future, + final FutureCallback callback, + Executor executor) { + Preconditions.checkNotNull(callback); + future.addListener(new CallbackListener(future, callback), executor); + } + + /** See {@link #addCallback(ListenableFuture, FutureCallback, Executor)} for behavioral notes. */ + private static final class CallbackListener implements Runnable { + final Future future; + final FutureCallback callback; + + CallbackListener(Future future, FutureCallback callback) { + this.future = future; + this.callback = callback; + } + + @Override + public void run() { + if (future instanceof InternalFutureFailureAccess) { + Throwable failure = + InternalFutures.tryInternalFastPathGetFailure((InternalFutureFailureAccess) future); + if (failure != null) { + callback.onFailure(failure); + return; + } + } + final V value; + try { + value = getDone(future); + } catch (ExecutionException e) { + callback.onFailure(e.getCause()); + return; + } catch (RuntimeException | Error e) { + callback.onFailure(e); + return; + } + callback.onSuccess(value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).addValue(callback).toString(); + } + } + + /** + * Returns the result of the input {@code Future}, which must have already completed. + * + *

The benefits of this method are twofold. First, the name "getDone" suggests to readers that + * the {@code Future} is already done. Second, if buggy code calls {@code getDone} on a {@code + * Future} that is still pending, the program will throw instead of block. This can be important + * for APIs like {@link #whenAllComplete whenAllComplete(...)}{@code .}{@link + * FutureCombiner#call(Callable, Executor) call(...)}, where it is easy to use a new input from + * the {@code call} implementation but forget to add it to the arguments of {@code + * whenAllComplete}. + * + *

If you are looking for a method to determine whether a given {@code Future} is done, use the + * instance method {@link Future#isDone()}. + * + * @throws ExecutionException if the {@code Future} failed with an exception + * @throws CancellationException if the {@code Future} was cancelled + * @throws IllegalStateException if the {@code Future} is not done + * @since 20.0 + */ + // TODO(cpovirk): Consider calling getDone() in our own code. + @ParametricNullness + public static V getDone(Future future) throws ExecutionException { + /* + * We throw IllegalStateException, since the call could succeed later. Perhaps we "should" throw + * IllegalArgumentException, since the call could succeed with a different argument. Those + * exceptions' docs suggest that either is acceptable. Google's Java Practices page recommends + * IllegalArgumentException here, in part to keep its recommendation simple: Static methods + * should throw IllegalStateException only when they use static state. + * + * Why do we deviate here? The answer: We want for fluentFuture.getDone() to throw the same + * exception as Futures.getDone(fluentFuture). + */ + checkState(future.isDone(), "Future was expected to be done: %s", future); + return getUninterruptibly(future); + } + + /** + * Returns the result of {@link Future#get()}, converting most exceptions to a new instance of the + * given checked exception type. This reduces boilerplate for a common use of {@code Future} in + * which it is unnecessary to programmatically distinguish between exception types or to extract + * other information from the exception instance. + * + *

Exceptions from {@code Future.get} are treated as follows: + * + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an {@code X} if the cause + * is a checked exception, an {@link UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an {@code Error}. + *
  • Any {@link InterruptedException} is wrapped in an {@code X} (after restoring the + * interrupt). + *
  • Any {@link CancellationException} is propagated untouched, as is any other {@link + * RuntimeException} (though {@code get} implementations are discouraged from throwing such + * exceptions). + *
+ * + *

The overall principle is to continue to treat every checked exception as a checked + * exception, every unchecked exception as an unchecked exception, and every error as an error. In + * addition, the cause of any {@code ExecutionException} is wrapped in order to ensure that the + * new stack trace matches that of the current thread. + * + *

Instances of {@code exceptionClass} are created by choosing an arbitrary public constructor + * that accepts zero or more arguments, all of type {@code String} or {@code Throwable} + * (preferring constructors with at least one {@code String}) and calling the constructor via + * reflection. If the exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an {@code + * IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code ExecutionException} + * whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code ExecutionException} with a + * {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} with an {@code + * Error} as its cause + * @throws CancellationException if {@code get} throws a {@code CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code RuntimeException} or + * does not have a suitable constructor + * @since 19.0 (in 10.0 as {@code get}) + */ + @J2ktIncompatible + @GwtIncompatible // reflection + @ParametricNullness + public static V getChecked( + Future future, Class exceptionClass) throws X { + return FuturesGetChecked.getChecked(future, exceptionClass); + } + + /** + * Returns the result of {@link Future#get(long, TimeUnit)}, converting most exceptions to a new + * instance of the given checked exception type. This reduces boilerplate for a common use of + * {@code Future} in which it is unnecessary to programmatically distinguish between exception + * types or to extract other information from the exception instance. + * + *

Exceptions from {@code Future.get} are treated as follows: + * + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an {@code X} if the cause + * is a checked exception, an {@link UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an {@code Error}. + *
  • Any {@link InterruptedException} is wrapped in an {@code X} (after restoring the + * interrupt). + *
  • Any {@link TimeoutException} is wrapped in an {@code X}. + *
  • Any {@link CancellationException} is propagated untouched, as is any other {@link + * RuntimeException} (though {@code get} implementations are discouraged from throwing such + * exceptions). + *
+ * + *

The overall principle is to continue to treat every checked exception as a checked + * exception, every unchecked exception as an unchecked exception, and every error as an error. In + * addition, the cause of any {@code ExecutionException} is wrapped in order to ensure that the + * new stack trace matches that of the current thread. + * + *

Instances of {@code exceptionClass} are created by choosing an arbitrary public constructor + * that accepts zero or more arguments, all of type {@code String} or {@code Throwable} + * (preferring constructors with at least one {@code String}) and calling the constructor via + * reflection. If the exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an {@code + * IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code ExecutionException} + * whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code ExecutionException} with a + * {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} with an {@code + * Error} as its cause + * @throws CancellationException if {@code get} throws a {@code CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code RuntimeException} or + * does not have a suitable constructor + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // reflection + @ParametricNullness + public static V getChecked( + Future future, Class exceptionClass, Duration timeout) throws X { + return getChecked(future, exceptionClass, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Returns the result of {@link Future#get(long, TimeUnit)}, converting most exceptions to a new + * instance of the given checked exception type. This reduces boilerplate for a common use of + * {@code Future} in which it is unnecessary to programmatically distinguish between exception + * types or to extract other information from the exception instance. + * + *

Exceptions from {@code Future.get} are treated as follows: + * + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an {@code X} if the cause + * is a checked exception, an {@link UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an {@code Error}. + *
  • Any {@link InterruptedException} is wrapped in an {@code X} (after restoring the + * interrupt). + *
  • Any {@link TimeoutException} is wrapped in an {@code X}. + *
  • Any {@link CancellationException} is propagated untouched, as is any other {@link + * RuntimeException} (though {@code get} implementations are discouraged from throwing such + * exceptions). + *
+ * + *

The overall principle is to continue to treat every checked exception as a checked + * exception, every unchecked exception as an unchecked exception, and every error as an error. In + * addition, the cause of any {@code ExecutionException} is wrapped in order to ensure that the + * new stack trace matches that of the current thread. + * + *

Instances of {@code exceptionClass} are created by choosing an arbitrary public constructor + * that accepts zero or more arguments, all of type {@code String} or {@code Throwable} + * (preferring constructors with at least one {@code String}) and calling the constructor via + * reflection. If the exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an {@code + * IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code ExecutionException} + * whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code ExecutionException} with a + * {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} with an {@code + * Error} as its cause + * @throws CancellationException if {@code get} throws a {@code CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code RuntimeException} or + * does not have a suitable constructor + * @since 19.0 (in 10.0 as {@code get} and with different parameter order) + */ + @J2ktIncompatible + @GwtIncompatible // reflection + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @ParametricNullness + public static V getChecked( + Future future, Class exceptionClass, long timeout, TimeUnit unit) throws X { + return FuturesGetChecked.getChecked(future, exceptionClass, timeout, unit); + } + + /** + * Returns the result of calling {@link Future#get()} uninterruptibly on a task known not to throw + * a checked exception. This makes {@code Future} more suitable for lightweight, fast-running + * tasks that, barring bugs in the code, will not fail. This gives it exception-handling behavior + * similar to that of {@code ForkJoinTask.join}. + * + *

Exceptions from {@code Future.get} are treated as follows: + * + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an {@link + * UncheckedExecutionException} (if the cause is an {@code Exception}) or {@link + * ExecutionError} (if the cause is an {@code Error}). + *
  • Any {@link InterruptedException} causes a retry of the {@code get} call. The interrupt is + * restored before {@code getUnchecked} returns. + *
  • Any {@link CancellationException} is propagated untouched. So is any other {@link + * RuntimeException} ({@code get} implementations are discouraged from throwing such + * exceptions). + *
+ * + *

The overall principle is to eliminate all checked exceptions: to loop to avoid {@code + * InterruptedException}, to pass through {@code CancellationException}, and to wrap any exception + * from the underlying computation in an {@code UncheckedExecutionException} or {@code + * ExecutionError}. + * + *

For an uninterruptible {@code get} that preserves other exceptions, see {@link + * Uninterruptibles#getUninterruptibly(Future)}. + * + * @throws UncheckedExecutionException if {@code get} throws an {@code ExecutionException} with an + * {@code Exception} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} with an {@code + * Error} as its cause + * @throws CancellationException if {@code get} throws a {@code CancellationException} + * @since 10.0 + */ + @ParametricNullness + public static V getUnchecked(Future future) { + checkNotNull(future); + try { + return getUninterruptibly(future); + } catch (ExecutionException e) { + wrapAndThrowUnchecked(e.getCause()); + throw new AssertionError(); + } + } + + private static void wrapAndThrowUnchecked(Throwable cause) { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } + /* + * It's an Exception. (Or it's a non-Error, non-Exception Throwable. From my survey of such + * classes, I believe that most users intended to extend Exception, so we'll treat it like an + * Exception.) + */ + throw new UncheckedExecutionException(cause); + } + + /* + * Arguably we don't need a timed getUnchecked because any operation slow enough to require a + * timeout is heavyweight enough to throw a checked exception and therefore be inappropriate to + * use with getUnchecked. Further, it's not clear that converting the checked TimeoutException to + * a RuntimeException -- especially to an UncheckedExecutionException, since it wasn't thrown by + * the computation -- makes sense, and if we don't convert it, the user still has to write a + * try-catch block. + * + * If you think you would use this method, let us know. You might also look into the + * Fork-Join framework: http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FuturesGetChecked.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FuturesGetChecked.java new file mode 100644 index 0000000000000..e939adac707cf --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/FuturesGetChecked.java @@ -0,0 +1,306 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.collect.Ordering; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.lang.Thread.currentThread; +import static java.util.Arrays.asList; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; + +/** Static methods used to implement {@link Futures#getChecked(Future, Class)}. */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class FuturesGetChecked { + @ParametricNullness + static V getChecked( + Future future, Class exceptionClass) throws X { + return getChecked(bestGetCheckedTypeValidator(), future, exceptionClass); + } + + /** Implementation of {@link Futures#getChecked(Future, Class)}. */ + @VisibleForTesting + @ParametricNullness + static V getChecked( + GetCheckedTypeValidator validator, Future future, Class exceptionClass) throws X { + validator.validateClass(exceptionClass); + try { + return future.get(); + } catch (InterruptedException e) { + currentThread().interrupt(); + throw newWithCause(exceptionClass, e); + } catch (ExecutionException e) { + wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); + throw new AssertionError(); + } + } + + /** Implementation of {@link Futures#getChecked(Future, Class, long, TimeUnit)}. */ + @ParametricNullness + static V getChecked( + Future future, Class exceptionClass, long timeout, TimeUnit unit) throws X { + // TODO(cpovirk): benchmark a version of this method that accepts a GetCheckedTypeValidator + bestGetCheckedTypeValidator().validateClass(exceptionClass); + try { + return future.get(timeout, unit); + } catch (InterruptedException e) { + currentThread().interrupt(); + throw newWithCause(exceptionClass, e); + } catch (TimeoutException e) { + throw newWithCause(exceptionClass, e); + } catch (ExecutionException e) { + wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); + throw new AssertionError(); + } + } + + @VisibleForTesting + interface GetCheckedTypeValidator { + void validateClass(Class exceptionClass); + } + + private static GetCheckedTypeValidator bestGetCheckedTypeValidator() { + return GetCheckedTypeValidatorHolder.BEST_VALIDATOR; + } + + @VisibleForTesting + static GetCheckedTypeValidator weakSetValidator() { + return GetCheckedTypeValidatorHolder.WeakSetValidator.INSTANCE; + } + + // ClassValue + @VisibleForTesting + static GetCheckedTypeValidator classValueValidator() { + return GetCheckedTypeValidatorHolder.ClassValueValidator.INSTANCE; + } + + /** + * Provides a check of whether an exception type is valid for use with {@link + * FuturesGetChecked#getChecked(Future, Class)}, possibly using caching. + * + *

Uses reflection to gracefully fall back to when certain implementations aren't available. + */ + @VisibleForTesting + static class GetCheckedTypeValidatorHolder { + static final String CLASS_VALUE_VALIDATOR_NAME = + GetCheckedTypeValidatorHolder.class.getName() + "$ClassValueValidator"; + + static final GetCheckedTypeValidator BEST_VALIDATOR = getBestValidator(); + + // ClassValue + enum ClassValueValidator implements GetCheckedTypeValidator { + INSTANCE; + + /* + * Static final fields are presumed to be fastest, based on our experience with + * UnsignedBytesBenchmark. TODO(cpovirk): benchmark this + */ + private static final ClassValue isValidClass = + new ClassValue() { + @Override + protected Boolean computeValue(Class type) { + checkExceptionClassValidity(type.asSubclass(Exception.class)); + return true; + } + }; + + @Override + public void validateClass(Class exceptionClass) { + isValidClass.get(exceptionClass); // throws if invalid; returns safely (and caches) if valid + } + } + + enum WeakSetValidator implements GetCheckedTypeValidator { + INSTANCE; + + /* + * Static final fields are presumed to be fastest, based on our experience with + * UnsignedBytesBenchmark. TODO(cpovirk): benchmark this + */ + /* + * A CopyOnWriteArraySet is faster than a newSetFromMap of a MapMaker map with + * weakKeys() and concurrencyLevel(1), even up to at least 12 cached exception types. + */ + private static final Set>> validClasses = + new CopyOnWriteArraySet<>(); + + @Override + public void validateClass(Class exceptionClass) { + for (WeakReference> knownGood : validClasses) { + if (exceptionClass.equals(knownGood.get())) { + return; + } + // TODO(cpovirk): if reference has been cleared, remove it? + } + checkExceptionClassValidity(exceptionClass); + + /* + * It's very unlikely that any loaded Futures class will see getChecked called with more + * than a handful of exceptions. But it seems prudent to set a cap on how many we'll cache. + * This avoids out-of-control memory consumption, and it keeps the cache from growing so + * large that doing the lookup is noticeably slower than redoing the work would be. + * + * Ideally we'd have a real eviction policy, but until we see a problem in practice, I hope + * that this will suffice. I have not even benchmarked with different size limits. + */ + if (validClasses.size() > 1000) { + validClasses.clear(); + } + + validClasses.add(new WeakReference>(exceptionClass)); + } + } + + /** + * Returns the ClassValue-using validator, or falls back to the "weak Set" implementation if + * unable to do so. + */ + static GetCheckedTypeValidator getBestValidator() { + try { + Class theClass = + Class.forName(CLASS_VALUE_VALIDATOR_NAME).asSubclass(Enum.class); + return (GetCheckedTypeValidator) theClass.getEnumConstants()[0]; + } catch (ClassNotFoundException + | RuntimeException + | Error t) { // ensure we really catch *everything* + return weakSetValidator(); + } + } + } + + // TODO(cpovirk): change parameter order to match other helper methods (Class, Throwable)? + private static void wrapAndThrowExceptionOrError( + Throwable cause, Class exceptionClass) throws X { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } + if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } + throw newWithCause(exceptionClass, cause); + } + + /* + * TODO(user): FutureChecker interface for these to be static methods on? If so, refer to it in + * the (static-method) Futures.getChecked documentation + */ + + private static boolean hasConstructorUsableByGetChecked( + Class exceptionClass) { + try { + Exception unused = newWithCause(exceptionClass, new Exception()); + return true; + } catch (RuntimeException | Error e) { + return false; + } + } + + private static X newWithCause(Class exceptionClass, Throwable cause) { + // getConstructors() guarantees this as long as we don't modify the array. + @SuppressWarnings({"unchecked", "rawtypes"}) + List> constructors = (List) Arrays.asList(exceptionClass.getConstructors()); + for (Constructor constructor : preferringStrings(constructors)) { + X instance = newFromConstructor(constructor, cause); + if (instance != null) { + if (instance.getCause() == null) { + instance.initCause(cause); + } + return instance; + } + } + throw new IllegalArgumentException( + "No appropriate constructor for exception of type " + + exceptionClass + + " in response to chained exception", + cause); + } + + private static List> preferringStrings( + List> constructors) { + return WITH_STRING_PARAM_FIRST.sortedCopy(constructors); + } + + private static final Ordering> WITH_STRING_PARAM_FIRST = + Ordering.natural() + .onResultOf( + (Function, Boolean>) + input -> asList(input.getParameterTypes()).contains(String.class)) + .reverse(); + + private static X newFromConstructor(Constructor constructor, Throwable cause) { + Class[] paramTypes = constructor.getParameterTypes(); + Object[] params = new Object[paramTypes.length]; + for (int i = 0; i < paramTypes.length; i++) { + Class paramType = paramTypes[i]; + if (paramType.equals(String.class)) { + params[i] = cause.toString(); + } else if (paramType.equals(Throwable.class)) { + params[i] = cause; + } else { + return null; + } + } + try { + return constructor.newInstance(params); + } catch (IllegalArgumentException + | InstantiationException + | IllegalAccessException + | InvocationTargetException e) { + return null; + } + } + + @VisibleForTesting + static boolean isCheckedException(Class type) { + return !RuntimeException.class.isAssignableFrom(type); + } + + @VisibleForTesting + static void checkExceptionClassValidity(Class exceptionClass) { + checkArgument( + isCheckedException(exceptionClass), + "Futures.getChecked exception type (%s) must not be a RuntimeException", + exceptionClass); + checkArgument( + hasConstructorUsableByGetChecked(exceptionClass), + "Futures.getChecked exception type (%s) must be an accessible class with an accessible " + + "constructor whose parameters (if any) must be of type String and/or Throwable", + exceptionClass); + } + + private FuturesGetChecked() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java new file mode 100644 index 0000000000000..ad9dea5c40567 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/GwtFluentFutureCatchingSpecialization.java @@ -0,0 +1,39 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Hidden superclass of {@link FluentFuture} that provides us a place to declare special GWT + * versions of the {@link FluentFuture#catching(Class, org.apache.iotdb.google.common.base.Function) + * FluentFuture.catching} family of methods. Those versions have slightly different signatures. + */ +@GwtCompatible(emulated = true) +@J2ktIncompatible // Super-sourced +@ElementTypesAreNonnullByDefault +abstract class GwtFluentFutureCatchingSpecialization extends AbstractFuture { + /* + * This server copy of the class is empty. The corresponding GWT copy contains alternative + * versions of catching() and catchingAsync() with slightly different signatures from the ones + * found in FluentFuture.java. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java new file mode 100644 index 0000000000000..d8a41ba36f5f9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/GwtFuturesCatchingSpecialization.java @@ -0,0 +1,40 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Hidden superclass of {@link Futures} that provides us a place to declare special GWT versions of + * the {@link Futures#catching(ListenableFuture, Class, + * org.apache.iotdb.google.common.base.Function, java.util.concurrent.Executor) Futures.catching} + * family of methods. Those versions have slightly different signatures. + */ +@GwtCompatible(emulated = true) +@J2ktIncompatible // Super-sourced +@ElementTypesAreNonnullByDefault +abstract class GwtFuturesCatchingSpecialization { + /* + * This server copy of the class is empty. The corresponding GWT copy contains alternative + * versions of catching() and catchingAsync() with slightly different signatures from the ones + * found in Futures.java. + */ +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ImmediateFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ImmediateFuture.java new file mode 100644 index 0000000000000..d56733b06ec52 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ImmediateFuture.java @@ -0,0 +1,113 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.util.concurrent.AbstractFuture.TrustedFuture; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** Implementation of {@link Futures#immediateFuture}. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +// TODO(cpovirk): Make this final (but that may break Mockito spy calls). +class ImmediateFuture implements ListenableFuture { + static final ListenableFuture NULL = new ImmediateFuture(null); + + private static final Logger log = Logger.getLogger(ImmediateFuture.class.getName()); + + @ParametricNullness private final V value; + + ImmediateFuture(@ParametricNullness V value) { + this.value = value; + } + + @Override + public void addListener(Runnable listener, Executor executor) { + checkNotNull(listener, "Runnable was null."); + checkNotNull(executor, "Executor was null."); + try { + executor.execute(listener); + } catch (RuntimeException e) { + // ListenableFuture's contract is that it will not throw unchecked exceptions, so log the bad + // runnable and/or executor and swallow it. + log.log( + Level.SEVERE, + "RuntimeException while executing runnable " + listener + " with executor " + executor, + e); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + // TODO(lukes): Consider throwing InterruptedException when appropriate. + @Override + @ParametricNullness + public V get() { + return value; + } + + @Override + @ParametricNullness + public V get(long timeout, TimeUnit unit) throws ExecutionException { + checkNotNull(unit); + return get(); + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public String toString() { + // Behaviour analogous to AbstractFuture#toString(). + return super.toString() + "[status=SUCCESS, result=[" + value + "]]"; + } + + static final class ImmediateFailedFuture extends TrustedFuture { + ImmediateFailedFuture(Throwable thrown) { + setException(thrown); + } + } + + static final class ImmediateCancelledFuture extends TrustedFuture { + static final ImmediateCancelledFuture INSTANCE = + AbstractFuture.GENERATE_CANCELLATION_CAUSES ? null : new ImmediateCancelledFuture<>(); + + ImmediateCancelledFuture() { + cancel(false); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Internal.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Internal.java new file mode 100644 index 0000000000000..9ead50c47c783 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Internal.java @@ -0,0 +1,51 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; + +/** This class is for {@code org.apache.iotdb.google.common.util.concurrent} use only! */ +@J2ktIncompatible +@GwtIncompatible // java.time.Duration +@ElementTypesAreNonnullByDefault +final class Internal { + + /** + * Returns the number of nanoseconds of the given duration without throwing or overflowing. + * + *

Instead of throwing {@link ArithmeticException}, this method silently saturates to either + * {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This behavior can be useful when decomposing + * a duration in order to call a legacy API which requires a {@code long, TimeUnit} pair. + */ + static long toNanosSaturated(Duration duration) { + // Using a try/catch seems lazy, but the catch block will rarely get invoked (except for + // durations longer than approximately +/- 292 years). + try { + return duration.toNanos(); + } catch (ArithmeticException tooBig) { + return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE; + } + } + + private Internal() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/InterruptibleTask.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/InterruptibleTask.java new file mode 100644 index 0000000000000..57014c50d333a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/InterruptibleTask.java @@ -0,0 +1,269 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.AbstractOwnableSynchronizer; +import java.util.concurrent.locks.LockSupport; + +import static org.apache.iotdb.google.common.util.concurrent.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +// Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause +// getDeclaredField to throw a NoSuchFieldException when the field is definitely there. +// Since this class only needs CAS on one field, we can avoid this bug by extending AtomicReference +// instead of using an AtomicReferenceFieldUpdater. This reference stores Thread instances +// and DONE/INTERRUPTED - they have a common ancestor of Runnable. +abstract class InterruptibleTask extends AtomicReference + implements Runnable { + static { + // Prevent rare disastrous classloading in first call to LockSupport.park. + // See: https://bugs.openjdk.java.net/browse/JDK-8074773 + @SuppressWarnings("unused") + Class ensureLoaded = LockSupport.class; + } + + private static final class DoNothingRunnable implements Runnable { + @Override + public void run() {} + } + + // The thread executing the task publishes itself to the superclass' reference and the thread + // interrupting sets DONE when it has finished interrupting. + private static final Runnable DONE = new DoNothingRunnable(); + private static final Runnable PARKED = new DoNothingRunnable(); + // Why 1000? WHY NOT! + private static final int MAX_BUSY_WAIT_SPINS = 1000; + + @SuppressWarnings("ThreadPriorityCheck") // The cow told me to + @Override + public final void run() { + /* + * Set runner thread before checking isDone(). If we were to check isDone() first, the task + * might be cancelled before we set the runner thread. That would make it impossible to + * interrupt, yet it will still run, since interruptTask will leave the runner value null, + * allowing the CAS below to succeed. + */ + Thread currentThread = Thread.currentThread(); + if (!compareAndSet(null, currentThread)) { + return; // someone else has run or is running. + } + + boolean run = !isDone(); + T result = null; + Throwable error = null; + try { + if (run) { + result = runInterruptibly(); + } + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + error = t; + } finally { + // Attempt to set the task as done so that further attempts to interrupt will fail. + if (!compareAndSet(currentThread, DONE)) { + waitForInterrupt(currentThread); + } + if (run) { + if (error == null) { + // The cast is safe because of the `run` and `error` checks. + afterRanInterruptiblySuccess(uncheckedCastNullableTToT(result)); + } else { + afterRanInterruptiblyFailure(error); + } + } + } + } + + private void waitForInterrupt(Thread currentThread) { + /* + * If someone called cancel(true), it is possible that the interrupted bit hasn't been set yet. + * Wait for the interrupting thread to set DONE. (See interruptTask().) We want to wait so that + * the interrupting thread doesn't interrupt the _next_ thing to run on this thread. + * + * Note: We don't reset the interrupted bit, just wait for it to be set. If this is a thread + * pool thread, the thread pool will reset it for us. Otherwise, the interrupted bit may have + * been intended for something else, so don't clear it. + */ + boolean restoreInterruptedBit = false; + int spinCount = 0; + // Interrupting Cow Says: + // ______ + // < Spin > + // ------ + // \ ^__^ + // \ (oo)\_______ + // (__)\ )\/\ + // ||----w | + // || || + Runnable state = get(); + Blocker blocker = null; + while (state instanceof Blocker || state == PARKED) { + if (state instanceof Blocker) { + blocker = (Blocker) state; + } + spinCount++; + if (spinCount > MAX_BUSY_WAIT_SPINS) { + /* + * If we have spun a lot, just park ourselves. This will save CPU while we wait for a slow + * interrupting thread. In theory, interruptTask() should be very fast, but due to + * InterruptibleChannel and JavaLangAccess.blockedOn(Thread, Interruptible), it isn't + * predictable what work might be done. (e.g., close a file and flush buffers to disk). To + * protect ourselves from this, we park ourselves and tell our interrupter that we did so. + */ + if (state == PARKED || compareAndSet(state, PARKED)) { + // Interrupting Cow Says: + // ______ + // < Park > + // ------ + // \ ^__^ + // \ (oo)\_______ + // (__)\ )\/\ + // ||----w | + // || || + // We need to clear the interrupted bit prior to calling park and maintain it in case we + // wake up spuriously. + restoreInterruptedBit = Thread.interrupted() || restoreInterruptedBit; + LockSupport.park(blocker); + } + } else { + Thread.yield(); + } + state = get(); + } + if (restoreInterruptedBit) { + currentThread.interrupt(); + } + /* + * TODO(cpovirk): Clear interrupt status here? We currently don't, which means that an interrupt + * before, during, or after runInterruptibly() (unless it produced an InterruptedException + * caught above) can linger and affect listeners. + */ + } + + /** + * Called before runInterruptibly - if true, runInterruptibly and afterRanInterruptibly will not + * be called. + */ + abstract boolean isDone(); + + /** + * Do interruptible work here - do not complete Futures here, as their listeners could be + * interrupted. + */ + @ParametricNullness + abstract T runInterruptibly() throws Exception; + + /** + * Any interruption that happens as a result of calling interruptTask will arrive before this + * method is called. Complete Futures here. + */ + abstract void afterRanInterruptiblySuccess(@ParametricNullness T result); + + /** + * Any interruption that happens as a result of calling interruptTask will arrive before this + * method is called. Complete Futures here. + */ + abstract void afterRanInterruptiblyFailure(Throwable error); + + /** + * Interrupts the running task. Because this internally calls {@link Thread#interrupt()} which can + * in turn invoke arbitrary code it is not safe to call while holding a lock. + */ + final void interruptTask() { + // Since the Thread is replaced by DONE before run() invokes listeners or returns, if we succeed + // in this CAS, there's no risk of interrupting the wrong thread or interrupting a thread that + // isn't currently executing this task. + Runnable currentRunner = get(); + if (currentRunner instanceof Thread) { + Blocker blocker = new Blocker(this); + blocker.setOwner(Thread.currentThread()); + if (compareAndSet(currentRunner, blocker)) { + // Thread.interrupt can throw arbitrary exceptions due to the nio InterruptibleChannel API + // This will make sure that tasks don't get stuck busy waiting. + // Some of this is fixed in jdk11 (see https://bugs.openjdk.java.net/browse/JDK-8198692) but + // not all. See the test cases for examples on how this can happen. + try { + ((Thread) currentRunner).interrupt(); + } finally { + Runnable prev = getAndSet(DONE); + if (prev == PARKED) { + LockSupport.unpark((Thread) currentRunner); + } + } + } + } + } + + /** + * Using this as the blocker object allows introspection and debugging tools to see that the + * currentRunner thread is blocked on the progress of the interruptor thread, which can help + * identify deadlocks. + */ + @VisibleForTesting + static final class Blocker extends AbstractOwnableSynchronizer implements Runnable { + private final InterruptibleTask task; + + private Blocker(InterruptibleTask task) { + this.task = task; + } + + @Override + public void run() {} + + private void setOwner(Thread thread) { + super.setExclusiveOwnerThread(thread); + } + + @VisibleForTesting + Thread getOwner() { + return super.getExclusiveOwnerThread(); + } + + @Override + public String toString() { + return task.toString(); + } + } + + @Override + public final String toString() { + Runnable state = get(); + String result; + if (state == DONE) { + result = "running=[DONE]"; + } else if (state instanceof Blocker) { + result = "running=[INTERRUPTED]"; + } else if (state instanceof Thread) { + // getName is final on Thread, no need to worry about exceptions + result = "running=[RUNNING ON " + ((Thread) state).getName() + "]"; + } else { + result = "running=[NOT STARTED YET]"; + } + return result + ", " + toPendingString(); + } + + abstract String toPendingString(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/JdkFutureAdapters.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/JdkFutureAdapters.java new file mode 100644 index 0000000000000..37d4857c93588 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/JdkFutureAdapters.java @@ -0,0 +1,181 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; + +/** + * Utilities necessary for working with libraries that supply plain {@link Future} instances. Note + * that, whenever possible, it is strongly preferred to modify those libraries to return {@code + * ListenableFuture} directly. + * + *

For interoperability between {@code ListenableFuture} and {@code CompletableFuture}, + * consider Future Converter. + * + * @author Sven Mawson + * @since 10.0 (replacing {@code Futures.makeListenable}, which existed in 1.0) + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class JdkFutureAdapters { + /** + * Assigns a thread to the given {@link Future} to provide {@link ListenableFuture} functionality. + * + *

Warning: If the input future does not already implement {@code ListenableFuture}, the + * returned future will emulate {@link ListenableFuture#addListener} by taking a thread from an + * internal, unbounded pool at the first call to {@code addListener} and holding it until the + * future is {@linkplain Future#isDone() done}. + * + *

Prefer to create {@code ListenableFuture} instances with {@link SettableFuture}, {@link + * MoreExecutors#listeningDecorator( java.util.concurrent.ExecutorService)}, {@link + * ListenableFutureTask}, {@link AbstractFuture}, and other utilities over creating plain {@code + * Future} instances to be upgraded to {@code ListenableFuture} after the fact. + */ + public static ListenableFuture listenInPoolThread(Future future) { + if (future instanceof ListenableFuture) { + return (ListenableFuture) future; + } + return new ListenableFutureAdapter<>(future); + } + + /** + * Submits a blocking task for the given {@link Future} to provide {@link ListenableFuture} + * functionality. + * + *

Warning: If the input future does not already implement {@code ListenableFuture}, the + * returned future will emulate {@link ListenableFuture#addListener} by submitting a task to the + * given executor at the first call to {@code addListener}. The task must be started by the + * executor promptly, or else the returned {@code ListenableFuture} may fail to work. The task's + * execution consists of blocking until the input future is {@linkplain Future#isDone() done}, so + * each call to this method may claim and hold a thread for an arbitrary length of time. Use of + * bounded executors or other executors that may fail to execute a task promptly may result in + * deadlocks. + * + *

Prefer to create {@code ListenableFuture} instances with {@link SettableFuture}, {@link + * MoreExecutors#listeningDecorator( java.util.concurrent.ExecutorService)}, {@link + * ListenableFutureTask}, {@link AbstractFuture}, and other utilities over creating plain {@code + * Future} instances to be upgraded to {@code ListenableFuture} after the fact. + * + * @since 12.0 + */ + public static ListenableFuture listenInPoolThread( + Future future, Executor executor) { + checkNotNull(executor); + if (future instanceof ListenableFuture) { + return (ListenableFuture) future; + } + return new ListenableFutureAdapter<>(future, executor); + } + + /** + * An adapter to turn a {@link Future} into a {@link ListenableFuture}. This will wait on the + * future to finish, and when it completes, run the listeners. This implementation will wait on + * the source future indefinitely, so if the source future never completes, the adapter will never + * complete either. + * + *

If the delegate future is interrupted or throws an unexpected unchecked exception, the + * listeners will not be invoked. + */ + private static class ListenableFutureAdapter extends ForwardingFuture + implements ListenableFuture { + + private static final ThreadFactory threadFactory = + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("ListenableFutureAdapter-thread-%d") + .build(); + private static final Executor defaultAdapterExecutor = + Executors.newCachedThreadPool(threadFactory); + + private final Executor adapterExecutor; + + // The execution list to hold our listeners. + private final ExecutionList executionList = new ExecutionList(); + + // This allows us to only start up a thread waiting on the delegate future when the first + // listener is added. + private final AtomicBoolean hasListeners = new AtomicBoolean(false); + + // The delegate future. + private final Future delegate; + + ListenableFutureAdapter(Future delegate) { + this(delegate, defaultAdapterExecutor); + } + + ListenableFutureAdapter(Future delegate, Executor adapterExecutor) { + this.delegate = checkNotNull(delegate); + this.adapterExecutor = checkNotNull(adapterExecutor); + } + + @Override + protected Future delegate() { + return delegate; + } + + @Override + public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); + + // When a listener is first added, we run a task that will wait for the delegate to finish, + // and when it is done will run the listeners. + if (hasListeners.compareAndSet(false, true)) { + if (delegate.isDone()) { + // If the delegate is already done, run the execution list immediately on the current + // thread. + executionList.execute(); + return; + } + + // TODO(lukes): handle RejectedExecutionException + adapterExecutor.execute( + () -> { + try { + /* + * Threads from our private pool are never interrupted. Threads from a + * user-supplied executor might be, but... what can we do? This is another reason + * to return a proper ListenableFuture instead of using listenInPoolThread. + */ + getUninterruptibly(delegate); + } catch (ExecutionException | RuntimeException | Error e) { + // (including CancellationException) + // The task is presumably done, run the listeners. + // TODO(cpovirk): Do *something* in case of Error (and maybe RuntimeException)? + } + executionList.execute(); + }); + } + } + } + + private JdkFutureAdapters() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableFuture.java new file mode 100644 index 0000000000000..5ef6dd8db7e04 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableFuture.java @@ -0,0 +1,161 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; + +/** + * A {@link Future} that accepts completion listeners. Each listener has an associated executor, and + * it is invoked using this executor once the future's computation is {@linkplain Future#isDone() + * complete}. If the computation has already completed when the listener is added, the listener will + * execute immediately. + * + *

See the Guava User Guide article on {@code + * ListenableFuture}. + * + *

This class is GWT-compatible. + * + *

Purpose

+ * + *

The main purpose of {@code ListenableFuture} is to help you chain together a graph of + * asynchronous operations. You can chain them together manually with calls to methods like {@link + * Futures#transform(ListenableFuture, org.apache.iotdb.google.common.base.Function, Executor) + * Futures.transform} (or {@link + * FluentFuture#transform(org.apache.iotdb.google.common.base.Function, Executor) + * FluentFuture.transform}), but you will often find it easier to use a framework. Frameworks + * automate the process, often adding features like monitoring, debugging, and cancellation. + * Examples of frameworks include: + * + *

+ * + *

The main purpose of {@link #addListener addListener} is to support this chaining. You will + * rarely use it directly, in part because it does not provide direct access to the {@code Future} + * result. (If you want such access, you may prefer {@link Futures#addCallback + * Futures.addCallback}.) Still, direct {@code addListener} calls are occasionally useful: + * + *

{@code
+ * final String name = ...;
+ * inFlight.add(name);
+ * ListenableFuture future = service.query(name);
+ * future.addListener(new Runnable() {
+ *   public void run() {
+ *     processedCount.incrementAndGet();
+ *     inFlight.remove(name);
+ *     lastProcessed.set(name);
+ *     logger.info("Done with {0}", name);
+ *   }
+ * }, executor);
+ * }
+ * + *

How to get an instance

+ * + *

We encourage you to return {@code ListenableFuture} from your methods so that your users can + * take advantage of the {@linkplain Futures utilities built atop the class}. The way that you will + * create {@code ListenableFuture} instances depends on how you currently create {@code Future} + * instances: + * + *

    + *
  • If you receive them from an {@code java.util.concurrent.ExecutorService}, convert that + * service to a {@link ListeningExecutorService}, usually by calling {@link + * MoreExecutors#listeningDecorator(java.util.concurrent.ExecutorService) + * MoreExecutors.listeningDecorator}. + *
  • If you manually call {@link java.util.concurrent.FutureTask#set} or a similar method, + * create a {@link SettableFuture} instead. (If your needs are more complex, you may prefer + * {@link AbstractFuture}.) + *
+ * + *

Test doubles: If you need a {@code ListenableFuture} for your test, try a {@link + * SettableFuture} or one of the methods in the {@link Futures#immediateFuture Futures.immediate*} + * family. Avoid creating a mock or stub {@code Future}. Mock and stub implementations are + * fragile because they assume that only certain methods will be called and because they often + * implement subtleties of the API improperly. + * + *

Custom implementation: Avoid implementing {@code ListenableFuture} from scratch. If you + * can't get by with the standard implementations, prefer to derive a new {@code Future} instance + * with the methods in {@link Futures} or, if necessary, to extend {@link AbstractFuture}. + * + *

Occasionally, an API will return a plain {@code Future} and it will be impossible to change + * the return type. For this case, we provide a more expensive workaround in {@code + * JdkFutureAdapters}. However, when possible, it is more efficient and reliable to create a {@code + * ListenableFuture} directly. + * + * @author Sven Mawson + * @author Nishant Thakkar + * @since 1.0 + */ +/* + * Some of the annotations below were added after we released our separate + * com.google.guava:listenablefuture:1.0 artifact. (For more on that artifact, see + * https://github.com/google/guava/releases/tag/v27.0) This means that the copy of ListenableFuture + * in com.google.guava:guava differs from the "frozen" copy in the listenablefuture artifact. This + * could in principle cause problems for some users. Still, we expect that the benefits of the + * nullness annotations in particular will outweigh the costs. (And it's worth noting that we have + * released multiple ListenableFuture.class files that are not byte-for-byte compatible even from + * the beginning, thanks to using different `-source -target` values for compiling our `-jre` and + * `-android` "flavors.") + * + * (We could consider releasing a listenablefuture:1.0.1 someday. But we would want to look into how + * that affects users, especially users of the Android Gradle Plugin, since the plugin developers + * put in a special hack for us: https://issuetracker.google.com/issues/131431257) + */ + +@ElementTypesAreNonnullByDefault +public interface ListenableFuture extends Future { + /** + * Registers a listener to be {@linkplain Executor#execute(Runnable) run} on the given executor. + * The listener will run when the {@code Future}'s computation is {@linkplain Future#isDone() + * complete} or, if the computation is already complete, immediately. + * + *

There is no guaranteed ordering of execution of listeners, but any listener added through + * this method is guaranteed to be called once the computation is complete. + * + *

Exceptions thrown by a listener will be propagated up to the executor. Any exception thrown + * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an exception + * thrown by {@linkplain MoreExecutors#directExecutor direct execution}) will be caught and + * logged. + * + *

Note: If your listener is lightweight -- and will not cause stack overflow by completing + * more futures or adding more {@code directExecutor()} listeners inline -- consider {@link + * MoreExecutors#directExecutor}. Otherwise, avoid it: See the warnings on the docs for {@code + * directExecutor}. + * + *

This is the most general listener interface. For common operations performed using + * listeners, see {@link Futures}. For a simplified but general listener interface, see {@link + * Futures#addCallback addCallback()}. + * + *

Memory consistency effects: Actions in a thread prior to adding a listener + * happen-before its execution begins, perhaps in another thread. + * + *

Guava implementations of {@code ListenableFuture} promptly release references to listeners + * after executing them. + * + * @param listener the listener to run when the computation is complete + * @param executor the executor to run the listener in + * @throws RejectedExecutionException if we tried to execute the listener immediately but the + * executor rejected it. + */ + void addListener(Runnable listener, Executor executor); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableFutureTask.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableFutureTask.java new file mode 100644 index 0000000000000..a30071850850e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableFutureTask.java @@ -0,0 +1,118 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.lang.Math.min; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +/** + * A {@link FutureTask} that also implements the {@link ListenableFuture} interface. Unlike {@code + * FutureTask}, {@code ListenableFutureTask} does not provide an overrideable {@link + * FutureTask#done() done()} method. For similar functionality, call {@link #addListener}. + * + *

Few users should use this class. It is intended primarily for those who are implementing an + * {@code ExecutorService}. Most users should call {@link ListeningExecutorService#submit(Callable) + * ListeningExecutorService.submit} on a service obtained from {@link + * MoreExecutors#listeningDecorator}. + * + * @author Sven Mawson + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public class ListenableFutureTask extends FutureTask + implements ListenableFuture { + // TODO(cpovirk): explore ways of making ListenableFutureTask final. There are some valid reasons + // such as BoundedQueueExecutorService to allow extends but it would be nice to make it final to + // avoid unintended usage. + + // The execution list to hold our listeners. + private final ExecutionList executionList = new ExecutionList(); + + /** + * Creates a {@code ListenableFutureTask} that will upon running, execute the given {@code + * Callable}. + * + * @param callable the callable task + * @since 10.0 + */ + public static ListenableFutureTask create(Callable callable) { + return new ListenableFutureTask<>(callable); + } + + /** + * Creates a {@code ListenableFutureTask} that will upon running, execute the given {@code + * Runnable}, and arrange that {@code get} will return the given result on successful completion. + * + * @param runnable the runnable task + * @param result the result to return on successful completion. If you don't need a particular + * result, consider using constructions of the form: {@code ListenableFuture f = + * ListenableFutureTask.create(runnable, null)} + * @since 10.0 + */ + public static ListenableFutureTask create( + Runnable runnable, @ParametricNullness V result) { + return new ListenableFutureTask<>(runnable, result); + } + + ListenableFutureTask(Callable callable) { + super(callable); + } + + ListenableFutureTask(Runnable runnable, @ParametricNullness V result) { + super(runnable, result); + } + + @Override + public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); + } + + @Override + @ParametricNullness + public V get(long timeout, TimeUnit unit) + throws TimeoutException, InterruptedException, ExecutionException { + + long timeoutNanos = unit.toNanos(timeout); + if (timeoutNanos <= OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD) { + return super.get(timeout, unit); + } + // Waiting 68 years should be enough for any program. + return super.get( + min(timeoutNanos, OverflowAvoidingLockSupport.MAX_NANOSECONDS_THRESHOLD), NANOSECONDS); + } + + /** Internal implementation detail used to invoke the listeners. */ + @Override + protected void done() { + executionList.execute(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableScheduledFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableScheduledFuture.java new file mode 100644 index 0000000000000..cd8b8eae1a354 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenableScheduledFuture.java @@ -0,0 +1,35 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.ScheduledFuture; + +/** + * Helper interface to implement both {@link ListenableFuture} and {@link ScheduledFuture}. + * + * @author Anthony Zana + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public interface ListenableScheduledFuture + extends ScheduledFuture, ListenableFuture {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenerCallQueue.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenerCallQueue.java new file mode 100644 index 0000000000000..c4b18a5eff84d --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListenerCallQueue.java @@ -0,0 +1,230 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.collect.Queues; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A list of listeners for implementing a concurrency friendly observable object. + * + *

Listeners are registered once via {@link #addListener} and then may be invoked by {@linkplain + * #enqueue enqueueing} and then {@linkplain #dispatch dispatching} events. + * + *

The API of this class is designed to make it easy to achieve the following properties + * + *

    + *
  • Multiple events for the same listener are never dispatched concurrently. + *
  • Events for the different listeners are dispatched concurrently. + *
  • All events for a given listener dispatch on the provided {@link #executor}. + *
  • It is easy for the user to ensure that listeners are never invoked while holding locks. + *
+ * + * The last point is subtle. Often the observable object will be managing its own internal state + * using a lock, however it is dangerous to dispatch listeners while holding a lock because they + * might run on the {@code directExecutor()} or be otherwise re-entrant (call back into your + * object). So it is important to not call {@link #dispatch} while holding any locks. This is why + * {@link #enqueue} and {@link #dispatch} are 2 different methods. It is expected that the decision + * to run a particular event is made during the state change, but the decision to actually invoke + * the listeners can be delayed slightly so that locks can be dropped. Also, because {@link + * #dispatch} is expected to be called concurrently, it is idempotent. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class ListenerCallQueue { + // TODO(cpovirk): consider using the logger associated with listener.getClass(). + private static final Logger logger = Logger.getLogger(ListenerCallQueue.class.getName()); + + // TODO(chrisn): promote AppendOnlyCollection for use here. + private final List> listeners = + Collections.synchronizedList(new ArrayList>()); + + /** Method reference-compatible listener event. */ + interface Event { + /** Call a method on the listener. */ + void call(L listener); + } + + /** + * Adds a listener that will be called using the given executor when events are later {@link + * #enqueue enqueued} and {@link #dispatch dispatched}. + */ + public void addListener(L listener, Executor executor) { + checkNotNull(listener, "listener"); + checkNotNull(executor, "executor"); + listeners.add(new PerListenerQueue<>(listener, executor)); + } + + /** + * Enqueues an event to be run on currently known listeners. + * + *

The {@code toString} method of the Event itself will be used to describe the event in the + * case of an error. + * + * @param event the callback to execute on {@link #dispatch} + */ + public void enqueue(Event event) { + enqueueHelper(event, event); + } + + /** + * Enqueues an event to be run on currently known listeners, with a label. + * + * @param event the callback to execute on {@link #dispatch} + * @param label a description of the event to use in the case of an error + */ + public void enqueue(Event event, String label) { + enqueueHelper(event, label); + } + + private void enqueueHelper(Event event, Object label) { + checkNotNull(event, "event"); + checkNotNull(label, "label"); + synchronized (listeners) { + for (PerListenerQueue queue : listeners) { + queue.add(event, label); + } + } + } + + /** + * Dispatches all events enqueued prior to this call, serially and in order, for every listener. + * + *

Note: this method is idempotent and safe to call from any thread + */ + public void dispatch() { + // iterate by index to avoid concurrent modification exceptions + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).dispatch(); + } + } + + /** + * A special purpose queue/executor that dispatches listener events serially on a configured + * executor. Each event can be added and dispatched as separate phases. + * + *

This class is very similar to {@link SequentialExecutor} with the exception that events can + * be added without necessarily executing immediately. + */ + private static final class PerListenerQueue implements Runnable { + final L listener; + final Executor executor; + + final Queue> waitQueue = Queues.newArrayDeque(); + + final Queue labelQueue = Queues.newArrayDeque(); + + boolean isThreadScheduled; + + PerListenerQueue(L listener, Executor executor) { + this.listener = checkNotNull(listener); + this.executor = checkNotNull(executor); + } + + /** Enqueues an event to be run. */ + synchronized void add(ListenerCallQueue.Event event, Object label) { + waitQueue.add(event); + labelQueue.add(label); + } + + /** + * Dispatches all listeners {@linkplain #enqueue enqueued} prior to this call, serially and in + * order. + */ + void dispatch() { + boolean scheduleEventRunner = false; + synchronized (this) { + if (!isThreadScheduled) { + isThreadScheduled = true; + scheduleEventRunner = true; + } + } + if (scheduleEventRunner) { + try { + executor.execute(this); + } catch (RuntimeException e) { + // reset state in case of an error so that later dispatch calls will actually do something + synchronized (this) { + isThreadScheduled = false; + } + // Log it and keep going. + logger.log( + Level.SEVERE, + "Exception while running callbacks for " + listener + " on " + executor, + e); + throw e; + } + } + } + + @Override + public void run() { + boolean stillRunning = true; + try { + while (true) { + ListenerCallQueue.Event nextToRun; + Object nextLabel; + synchronized (PerListenerQueue.this) { + Preconditions.checkState(isThreadScheduled); + nextToRun = waitQueue.poll(); + nextLabel = labelQueue.poll(); + if (nextToRun == null) { + isThreadScheduled = false; + stillRunning = false; + break; + } + } + + // Always run while _not_ holding the lock, to avoid deadlocks. + try { + nextToRun.call(listener); + } catch (RuntimeException e) { + // Log it and keep going. + logger.log( + Level.SEVERE, + "Exception while executing callback: " + listener + " " + nextLabel, + e); + } + } + } finally { + if (stillRunning) { + // An Error is bubbling up. We should mark ourselves as no longer running. That way, if + // anyone tries to keep using us, we won't be corrupted. + synchronized (PerListenerQueue.this) { + isThreadScheduled = false; + } + } + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListeningExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListeningExecutorService.java new file mode 100644 index 0000000000000..0d6416f689978 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListeningExecutorService.java @@ -0,0 +1,147 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * An {@link ExecutorService} that returns {@link ListenableFuture} instances. To create an instance + * from an existing {@link ExecutorService}, call {@link + * MoreExecutors#listeningDecorator(ExecutorService)}. + * + * @author Chris Povirk + * @since 10.0 + */ +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface ListeningExecutorService extends ExecutorService { + /** + * @return a {@code ListenableFuture} representing pending completion of the task + * @throws RejectedExecutionException {@inheritDoc} + */ + @Override + ListenableFuture submit(Callable task); + + /** + * @return a {@code ListenableFuture} representing pending completion of the task + * @throws RejectedExecutionException {@inheritDoc} + */ + @Override + ListenableFuture submit(Runnable task); + + /** + * @return a {@code ListenableFuture} representing pending completion of the task + * @throws RejectedExecutionException {@inheritDoc} + */ + @Override + ListenableFuture submit(Runnable task, @ParametricNullness T result); + + /** + * {@inheritDoc} + * + *

All elements in the returned list must be {@link ListenableFuture} instances. The easiest + * way to obtain a {@code List>} from this method is an unchecked (but safe) + * cast: + * + *

+   *   {@code @SuppressWarnings("unchecked") // guaranteed by invokeAll contract}
+   *   {@code List> futures = (List) executor.invokeAll(tasks);}
+   * 
+ * + * @return A list of {@code ListenableFuture} instances representing the tasks, in the same + * sequential order as produced by the iterator for the given task list, each of which has + * completed. + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException if any task is null + */ + @Override + List> invokeAll(Collection> tasks) + throws InterruptedException; + + /** + * {@inheritDoc} + * + *

All elements in the returned list must be {@link ListenableFuture} instances. The easiest + * way to obtain a {@code List>} from this method is an unchecked (but safe) + * cast: + * + *

+   *   {@code @SuppressWarnings("unchecked") // guaranteed by invokeAll contract}
+   *   {@code List> futures = (List) executor.invokeAll(tasks, timeout, unit);}
+   * 
+ * + * @return a list of {@code ListenableFuture} instances representing the tasks, in the same + * sequential order as produced by the iterator for the given task list. If the operation did + * not time out, each task will have completed. If it did time out, some of these tasks will + * not have completed. + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException if any task is null + */ + @Override + List> invokeAll( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Duration-based overload of {@link #invokeAll(Collection, long, TimeUnit)}. + * + * @since 32.1.0 + */ + @J2ktIncompatible + default List> invokeAll( + Collection> tasks, Duration timeout) throws InterruptedException { + return invokeAll(tasks, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Duration-based overload of {@link #invokeAny(Collection, long, TimeUnit)}. + * + * @since 32.1.0 + */ + @J2ktIncompatible + default T invokeAny(Collection> tasks, Duration timeout) + throws InterruptedException, ExecutionException, TimeoutException { + return invokeAny(tasks, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Duration-based overload of {@link #awaitTermination(long, TimeUnit)}. + * + * @since 32.1.0 + */ + @J2ktIncompatible + default boolean awaitTermination(Duration timeout) throws InterruptedException { + return awaitTermination(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListeningScheduledExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListeningScheduledExecutorService.java new file mode 100644 index 0000000000000..908778e92e99c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ListeningScheduledExecutorService.java @@ -0,0 +1,114 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * A {@link ScheduledExecutorService} that returns {@link ListenableFuture} instances from its + * {@code ExecutorService} methods. To create an instance from an existing {@link + * ScheduledExecutorService}, call {@link + * MoreExecutors#listeningDecorator(ScheduledExecutorService)}. + * + * @author Chris Povirk + * @since 10.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface ListeningScheduledExecutorService + extends ScheduledExecutorService, ListeningExecutorService { + + /** + * @since 15.0 (previously returned ScheduledFuture) + */ + @Override + ListenableScheduledFuture schedule(Runnable command, long delay, TimeUnit unit); + + /** + * Duration-based overload of {@link #schedule(Runnable, long, TimeUnit)}. + * + * @since 29.0 + */ + default ListenableScheduledFuture schedule(Runnable command, Duration delay) { + return schedule(command, toNanosSaturated(delay), TimeUnit.NANOSECONDS); + } + + /** + * @since 15.0 (previously returned ScheduledFuture) + */ + @Override + ListenableScheduledFuture schedule( + Callable callable, long delay, TimeUnit unit); + + /** + * Duration-based overload of {@link #schedule(Callable, long, TimeUnit)}. + * + * @since 29.0 + */ + default ListenableScheduledFuture schedule( + Callable callable, Duration delay) { + return schedule(callable, toNanosSaturated(delay), TimeUnit.NANOSECONDS); + } + + /** + * @since 15.0 (previously returned ScheduledFuture) + */ + @Override + ListenableScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit); + + /** + * Duration-based overload of {@link #scheduleAtFixedRate(Runnable, long, long, TimeUnit)}. + * + * @since 29.0 + */ + default ListenableScheduledFuture scheduleAtFixedRate( + Runnable command, Duration initialDelay, Duration period) { + return scheduleAtFixedRate( + command, toNanosSaturated(initialDelay), toNanosSaturated(period), TimeUnit.NANOSECONDS); + } + + /** + * @since 15.0 (previously returned ScheduledFuture) + */ + @Override + ListenableScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit); + + /** + * Duration-based overload of {@link #scheduleWithFixedDelay(Runnable, long, long, TimeUnit)}. + * + * @since 29.0 + */ + default ListenableScheduledFuture scheduleWithFixedDelay( + Runnable command, Duration initialDelay, Duration delay) { + return scheduleWithFixedDelay( + command, toNanosSaturated(initialDelay), toNanosSaturated(delay), TimeUnit.NANOSECONDS); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Monitor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Monitor.java new file mode 100644 index 0000000000000..cab8c19213b19 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Monitor.java @@ -0,0 +1,1223 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.primitives.Longs; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BooleanSupplier; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * A synchronization abstraction supporting waiting on arbitrary boolean conditions. + * + *

This class is intended as a replacement for {@link ReentrantLock}. Code using {@code Monitor} + * is less error-prone and more readable than code using {@code ReentrantLock}, without significant + * performance loss. {@code Monitor} even has the potential for performance gain by optimizing the + * evaluation and signaling of conditions. Signaling is entirely implicit. By + * eliminating explicit signaling, this class can guarantee that only one thread is awakened when a + * condition becomes true (no "signaling storms" due to use of {@link + * java.util.concurrent.locks.Condition#signalAll Condition.signalAll}) and that no signals are lost + * (no "hangs" due to incorrect use of {@link java.util.concurrent.locks.Condition#signal + * Condition.signal}). + * + *

A thread is said to occupy a monitor if it has entered the monitor but not yet + * left. Only one thread may occupy a given monitor at any moment. A monitor is also + * reentrant, so a thread may enter a monitor any number of times, and then must leave the same + * number of times. The enter and leave operations have the same synchronization + * semantics as the built-in Java language synchronization primitives. + * + *

A call to any of the enter methods with void return type should always be + * followed immediately by a try/finally block to ensure that the current thread leaves the + * monitor cleanly: + * + *

{@code
+ * monitor.enter();
+ * try {
+ *   // do things while occupying the monitor
+ * } finally {
+ *   monitor.leave();
+ * }
+ * }
+ * + *

A call to any of the enter methods with boolean return type should always appear + * as the condition of an if statement containing a try/finally block to ensure that + * the current thread leaves the monitor cleanly: + * + *

{@code
+ * if (monitor.tryEnter()) {
+ *   try {
+ *     // do things while occupying the monitor
+ *   } finally {
+ *     monitor.leave();
+ *   }
+ * } else {
+ *   // do other things since the monitor was not available
+ * }
+ * }
+ * + *

Comparison with {@code synchronized} and {@code ReentrantLock}

+ * + *

The following examples show a simple threadsafe holder expressed using {@code synchronized}, + * {@link ReentrantLock}, and {@code Monitor}. + * + *

{@code synchronized}

+ * + *

This version is the fewest lines of code, largely because the synchronization mechanism used + * is built into the language and runtime. But the programmer has to remember to avoid a couple of + * common bugs: The {@code wait()} must be inside a {@code while} instead of an {@code if}, and + * {@code notifyAll()} must be used instead of {@code notify()} because there are two different + * logical conditions being awaited. + * + *

{@code
+ * public class SafeBox {
+ *   private V value;
+ *
+ *   public synchronized V get() throws InterruptedException {
+ *     while (value == null) {
+ *       wait();
+ *     }
+ *     V result = value;
+ *     value = null;
+ *     notifyAll();
+ *     return result;
+ *   }
+ *
+ *   public synchronized void set(V newValue) throws InterruptedException {
+ *     while (value != null) {
+ *       wait();
+ *     }
+ *     value = newValue;
+ *     notifyAll();
+ *   }
+ * }
+ * }
+ * + *

{@code ReentrantLock}

+ * + *

This version is much more verbose than the {@code synchronized} version, and still suffers + * from the need for the programmer to remember to use {@code while} instead of {@code if}. However, + * one advantage is that we can introduce two separate {@code Condition} objects, which allows us to + * use {@code signal()} instead of {@code signalAll()}, which may be a performance benefit. + * + *

{@code
+ * public class SafeBox {
+ *   private V value;
+ *   private final ReentrantLock lock = new ReentrantLock();
+ *   private final Condition valuePresent = lock.newCondition();
+ *   private final Condition valueAbsent = lock.newCondition();
+ *
+ *   public V get() throws InterruptedException {
+ *     lock.lock();
+ *     try {
+ *       while (value == null) {
+ *         valuePresent.await();
+ *       }
+ *       V result = value;
+ *       value = null;
+ *       valueAbsent.signal();
+ *       return result;
+ *     } finally {
+ *       lock.unlock();
+ *     }
+ *   }
+ *
+ *   public void set(V newValue) throws InterruptedException {
+ *     lock.lock();
+ *     try {
+ *       while (value != null) {
+ *         valueAbsent.await();
+ *       }
+ *       value = newValue;
+ *       valuePresent.signal();
+ *     } finally {
+ *       lock.unlock();
+ *     }
+ *   }
+ * }
+ * }
+ * + *

{@code Monitor}

+ * + *

This version adds some verbosity around the {@code Guard} objects, but removes that same + * verbosity, and more, from the {@code get} and {@code set} methods. {@code Monitor} implements the + * same efficient signaling as we had to hand-code in the {@code ReentrantLock} version above. + * Finally, the programmer no longer has to hand-code the wait loop, and therefore doesn't have to + * remember to use {@code while} instead of {@code if}. + * + *

{@code
+ * public class SafeBox {
+ *   private V value;
+ *   private final Monitor monitor = new Monitor();
+ *   private final Monitor.Guard valuePresent = monitor.newGuard(() -> value != null);
+ *   private final Monitor.Guard valueAbsent = monitor.newGuard(() -> value == null);
+ *
+ *   public V get() throws InterruptedException {
+ *     monitor.enterWhen(valuePresent);
+ *     try {
+ *       V result = value;
+ *       value = null;
+ *       return result;
+ *     } finally {
+ *       monitor.leave();
+ *     }
+ *   }
+ *
+ *   public void set(V newValue) throws InterruptedException {
+ *     monitor.enterWhen(valueAbsent);
+ *     try {
+ *       value = newValue;
+ *     } finally {
+ *       monitor.leave();
+ *     }
+ *   }
+ * }
+ * }
+ * + * @author Justin T. Sampson + * @author Martin Buchholz + * @since 10.0 + */ +@J2ktIncompatible +@GwtIncompatible +@SuppressWarnings("GuardedBy") // TODO(b/35466881): Fix or suppress. +@ElementTypesAreNonnullByDefault +public final class Monitor { + // TODO(user): Use raw LockSupport or AbstractQueuedSynchronizer instead of ReentrantLock. + // TODO(user): "Port" jsr166 tests for ReentrantLock. + // + // TODO(user): Change API to make it impossible to use a Guard with the "wrong" monitor, + // by making the monitor implicit, and to eliminate other sources of IMSE. + // Imagine: + // guard.lock(); + // try { /* monitor locked and guard satisfied here */ } + // finally { guard.unlock(); } + // Here are Justin's design notes about this: + // + // This idea has come up from time to time, and I think one of my + // earlier versions of Monitor even did something like this. I ended + // up strongly favoring the current interface. + // + // I probably can't remember all the reasons (it's possible you + // could find them in the code review archives), but here are a few: + // + // 1. What about leaving/unlocking? Are you going to do + // guard.enter() paired with monitor.leave()? That might get + // confusing. It's nice for the finally block to look as close as + // possible to the thing right before the try. You could have + // guard.leave(), but that's a little odd as well because the + // guard doesn't have anything to do with leaving. You can't + // really enforce that the guard you're leaving is the same one + // you entered with, and it doesn't actually matter. + // + // 2. Since you can enter the monitor without a guard at all, some + // places you'll have monitor.enter()/monitor.leave() and other + // places you'll have guard.enter()/guard.leave() even though + // it's the same lock being acquired underneath. Always using + // monitor.enterXXX()/monitor.leave() will make it really clear + // which lock is held at any point in the code. + // + // 3. I think "enterWhen(notEmpty)" reads better than "notEmpty.enter()". + // + // TODO(user): Implement ReentrantLock features: + // - toString() method + // - getOwner() method + // - getQueuedThreads() method + // - getWaitingThreads(Guard) method + // - implement Serializable + // - redo the API to be as close to identical to ReentrantLock as possible, + // since, after all, this class is also a reentrant mutual exclusion lock!? + + /* + * One of the key challenges of this class is to prevent lost signals, while trying hard to + * minimize unnecessary signals. One simple and correct algorithm is to signal some other waiter + * with a satisfied guard (if one exists) whenever any thread occupying the monitor exits the + * monitor, either by unlocking all of its held locks, or by starting to wait for a guard. This + * includes exceptional exits, so all control paths involving signalling must be protected by a + * finally block. + * + * Further optimizations of this algorithm become increasingly subtle. A wait that terminates + * without the guard being satisfied (due to timeout, but not interrupt) can then immediately exit + * the monitor without signalling. If it timed out without being signalled, it does not need to + * "pass on" the signal to another thread. If it *was* signalled, then its guard must have been + * satisfied at the time of signal, and has since been modified by some other thread to be + * non-satisfied before reacquiring the lock, and that other thread takes over the responsibility + * of signaling the next waiter. + * + * Unlike the underlying Condition, if we are not careful, an interrupt *can* cause a signal to be + * lost, because the signal may be sent to a condition whose sole waiter has just been + * interrupted. + * + * Imagine a monitor with multiple guards. A thread enters the monitor, satisfies all the guards, + * and leaves, calling signalNextWaiter. With traditional locks and conditions, all the conditions + * need to be signalled because it is not known which if any of them have waiters (and hasWaiters + * can't be used reliably because of a check-then-act race). With our Monitor guards, we only + * signal the first active guard that is satisfied. But the corresponding thread may have already + * been interrupted and is waiting to reacquire the lock while still registered in activeGuards, + * in which case the signal is a no-op, and the bigger-picture signal is lost unless interrupted + * threads take special action by participating in the signal-passing game. + */ + + /* + * Timeout handling is intricate, especially given our ambitious goals: + * - Avoid underflow and overflow of timeout values when specified timeouts are close to + * Long.MIN_VALUE or Long.MAX_VALUE. + * - Favor responding to interrupts over timeouts. + * - System.nanoTime() is expensive enough that we want to call it the minimum required number of + * times, typically once before invoking a blocking method. This often requires keeping track of + * the first time in a method that nanoTime() has been invoked, for which the special value 0L + * is reserved to mean "uninitialized". If timeout is non-positive, then nanoTime need never be + * called. + * - Keep behavior of fair and non-fair instances consistent. + */ + + /** + * A boolean condition for which a thread may wait. A {@code Guard} is associated with a single + * {@code Monitor}. The monitor may check the guard at arbitrary times from any thread occupying + * the monitor, so code should not be written to rely on how often a guard might or might not be + * checked. + * + *

If a {@code Guard} is passed into any method of a {@code Monitor} other than the one it is + * associated with, an {@link IllegalMonitorStateException} is thrown. + * + * @since 10.0 + */ + public abstract static class Guard { + + final Monitor monitor; + final Condition condition; + + int waiterCount = 0; + + /** The next active guard */ + Guard next; + + protected Guard(Monitor monitor) { + this.monitor = checkNotNull(monitor, "monitor"); + this.condition = monitor.lock.newCondition(); + } + + /** + * Evaluates this guard's boolean condition. This method is always called with the associated + * monitor already occupied. Implementations of this method must depend only on state protected + * by the associated monitor, and must not modify that state. + */ + public abstract boolean isSatisfied(); + } + + /** Whether this monitor is fair. */ + private final boolean fair; + + /** The lock underlying this monitor. */ + private final ReentrantLock lock; + + /** + * The guards associated with this monitor that currently have waiters ({@code waiterCount > 0}). + * A linked list threaded through the Guard.next field. + */ + private Guard activeGuards = null; + + /** + * Creates a monitor with a non-fair (but fast) ordering policy. Equivalent to {@code + * Monitor(false)}. + */ + public Monitor() { + this(false); + } + + /** + * Creates a monitor with the given ordering policy. + * + * @param fair whether this monitor should use a fair ordering policy rather than a non-fair (but + * fast) one + */ + public Monitor(boolean fair) { + this.fair = fair; + this.lock = new ReentrantLock(fair); + } + + /** + * Creates a new {@linkplain Guard guard} for this monitor. + * + * @param isSatisfied the new guard's boolean condition (see {@link Guard#isSatisfied + * isSatisfied()}) + * @since 21.0 + */ + public Guard newGuard(final BooleanSupplier isSatisfied) { + checkNotNull(isSatisfied, "isSatisfied"); + return new Guard(this) { + @Override + public boolean isSatisfied() { + return isSatisfied.getAsBoolean(); + } + }; + } + + /** Enters this monitor. Blocks indefinitely. */ + public void enter() { + lock.lock(); + } + + /** + * Enters this monitor. Blocks at most the given time. + * + * @return whether the monitor was entered + * @since 28.0 + */ + public boolean enter(Duration time) { + return enter(toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor. Blocks at most the given time. + * + * @return whether the monitor was entered + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean enter(long time, TimeUnit unit) { + final long timeoutNanos = toSafeNanos(time, unit); + final ReentrantLock lock = this.lock; + if (!fair && lock.tryLock()) { + return true; + } + boolean interrupted = Thread.interrupted(); + try { + final long startTime = System.nanoTime(); + for (long remainingNanos = timeoutNanos; ; ) { + try { + return lock.tryLock(remainingNanos, TimeUnit.NANOSECONDS); + } catch (InterruptedException interrupt) { + interrupted = true; + remainingNanos = remainingNanos(startTime, timeoutNanos); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Enters this monitor. Blocks indefinitely, but may be interrupted. + * + * @throws InterruptedException if interrupted while waiting + */ + public void enterInterruptibly() throws InterruptedException { + lock.lockInterruptibly(); + } + + /** + * Enters this monitor. Blocks at most the given time, and may be interrupted. + * + * @return whether the monitor was entered + * @throws InterruptedException if interrupted while waiting + * @since 28.0 + */ + public boolean enterInterruptibly(Duration time) throws InterruptedException { + return enterInterruptibly(toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor. Blocks at most the given time, and may be interrupted. + * + * @return whether the monitor was entered + * @throws InterruptedException if interrupted while waiting + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean enterInterruptibly(long time, TimeUnit unit) throws InterruptedException { + return lock.tryLock(time, unit); + } + + /** + * Enters this monitor if it is possible to do so immediately. Does not block. + * + *

Note: This method disregards the fairness setting of this monitor. + * + * @return whether the monitor was entered + */ + public boolean tryEnter() { + return lock.tryLock(); + } + + /** + * Enters this monitor when the guard is satisfied. Blocks indefinitely, but may be interrupted. + * + * @throws InterruptedException if interrupted while waiting + */ + public void enterWhen(Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); + lock.lockInterruptibly(); + + boolean satisfied = false; + try { + if (!guard.isSatisfied()) { + await(guard, signalBeforeWaiting); + } + satisfied = true; + } finally { + if (!satisfied) { + leave(); + } + } + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied, and may be + * interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + * @since 28.0 + */ + public boolean enterWhen(Guard guard, Duration time) throws InterruptedException { + return enterWhen(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied, and may be + * interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean enterWhen(Guard guard, long time, TimeUnit unit) throws InterruptedException { + final long timeoutNanos = toSafeNanos(time, unit); + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean reentrant = lock.isHeldByCurrentThread(); + long startTime = 0L; + + locked: + { + if (!fair) { + // Check interrupt status to get behavior consistent with fair case. + if (Thread.interrupted()) { + throw new InterruptedException(); + } + if (lock.tryLock()) { + break locked; + } + } + startTime = initNanoTime(timeoutNanos); + if (!lock.tryLock(time, unit)) { + return false; + } + } + + boolean satisfied = false; + boolean threw = true; + try { + satisfied = + guard.isSatisfied() + || awaitNanos( + guard, + (startTime == 0L) ? timeoutNanos : remainingNanos(startTime, timeoutNanos), + reentrant); + threw = false; + return satisfied; + } finally { + if (!satisfied) { + try { + // Don't need to signal if timed out, but do if interrupted + if (threw && !reentrant) { + signalNextWaiter(); + } + } finally { + lock.unlock(); + } + } + } + } + + /** Enters this monitor when the guard is satisfied. Blocks indefinitely. */ + public void enterWhenUninterruptibly(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); + lock.lock(); + + boolean satisfied = false; + try { + if (!guard.isSatisfied()) { + awaitUninterruptibly(guard, signalBeforeWaiting); + } + satisfied = true; + } finally { + if (!satisfied) { + leave(); + } + } + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @since 28.0 + */ + public boolean enterWhenUninterruptibly(Guard guard, Duration time) { + return enterWhenUninterruptibly(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor when the guard is satisfied. Blocks at most the given time, including both + * the time to acquire the lock and the time to wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean enterWhenUninterruptibly(Guard guard, long time, TimeUnit unit) { + final long timeoutNanos = toSafeNanos(time, unit); + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + long startTime = 0L; + boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); + boolean interrupted = Thread.interrupted(); + try { + if (fair || !lock.tryLock()) { + startTime = initNanoTime(timeoutNanos); + for (long remainingNanos = timeoutNanos; ; ) { + try { + if (lock.tryLock(remainingNanos, TimeUnit.NANOSECONDS)) { + break; + } else { + return false; + } + } catch (InterruptedException interrupt) { + interrupted = true; + remainingNanos = remainingNanos(startTime, timeoutNanos); + } + } + } + + boolean satisfied = false; + try { + while (true) { + try { + if (guard.isSatisfied()) { + satisfied = true; + } else { + final long remainingNanos; + if (startTime == 0L) { + startTime = initNanoTime(timeoutNanos); + remainingNanos = timeoutNanos; + } else { + remainingNanos = remainingNanos(startTime, timeoutNanos); + } + satisfied = awaitNanos(guard, remainingNanos, signalBeforeWaiting); + } + return satisfied; + } catch (InterruptedException interrupt) { + interrupted = true; + signalBeforeWaiting = false; + } + } + } finally { + if (!satisfied) { + lock.unlock(); // No need to signal if timed out + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Enters this monitor if the guard is satisfied. Blocks indefinitely acquiring the lock, but does + * not wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + */ + public boolean enterIf(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + lock.lock(); + + boolean satisfied = false; + try { + return satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + } + + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @since 28.0 + */ + public boolean enterIf(Guard guard, Duration time) { + return enterIf(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean enterIf(Guard guard, long time, TimeUnit unit) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + if (!enter(time, unit)) { + return false; + } + + boolean satisfied = false; + try { + return satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + } + + /** + * Enters this monitor if the guard is satisfied. Blocks indefinitely acquiring the lock, but does + * not wait for the guard to be satisfied, and may be interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + */ + public boolean enterIfInterruptibly(Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + + boolean satisfied = false; + try { + return satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + } + + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied, and may be interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + * @since 28.0 + */ + public boolean enterIfInterruptibly(Guard guard, Duration time) throws InterruptedException { + return enterIfInterruptibly(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Enters this monitor if the guard is satisfied. Blocks at most the given time acquiring the + * lock, but does not wait for the guard to be satisfied, and may be interrupted. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean enterIfInterruptibly(Guard guard, long time, TimeUnit unit) + throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + if (!lock.tryLock(time, unit)) { + return false; + } + + boolean satisfied = false; + try { + return satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + } + + /** + * Enters this monitor if it is possible to do so immediately and the guard is satisfied. Does not + * block acquiring the lock and does not wait for the guard to be satisfied. + * + *

Note: This method disregards the fairness setting of this monitor. + * + * @return whether the monitor was entered, which guarantees that the guard is now satisfied + */ + public boolean tryEnterIf(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + final ReentrantLock lock = this.lock; + if (!lock.tryLock()) { + return false; + } + + boolean satisfied = false; + try { + return satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + } + } + + /** + * Waits for the guard to be satisfied. Waits indefinitely, but may be interrupted. May be called + * only by a thread currently occupying this monitor. + * + * @throws InterruptedException if interrupted while waiting + */ + public void waitFor(Guard guard) throws InterruptedException { + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } + if (!guard.isSatisfied()) { + await(guard, true); + } + } + + /** + * Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. May + * be called only by a thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + * @since 28.0 + */ + public boolean waitFor(Guard guard, Duration time) throws InterruptedException { + return waitFor(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Waits for the guard to be satisfied. Waits at most the given time, and may be interrupted. May + * be called only by a thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + * @throws InterruptedException if interrupted while waiting + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean waitFor(Guard guard, long time, TimeUnit unit) throws InterruptedException { + final long timeoutNanos = toSafeNanos(time, unit); + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } + if (guard.isSatisfied()) { + return true; + } + if (Thread.interrupted()) { + throw new InterruptedException(); + } + return awaitNanos(guard, timeoutNanos, true); + } + + /** + * Waits for the guard to be satisfied. Waits indefinitely. May be called only by a thread + * currently occupying this monitor. + */ + public void waitForUninterruptibly(Guard guard) { + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } + if (!guard.isSatisfied()) { + awaitUninterruptibly(guard, true); + } + } + + /** + * Waits for the guard to be satisfied. Waits at most the given time. May be called only by a + * thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + * @since 28.0 + */ + public boolean waitForUninterruptibly(Guard guard, Duration time) { + return waitForUninterruptibly(guard, toNanosSaturated(time), TimeUnit.NANOSECONDS); + } + + /** + * Waits for the guard to be satisfied. Waits at most the given time. May be called only by a + * thread currently occupying this monitor. + * + * @return whether the guard is now satisfied + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean waitForUninterruptibly(Guard guard, long time, TimeUnit unit) { + final long timeoutNanos = toSafeNanos(time, unit); + if (!((guard.monitor == this) && lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } + if (guard.isSatisfied()) { + return true; + } + boolean signalBeforeWaiting = true; + final long startTime = initNanoTime(timeoutNanos); + boolean interrupted = Thread.interrupted(); + try { + for (long remainingNanos = timeoutNanos; ; ) { + try { + return awaitNanos(guard, remainingNanos, signalBeforeWaiting); + } catch (InterruptedException interrupt) { + interrupted = true; + if (guard.isSatisfied()) { + return true; + } + signalBeforeWaiting = false; + remainingNanos = remainingNanos(startTime, timeoutNanos); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** Leaves this monitor. May be called only by a thread currently occupying this monitor. */ + public void leave() { + final ReentrantLock lock = this.lock; + try { + // No need to signal if we will still be holding the lock when we return + if (lock.getHoldCount() == 1) { + signalNextWaiter(); + } + } finally { + lock.unlock(); // Will throw IllegalMonitorStateException if not held + } + } + + /** Returns whether this monitor is using a fair ordering policy. */ + public boolean isFair() { + return fair; + } + + /** + * Returns whether this monitor is occupied by any thread. This method is designed for use in + * monitoring of the system state, not for synchronization control. + */ + public boolean isOccupied() { + return lock.isLocked(); + } + + /** + * Returns whether the current thread is occupying this monitor (has entered more times than it + * has left). + */ + public boolean isOccupiedByCurrentThread() { + return lock.isHeldByCurrentThread(); + } + + /** + * Returns the number of times the current thread has entered this monitor in excess of the number + * of times it has left. Returns 0 if the current thread is not occupying this monitor. + */ + public int getOccupiedDepth() { + return lock.getHoldCount(); + } + + /** + * Returns an estimate of the number of threads waiting to enter this monitor. The value is only + * an estimate because the number of threads may change dynamically while this method traverses + * internal data structures. This method is designed for use in monitoring of the system state, + * not for synchronization control. + */ + public int getQueueLength() { + return lock.getQueueLength(); + } + + /** + * Returns whether any threads are waiting to enter this monitor. Note that because cancellations + * may occur at any time, a {@code true} return does not guarantee that any other thread will ever + * enter this monitor. This method is designed primarily for use in monitoring of the system + * state. + */ + public boolean hasQueuedThreads() { + return lock.hasQueuedThreads(); + } + + /** + * Queries whether the given thread is waiting to enter this monitor. Note that because + * cancellations may occur at any time, a {@code true} return does not guarantee that this thread + * will ever enter this monitor. This method is designed primarily for use in monitoring of the + * system state. + */ + public boolean hasQueuedThread(Thread thread) { + return lock.hasQueuedThread(thread); + } + + /** + * Queries whether any threads are waiting for the given guard to become satisfied. Note that + * because timeouts and interrupts may occur at any time, a {@code true} return does not guarantee + * that the guard becoming satisfied in the future will awaken any threads. This method is + * designed primarily for use in monitoring of the system state. + */ + public boolean hasWaiters(Guard guard) { + return getWaitQueueLength(guard) > 0; + } + + /** + * Returns an estimate of the number of threads waiting for the given guard to become satisfied. + * Note that because timeouts and interrupts may occur at any time, the estimate serves only as an + * upper bound on the actual number of waiters. This method is designed for use in monitoring of + * the system state, not for synchronization control. + */ + public int getWaitQueueLength(Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } + lock.lock(); + try { + return guard.waiterCount; + } finally { + lock.unlock(); + } + } + + /** + * Returns unit.toNanos(time), additionally ensuring the returned value is not at risk of + * overflowing or underflowing, by bounding the value between 0 and (Long.MAX_VALUE / 4) * 3. + * Actually waiting for more than 219 years is not supported! + */ + private static long toSafeNanos(long time, TimeUnit unit) { + long timeoutNanos = unit.toNanos(time); + return Longs.constrainToRange(timeoutNanos, 0L, (Long.MAX_VALUE / 4) * 3); + } + + /** + * Returns System.nanoTime() unless the timeout has already elapsed. Returns 0L if and only if the + * timeout has already elapsed. + */ + private static long initNanoTime(long timeoutNanos) { + if (timeoutNanos <= 0L) { + return 0L; + } else { + long startTime = System.nanoTime(); + return (startTime == 0L) ? 1L : startTime; + } + } + + /** + * Returns the remaining nanos until the given timeout, or 0L if the timeout has already elapsed. + * Caller must have previously sanitized timeoutNanos using toSafeNanos. + */ + private static long remainingNanos(long startTime, long timeoutNanos) { + // assert timeoutNanos == 0L || startTime != 0L; + + // TODO : NOT CORRECT, BUT TESTS PASS ANYWAYS! + // if (true) return timeoutNanos; + // ONLY 2 TESTS FAIL IF WE DO: + // if (true) return 0; + + return (timeoutNanos <= 0L) ? 0L : timeoutNanos - (System.nanoTime() - startTime); + } + + /** + * Signals some other thread waiting on a satisfied guard, if one exists. + * + *

We manage calls to this method carefully, to signal only when necessary, but never losing a + * signal, which is the classic problem of this kind of concurrency construct. We must signal if + * the current thread is about to relinquish the lock and may have changed the state protected by + * the monitor, thereby causing some guard to be satisfied. + * + *

In addition, any thread that has been signalled when its guard was satisfied acquires the + * responsibility of signalling the next thread when it again relinquishes the lock. Unlike a + * normal Condition, there is no guarantee that an interrupted thread has not been signalled, + * since the concurrency control must manage multiple Conditions. So this method must generally be + * called when waits are interrupted. + * + *

On the other hand, if a signalled thread wakes up to discover that its guard is still not + * satisfied, it does *not* need to call this method before returning to wait. This can only + * happen due to spurious wakeup (ignorable) or another thread acquiring the lock before the + * current thread can and returning the guard to the unsatisfied state. In the latter case the + * other thread (last thread modifying the state protected by the monitor) takes over the + * responsibility of signalling the next waiter. + * + *

This method must not be called from within a beginWaitingFor/endWaitingFor block, or else + * the current thread's guard might be mistakenly signalled, leading to a lost signal. + */ + private void signalNextWaiter() { + for (Guard guard = activeGuards; guard != null; guard = guard.next) { + if (isSatisfied(guard)) { + guard.condition.signal(); + break; + } + } + } + + /** + * Exactly like signalNextWaiter, but caller guarantees that guardToSkip need not be considered, + * because caller has previously checked that guardToSkip.isSatisfied() returned false. An + * optimization for the case that guardToSkip.isSatisfied() may be expensive. + * + *

We decided against using this method, since in practice, isSatisfied() is likely to be very + * cheap (typically one field read). Resurrect this method if you find that not to be true. + */ + // @GuardedBy("lock") + // private void signalNextWaiterSkipping(Guard guardToSkip) { + // for (Guard guard = activeGuards; guard != null; guard = guard.next) { + // if (guard != guardToSkip && isSatisfied(guard)) { + // guard.condition.signal(); + // break; + // } + // } + // } + + /** + * Exactly like guard.isSatisfied(), but in addition signals all waiting threads in the (hopefully + * unlikely) event that isSatisfied() throws. + */ + private boolean isSatisfied(Guard guard) { + try { + return guard.isSatisfied(); + } catch (RuntimeException | Error throwable) { + signalAllWaiters(); + throw throwable; + } + } + + /** Signals all threads waiting on guards. */ + private void signalAllWaiters() { + for (Guard guard = activeGuards; guard != null; guard = guard.next) { + guard.condition.signalAll(); + } + } + + /** Records that the current thread is about to wait on the specified guard. */ + private void beginWaitingFor(Guard guard) { + int waiters = guard.waiterCount++; + if (waiters == 0) { + // push guard onto activeGuards + guard.next = activeGuards; + activeGuards = guard; + } + } + + /** Records that the current thread is no longer waiting on the specified guard. */ + private void endWaitingFor(Guard guard) { + int waiters = --guard.waiterCount; + if (waiters == 0) { + // unlink guard from activeGuards + for (Guard p = activeGuards, pred = null; ; pred = p, p = p.next) { + if (p == guard) { + if (pred == null) { + activeGuards = p.next; + } else { + pred.next = p.next; + } + p.next = null; // help GC + break; + } + } + } + } + + /* + * Methods that loop waiting on a guard's condition until the guard is satisfied, while recording + * this fact so that other threads know to check our guard and signal us. It's caller's + * responsibility to ensure that the guard is *not* currently satisfied. + */ + + private void await(Guard guard, boolean signalBeforeWaiting) throws InterruptedException { + if (signalBeforeWaiting) { + signalNextWaiter(); + } + beginWaitingFor(guard); + try { + do { + guard.condition.await(); + } while (!guard.isSatisfied()); + } finally { + endWaitingFor(guard); + } + } + + private void awaitUninterruptibly(Guard guard, boolean signalBeforeWaiting) { + if (signalBeforeWaiting) { + signalNextWaiter(); + } + beginWaitingFor(guard); + try { + do { + guard.condition.awaitUninterruptibly(); + } while (!guard.isSatisfied()); + } finally { + endWaitingFor(guard); + } + } + + /** Caller should check before calling that guard is not satisfied. */ + private boolean awaitNanos(Guard guard, long nanos, boolean signalBeforeWaiting) + throws InterruptedException { + boolean firstTime = true; + try { + do { + if (nanos <= 0L) { + return false; + } + if (firstTime) { + if (signalBeforeWaiting) { + signalNextWaiter(); + } + beginWaitingFor(guard); + firstTime = false; + } + nanos = guard.condition.awaitNanos(nanos); + } while (!guard.isSatisfied()); + return true; + } finally { + if (!firstTime) { + endWaitingFor(guard); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/MoreExecutors.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/MoreExecutors.java new file mode 100644 index 0000000000000..db7ba4e615a07 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/MoreExecutors.java @@ -0,0 +1,1167 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.base.Throwables; +import org.apache.iotdb.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Queues; +import org.apache.iotdb.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; + +import java.lang.reflect.InvocationTargetException; +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService}, + * and {@link java.util.concurrent.ThreadFactory}. + * + * @author Eric Fellheimer + * @author Kyle Littlefield + * @author Justin Mahoney + * @since 3.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class MoreExecutors { + private MoreExecutors() {} + + /** + * Converts the given ThreadPoolExecutor into an ExecutorService that exits when the application + * is complete. It does so by using daemon threads and adding a shutdown hook to wait for their + * completion. + * + *

This is mainly for fixed thread pools. See {@link Executors#newFixedThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @return an unmodifiable version of the input which will not hang the JVM + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static ExecutorService getExitingExecutorService( + ThreadPoolExecutor executor, Duration terminationTimeout) { + return getExitingExecutorService( + executor, toNanosSaturated(terminationTimeout), TimeUnit.NANOSECONDS); + } + + /** + * Converts the given ThreadPoolExecutor into an ExecutorService that exits when the application + * is complete. It does so by using daemon threads and adding a shutdown hook to wait for their + * completion. + * + *

This is mainly for fixed thread pools. See {@link Executors#newFixedThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @param timeUnit unit of time for the time parameter + * @return an unmodifiable version of the input which will not hang the JVM + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static ExecutorService getExitingExecutorService( + ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + return new Application().getExitingExecutorService(executor, terminationTimeout, timeUnit); + } + + /** + * Converts the given ThreadPoolExecutor into an ExecutorService that exits when the application + * is complete. It does so by using daemon threads and adding a shutdown hook to wait for their + * completion. + * + *

This method waits 120 seconds before continuing with JVM termination, even if the executor + * has not finished its work. + * + *

This is mainly for fixed thread pools. See {@link Executors#newFixedThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @return an unmodifiable version of the input which will not hang the JVM + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { + return new Application().getExitingExecutorService(executor); + } + + /** + * Converts the given ScheduledThreadPoolExecutor into a ScheduledExecutorService that exits when + * the application is complete. It does so by using daemon threads and adding a shutdown hook to + * wait for their completion. + * + *

This is mainly for fixed thread pools. See {@link Executors#newScheduledThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @return an unmodifiable version of the input which will not hang the JVM + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + public static ScheduledExecutorService getExitingScheduledExecutorService( + ScheduledThreadPoolExecutor executor, Duration terminationTimeout) { + return getExitingScheduledExecutorService( + executor, toNanosSaturated(terminationTimeout), TimeUnit.NANOSECONDS); + } + + /** + * Converts the given ScheduledThreadPoolExecutor into a ScheduledExecutorService that exits when + * the application is complete. It does so by using daemon threads and adding a shutdown hook to + * wait for their completion. + * + *

This is mainly for fixed thread pools. See {@link Executors#newScheduledThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @param timeUnit unit of time for the time parameter + * @return an unmodifiable version of the input which will not hang the JVM + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static ScheduledExecutorService getExitingScheduledExecutorService( + ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + return new Application() + .getExitingScheduledExecutorService(executor, terminationTimeout, timeUnit); + } + + /** + * Converts the given ScheduledThreadPoolExecutor into a ScheduledExecutorService that exits when + * the application is complete. It does so by using daemon threads and adding a shutdown hook to + * wait for their completion. + * + *

This method waits 120 seconds before continuing with JVM termination, even if the executor + * has not finished its work. + * + *

This is mainly for fixed thread pools. See {@link Executors#newScheduledThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the application is finished + * @return an unmodifiable version of the input which will not hang the JVM + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static ScheduledExecutorService getExitingScheduledExecutorService( + ScheduledThreadPoolExecutor executor) { + return new Application().getExitingScheduledExecutorService(executor); + } + + /** + * Add a shutdown hook to wait for thread completion in the given {@link ExecutorService service}. + * This is useful if the given service uses daemon threads, and we want to keep the JVM from + * exiting immediately on shutdown, instead giving these daemon threads a chance to terminate + * normally. + * + * @param service ExecutorService which uses daemon threads + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + public static void addDelayedShutdownHook(ExecutorService service, Duration terminationTimeout) { + addDelayedShutdownHook(service, toNanosSaturated(terminationTimeout), TimeUnit.NANOSECONDS); + } + + /** + * Add a shutdown hook to wait for thread completion in the given {@link ExecutorService service}. + * This is useful if the given service uses daemon threads, and we want to keep the JVM from + * exiting immediately on shutdown, instead giving these daemon threads a chance to terminate + * normally. + * + * @param service ExecutorService which uses daemon threads + * @param terminationTimeout how long to wait for the executor to finish before terminating the + * JVM + * @param timeUnit unit of time for the time parameter + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static void addDelayedShutdownHook( + ExecutorService service, long terminationTimeout, TimeUnit timeUnit) { + new Application().addDelayedShutdownHook(service, terminationTimeout, timeUnit); + } + + /** Represents the current application to register shutdown hooks. */ + @J2ktIncompatible + @GwtIncompatible // TODO + @VisibleForTesting + static class Application { + + final ExecutorService getExitingExecutorService( + ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + useDaemonThreadFactory(executor); + ExecutorService service = Executors.unconfigurableExecutorService(executor); + addDelayedShutdownHook(executor, terminationTimeout, timeUnit); + return service; + } + + final ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { + return getExitingExecutorService(executor, 120, TimeUnit.SECONDS); + } + + final ScheduledExecutorService getExitingScheduledExecutorService( + ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + useDaemonThreadFactory(executor); + ScheduledExecutorService service = Executors.unconfigurableScheduledExecutorService(executor); + addDelayedShutdownHook(executor, terminationTimeout, timeUnit); + return service; + } + + final ScheduledExecutorService getExitingScheduledExecutorService( + ScheduledThreadPoolExecutor executor) { + return getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS); + } + + final void addDelayedShutdownHook( + final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) { + checkNotNull(service); + checkNotNull(timeUnit); + addShutdownHook( + MoreExecutors.newThread( + "DelayedShutdownHook-for-" + service, + new Runnable() { + @Override + public void run() { + try { + // We'd like to log progress and failures that may arise in the + // following code, but unfortunately the behavior of logging + // is undefined in shutdown hooks. + // This is because the logging code installs a shutdown hook of its + // own. See Cleaner class inside {@link LogManager}. + service.shutdown(); + service.awaitTermination(terminationTimeout, timeUnit); + } catch (InterruptedException ignored) { + // We're shutting down anyway, so just ignore. + } + } + })); + } + + @VisibleForTesting + void addShutdownHook(Thread hook) { + Runtime.getRuntime().addShutdownHook(hook); + } + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { + executor.setThreadFactory( + new ThreadFactoryBuilder() + .setDaemon(true) + .setThreadFactory(executor.getThreadFactory()) + .build()); + } + + // See newDirectExecutorService javadoc for behavioral notes. + @J2ktIncompatible + @GwtIncompatible // TODO + private static final class DirectExecutorService extends AbstractListeningExecutorService { + /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ + private final Object lock = new Object(); + + /* + * Conceptually, these two variables describe the executor being in + * one of three states: + * - Active: shutdown == false + * - Shutdown: runningTasks > 0 and shutdown == true + * - Terminated: runningTasks == 0 and shutdown == true + */ + + private int runningTasks = 0; + + private boolean shutdown = false; + + @Override + public void execute(Runnable command) { + startTask(); + try { + command.run(); + } finally { + endTask(); + } + } + + @Override + public boolean isShutdown() { + synchronized (lock) { + return shutdown; + } + } + + @Override + public void shutdown() { + synchronized (lock) { + shutdown = true; + if (runningTasks == 0) { + lock.notifyAll(); + } + } + } + + // See newDirectExecutorService javadoc for unusual behavior of this method. + @Override + public List shutdownNow() { + shutdown(); + return Collections.emptyList(); + } + + @Override + public boolean isTerminated() { + synchronized (lock) { + return shutdown && runningTasks == 0; + } + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); + synchronized (lock) { + while (true) { + if (shutdown && runningTasks == 0) { + return true; + } else if (nanos <= 0) { + return false; + } else { + long now = System.nanoTime(); + TimeUnit.NANOSECONDS.timedWait(lock, nanos); + nanos -= System.nanoTime() - now; // subtract the actual time we waited + } + } + } + } + + /** + * Checks if the executor has been shut down and increments the running task count. + * + * @throws RejectedExecutionException if the executor has been previously shutdown + */ + private void startTask() { + synchronized (lock) { + if (shutdown) { + throw new RejectedExecutionException("Executor already shutdown"); + } + runningTasks++; + } + } + + /** Decrements the running task count. */ + private void endTask() { + synchronized (lock) { + int numRunning = --runningTasks; + if (numRunning == 0) { + lock.notifyAll(); + } + } + } + } + + /** + * Creates an executor service that runs each task in the thread that invokes {@code + * execute/submit}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. This applies both to + * individually submitted tasks and to collections of tasks submitted via {@code invokeAll} or + * {@code invokeAny}. In the latter case, tasks will run serially on the calling thread. Tasks are + * run to completion before a {@code Future} is returned to the caller (unless the executor has + * been shutdown). + * + *

Although all tasks are immediately executed in the thread that submitted the task, this + * {@code ExecutorService} imposes a small locking overhead on each task submission in order to + * implement shutdown and termination behavior. + * + *

The implementation deviates from the {@code ExecutorService} specification with regards to + * the {@code shutdownNow} method. First, "best-effort" with regards to canceling running tasks is + * implemented as "no-effort". No interrupts or other attempts are made to stop threads executing + * tasks. Second, the returned list will always be empty, as any submitted task is considered to + * have started execution. This applies also to tasks given to {@code invokeAll} or {@code + * invokeAny} which are pending serial execution, even the subset of the tasks that have not yet + * started execution. It is unclear from the {@code ExecutorService} specification if these should + * be included, and it's much easier to implement the interpretation that they not be. Finally, a + * call to {@code shutdown} or {@code shutdownNow} may result in concurrent calls to {@code + * invokeAll/invokeAny} throwing RejectedExecutionException, although a subset of the tasks may + * already have been executed. + * + * @since 18.0 (present as MoreExecutors.sameThreadExecutor() since 10.0) + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static ListeningExecutorService newDirectExecutorService() { + return new DirectExecutorService(); + } + + /** + * Returns an {@link Executor} that runs each task in the thread that invokes {@link + * Executor#execute execute}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. + * + *

This executor is appropriate for tasks that are lightweight and not deeply chained. + * Inappropriate {@code directExecutor} usage can cause problems, and these problems can be + * difficult to reproduce because they depend on timing. For example: + * + *

    + *
  • When a {@code ListenableFuture} listener is registered to run under {@code + * directExecutor}, the listener can execute in any of three possible threads: + *
      + *
    1. When a thread attaches a listener to a {@code ListenableFuture} that's already + * complete, the listener runs immediately in that thread. + *
    2. When a thread attaches a listener to a {@code ListenableFuture} that's + * incomplete and the {@code ListenableFuture} later completes normally, the + * listener runs in the thread that completes the {@code ListenableFuture}. + *
    3. When a listener is attached to a {@code ListenableFuture} and the {@code + * ListenableFuture} gets cancelled, the listener runs immediately in the thread that + * cancelled the {@code Future}. + *
    + * Given all these possibilities, it is frequently possible for listeners to execute in UI + * threads, RPC network threads, or other latency-sensitive threads. In those cases, slow + * listeners can harm responsiveness, slow the system as a whole, or worse. (See also the + * note about locking below.) + *
  • If many tasks will be triggered by the same event, one heavyweight task may delay other + * tasks -- even tasks that are not themselves {@code directExecutor} tasks. + *
  • If many such tasks are chained together (such as with {@code + * future.transform(...).transform(...).transform(...)....}), they may overflow the stack. + * (In simple cases, callers can avoid this by registering all tasks with the same {@link + * MoreExecutors#newSequentialExecutor} wrapper around {@code directExecutor()}. More + * complex cases may require using thread pools or making deeper changes.) + *
  • If an exception propagates out of a {@code Runnable}, it is not necessarily seen by any + * {@code UncaughtExceptionHandler} for the thread. For example, if the callback passed to + * {@link Futures#addCallback} throws an exception, that exception will be typically be + * logged by the {@link ListenableFuture} implementation, even if the thread is configured + * to do something different. In other cases, no code will catch the exception, and it may + * terminate whichever thread happens to trigger the execution. + *
+ * + * A specific warning about locking: Code that executes user-supplied tasks, such as {@code + * ListenableFuture} listeners, should take care not to do so while holding a lock. Additionally, + * as a further line of defense, prefer not to perform any locking inside a task that will be run + * under {@code directExecutor}: Not only might the wait for a lock be long, but if the running + * thread was holding a lock, the listener may deadlock or break lock isolation. + * + *

This instance is equivalent to: + * + *

{@code
+   * final class DirectExecutor implements Executor {
+   *   public void execute(Runnable r) {
+   *     r.run();
+   *   }
+   * }
+   * }
+ * + *

This should be preferred to {@link #newDirectExecutorService()} because implementing the + * {@link ExecutorService} subinterface necessitates significant performance overhead. + * + * @since 18.0 + */ + public static Executor directExecutor() { + return DirectExecutor.INSTANCE; + } + + /** + * Returns an {@link Executor} that runs each task executed sequentially, such that no two tasks + * are running concurrently. + * + *

{@linkplain Executor#execute executed} tasks have a happens-before order as defined in the + * Java Language Specification. Tasks execute with the same happens-before order that the function + * calls to {@link Executor#execute `execute()`} that submitted those tasks had. + * + *

The executor uses {@code delegate} in order to {@link Executor#execute execute} each task in + * turn, and does not create any threads of its own. + * + *

After execution begins on a thread from the {@code delegate} {@link Executor}, tasks are + * polled and executed from a task queue until there are no more tasks. The thread will not be + * released until there are no more tasks to run. + * + *

If a task is submitted while a thread is executing tasks from the task queue, the thread + * will not be released until that submitted task is also complete. + * + *

If a task is {@linkplain Thread#interrupt interrupted} while a task is running: + * + *

    + *
  1. execution will not stop until the task queue is empty. + *
  2. tasks will begin execution with the thread marked as not interrupted - any interruption + * applies only to the task that was running at the point of interruption. + *
  3. if the thread was interrupted before the SequentialExecutor's worker begins execution, + * the interrupt will be restored to the thread after it completes so that its {@code + * delegate} Executor may process the interrupt. + *
  4. subtasks are run with the thread uninterrupted and interrupts received during execution + * of a task are ignored. + *
+ * + *

{@code RuntimeException}s thrown by tasks are simply logged and the executor keeps trucking. + * If an {@code Error} is thrown, the error will propagate and execution will stop until the next + * time a task is submitted. + * + *

When an {@code Error} is thrown by an executed task, previously submitted tasks may never + * run. An attempt will be made to restart execution on the next call to {@code execute}. If the + * {@code delegate} has begun to reject execution, the previously submitted tasks may never run, + * despite not throwing a RejectedExecutionException synchronously with the call to {@code + * execute}. If this behaviour is problematic, use an Executor with a single thread (e.g. {@link + * Executors#newSingleThreadExecutor}). + * + * @since 23.3 (since 23.1 as {@code sequentialExecutor}) + */ + @J2ktIncompatible + @GwtIncompatible + public static Executor newSequentialExecutor(Executor delegate) { + return new SequentialExecutor(delegate); + } + + /** + * Creates an {@link ExecutorService} whose {@code submit} and {@code invokeAll} methods submit + * {@link ListenableFutureTask} instances to the given delegate executor. Those methods, as well + * as {@code execute} and {@code invokeAny}, are implemented in terms of calls to {@code + * delegate.execute}. All other methods are forwarded unchanged to the delegate. This implies that + * the returned {@code ListeningExecutorService} never calls the delegate's {@code submit}, {@code + * invokeAll}, and {@code invokeAny} methods, so any special handling of tasks must be implemented + * in the delegate's {@code execute} method or by wrapping the returned {@code + * ListeningExecutorService}. + * + *

If the delegate executor was already an instance of {@code ListeningExecutorService}, it is + * returned untouched, and the rest of this documentation does not apply. + * + * @since 10.0 + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static ListeningExecutorService listeningDecorator(ExecutorService delegate) { + return (delegate instanceof ListeningExecutorService) + ? (ListeningExecutorService) delegate + : (delegate instanceof ScheduledExecutorService) + ? new ScheduledListeningDecorator((ScheduledExecutorService) delegate) + : new ListeningDecorator(delegate); + } + + /** + * Creates a {@link ScheduledExecutorService} whose {@code submit} and {@code invokeAll} methods + * submit {@link ListenableFutureTask} instances to the given delegate executor. Those methods, as + * well as {@code execute} and {@code invokeAny}, are implemented in terms of calls to {@code + * delegate.execute}. All other methods are forwarded unchanged to the delegate. This implies that + * the returned {@code ListeningScheduledExecutorService} never calls the delegate's {@code + * submit}, {@code invokeAll}, and {@code invokeAny} methods, so any special handling of tasks + * must be implemented in the delegate's {@code execute} method or by wrapping the returned {@code + * ListeningScheduledExecutorService}. + * + *

If the delegate executor was already an instance of {@code + * ListeningScheduledExecutorService}, it is returned untouched, and the rest of this + * documentation does not apply. + * + * @since 10.0 + */ + @J2ktIncompatible + @GwtIncompatible // TODO + public static ListeningScheduledExecutorService listeningDecorator( + ScheduledExecutorService delegate) { + return (delegate instanceof ListeningScheduledExecutorService) + ? (ListeningScheduledExecutorService) delegate + : new ScheduledListeningDecorator(delegate); + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static class ListeningDecorator extends AbstractListeningExecutorService { + private final ExecutorService delegate; + + ListeningDecorator(ExecutorService delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + public final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegate.awaitTermination(timeout, unit); + } + + @Override + public final boolean isShutdown() { + return delegate.isShutdown(); + } + + @Override + public final boolean isTerminated() { + return delegate.isTerminated(); + } + + @Override + public final void shutdown() { + delegate.shutdown(); + } + + @Override + public final List shutdownNow() { + return delegate.shutdownNow(); + } + + @Override + public final void execute(Runnable command) { + delegate.execute(command); + } + + @Override + public final String toString() { + return super.toString() + "[" + delegate + "]"; + } + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static final class ScheduledListeningDecorator extends ListeningDecorator + implements ListeningScheduledExecutorService { + @SuppressWarnings("hiding") + final ScheduledExecutorService delegate; + + ScheduledListeningDecorator(ScheduledExecutorService delegate) { + super(delegate); + this.delegate = checkNotNull(delegate); + } + + @Override + public ListenableScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + TrustedListenableFutureTask task = TrustedListenableFutureTask.create(command, null); + ScheduledFuture scheduled = delegate.schedule(task, delay, unit); + return new ListenableScheduledTask(task, scheduled); + } + + @Override + public ListenableScheduledFuture schedule( + Callable callable, long delay, TimeUnit unit) { + TrustedListenableFutureTask task = TrustedListenableFutureTask.create(callable); + ScheduledFuture scheduled = delegate.schedule(task, delay, unit); + return new ListenableScheduledTask(task, scheduled); + } + + @Override + public ListenableScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + NeverSuccessfulListenableFutureTask task = new NeverSuccessfulListenableFutureTask(command); + ScheduledFuture scheduled = delegate.scheduleAtFixedRate(task, initialDelay, period, unit); + return new ListenableScheduledTask(task, scheduled); + } + + @Override + public ListenableScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + NeverSuccessfulListenableFutureTask task = new NeverSuccessfulListenableFutureTask(command); + ScheduledFuture scheduled = + delegate.scheduleWithFixedDelay(task, initialDelay, delay, unit); + return new ListenableScheduledTask(task, scheduled); + } + + private static final class ListenableScheduledTask + extends SimpleForwardingListenableFuture implements ListenableScheduledFuture { + + private final ScheduledFuture scheduledDelegate; + + public ListenableScheduledTask( + ListenableFuture listenableDelegate, ScheduledFuture scheduledDelegate) { + super(listenableDelegate); + this.scheduledDelegate = scheduledDelegate; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + boolean cancelled = super.cancel(mayInterruptIfRunning); + if (cancelled) { + // Unless it is cancelled, the delegate may continue being scheduled + scheduledDelegate.cancel(mayInterruptIfRunning); + + // TODO(user): Cancel "this" if "scheduledDelegate" is cancelled. + } + return cancelled; + } + + @Override + public long getDelay(TimeUnit unit) { + return scheduledDelegate.getDelay(unit); + } + + @Override + public int compareTo(Delayed other) { + return scheduledDelegate.compareTo(other); + } + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static final class NeverSuccessfulListenableFutureTask + extends AbstractFuture.TrustedFuture implements Runnable { + private final Runnable delegate; + + public NeverSuccessfulListenableFutureTask(Runnable delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + public void run() { + try { + delegate.run(); + } catch (RuntimeException | Error t) { + setException(t); + throw t; + } + } + + @Override + protected String pendingToString() { + return "task=[" + delegate + "]"; + } + } + } + + /* + * This following method is a modified version of one found in + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck/AbstractExecutorServiceTest.java?revision=1.30 + * which contained the following notice: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 Expert Group and released to + * the public domain, as explained at http://creativecommons.org/publicdomain/zero/1.0/ + * + * Other contributors include Andrew Wright, Jeffrey Hayes, Pat Fisher, Mike Judd. + */ + + /** + * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} + * implementations. + */ + @J2ktIncompatible + @GwtIncompatible + @ParametricNullness + static T invokeAnyImpl( + ListeningExecutorService executorService, + Collection> tasks, + boolean timed, + Duration timeout) + throws InterruptedException, ExecutionException, TimeoutException { + return invokeAnyImpl( + executorService, tasks, timed, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} + * implementations. + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @J2ktIncompatible + @GwtIncompatible + @ParametricNullness + static T invokeAnyImpl( + ListeningExecutorService executorService, + Collection> tasks, + boolean timed, + long timeout, + TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + checkNotNull(executorService); + checkNotNull(unit); + int ntasks = tasks.size(); + checkArgument(ntasks > 0); + List> futures = Lists.newArrayListWithCapacity(ntasks); + BlockingQueue> futureQueue = Queues.newLinkedBlockingQueue(); + long timeoutNanos = unit.toNanos(timeout); + + // For efficiency, especially in executors with limited + // parallelism, check to see if previously submitted tasks are + // done before submitting more of them. This interleaving + // plus the exception mechanics account for messiness of main + // loop. + + try { + // Record exceptions so that if we fail to obtain any + // result, we can throw the last exception we got. + ExecutionException ee = null; + long lastTime = timed ? System.nanoTime() : 0; + Iterator> it = tasks.iterator(); + + futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue)); + --ntasks; + int active = 1; + + while (true) { + Future f = futureQueue.poll(); + if (f == null) { + if (ntasks > 0) { + --ntasks; + futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue)); + ++active; + } else if (active == 0) { + break; + } else if (timed) { + f = futureQueue.poll(timeoutNanos, TimeUnit.NANOSECONDS); + if (f == null) { + throw new TimeoutException(); + } + long now = System.nanoTime(); + timeoutNanos -= now - lastTime; + lastTime = now; + } else { + f = futureQueue.take(); + } + } + if (f != null) { + --active; + try { + return f.get(); + } catch (ExecutionException eex) { + ee = eex; + } catch (RuntimeException rex) { + ee = new ExecutionException(rex); + } + } + } + + if (ee == null) { + ee = new ExecutionException(null); + } + throw ee; + } finally { + for (Future f : futures) { + f.cancel(true); + } + } + } + + /** + * Submits the task and adds a listener that adds the future to {@code queue} when it completes. + */ + @J2ktIncompatible + @GwtIncompatible // TODO + private static ListenableFuture submitAndAddQueueListener( + ListeningExecutorService executorService, + Callable task, + final BlockingQueue> queue) { + final ListenableFuture future = executorService.submit(task); + future.addListener( + new Runnable() { + @Override + public void run() { + queue.add(future); + } + }, + directExecutor()); + return future; + } + + /** + * Returns a default thread factory used to create new threads. + * + *

When running on AppEngine with access to AppEngine legacy + * APIs, this method returns {@code ThreadManager.currentRequestThreadFactory()}. Otherwise, + * it returns {@link Executors#defaultThreadFactory()}. + * + * @since 14.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static ThreadFactory platformThreadFactory() { + if (!isAppEngineWithApiClasses()) { + return Executors.defaultThreadFactory(); + } + try { + return (ThreadFactory) + Class.forName("com.google.appengine.api.ThreadManager") + .getMethod("currentRequestThreadFactory") + .invoke(null); + /* + * Do not merge the 3 catch blocks below. javac would infer a type of + * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of Android + * don't *seem* to mind, but there might be edge cases of which we're unaware.) + */ + } catch (IllegalAccessException e) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); + } catch (InvocationTargetException e) { + throw Throwables.propagate(e.getCause()); + } + } + + @J2ktIncompatible + @GwtIncompatible // TODO + private static boolean isAppEngineWithApiClasses() { + if (System.getProperty("com.google.appengine.runtime.environment") == null) { + return false; + } + try { + Class.forName("com.google.appengine.api.utils.SystemProperty"); + } catch (ClassNotFoundException e) { + return false; + } + try { + // If the current environment is null, we're not inside AppEngine. + return Class.forName("com.google.apphosting.api.ApiProxy") + .getMethod("getCurrentEnvironment") + .invoke(null) + != null; + } catch (ClassNotFoundException e) { + // If ApiProxy doesn't exist, we're not on AppEngine at all. + return false; + } catch (InvocationTargetException e) { + // If ApiProxy throws an exception, we're not in a proper AppEngine environment. + return false; + } catch (IllegalAccessException e) { + // If the method isn't accessible, we're not on a supported version of AppEngine; + return false; + } catch (NoSuchMethodException e) { + // If the method doesn't exist, we're not on a supported version of AppEngine; + return false; + } + } + + /** + * Creates a thread using {@link #platformThreadFactory}, and sets its name to {@code name} unless + * changing the name is forbidden by the security manager. + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + static Thread newThread(String name, Runnable runnable) { + checkNotNull(name); + checkNotNull(runnable); + // TODO(b/139726489): Confirm that null is impossible here. + Thread result = requireNonNull(platformThreadFactory().newThread(runnable)); + try { + result.setName(name); + } catch (SecurityException e) { + // OK if we can't set the name in this environment. + } + return result; + } + + // TODO(lukes): provide overloads for ListeningExecutorService? ListeningScheduledExecutorService? + // TODO(lukes): provide overloads that take constant strings? Functions to + // calculate names? + + /** + * Creates an {@link Executor} that renames the {@link Thread threads} that its tasks run in. + * + *

The names are retrieved from the {@code nameSupplier} on the thread that is being renamed + * right before each task is run. The renaming is best effort, if a {@link SecurityManager} + * prevents the renaming then it will be skipped but the tasks will still execute. + * + * @param executor The executor to decorate + * @param nameSupplier The source of names for each task + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + static Executor renamingDecorator(final Executor executor, final Supplier nameSupplier) { + checkNotNull(executor); + checkNotNull(nameSupplier); + return new Executor() { + @Override + public void execute(Runnable command) { + executor.execute(Callables.threadRenaming(command, nameSupplier)); + } + }; + } + + /** + * Creates an {@link ExecutorService} that renames the {@link Thread threads} that its tasks run + * in. + * + *

The names are retrieved from the {@code nameSupplier} on the thread that is being renamed + * right before each task is run. The renaming is best effort, if a {@link SecurityManager} + * prevents the renaming then it will be skipped but the tasks will still execute. + * + * @param service The executor to decorate + * @param nameSupplier The source of names for each task + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + static ExecutorService renamingDecorator( + final ExecutorService service, final Supplier nameSupplier) { + checkNotNull(service); + checkNotNull(nameSupplier); + return new WrappingExecutorService(service) { + @Override + protected Callable wrapTask(Callable callable) { + return Callables.threadRenaming(callable, nameSupplier); + } + + @Override + protected Runnable wrapTask(Runnable command) { + return Callables.threadRenaming(command, nameSupplier); + } + }; + } + + /** + * Creates a {@link ScheduledExecutorService} that renames the {@link Thread threads} that its + * tasks run in. + * + *

The names are retrieved from the {@code nameSupplier} on the thread that is being renamed + * right before each task is run. The renaming is best effort, if a {@link SecurityManager} + * prevents the renaming then it will be skipped but the tasks will still execute. + * + * @param service The executor to decorate + * @param nameSupplier The source of names for each task + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + static ScheduledExecutorService renamingDecorator( + final ScheduledExecutorService service, final Supplier nameSupplier) { + checkNotNull(service); + checkNotNull(nameSupplier); + return new WrappingScheduledExecutorService(service) { + @Override + protected Callable wrapTask(Callable callable) { + return Callables.threadRenaming(callable, nameSupplier); + } + + @Override + protected Runnable wrapTask(Runnable command) { + return Callables.threadRenaming(command, nameSupplier); + } + }; + } + + /** + * Shuts down the given executor service gradually, first disabling new submissions and later, if + * necessary, cancelling remaining tasks. + * + *

The method takes the following steps: + * + *

    + *
  1. calls {@link ExecutorService#shutdown()}, disabling acceptance of new submitted tasks. + *
  2. awaits executor service termination for half of the specified timeout. + *
  3. if the timeout expires, it calls {@link ExecutorService#shutdownNow()}, cancelling + * pending tasks and interrupting running tasks. + *
  4. awaits executor service termination for the other half of the specified timeout. + *
+ * + *

If, at any step of the process, the calling thread is interrupted, the method calls {@link + * ExecutorService#shutdownNow()} and returns. + * + * @param service the {@code ExecutorService} to shut down + * @param timeout the maximum time to wait for the {@code ExecutorService} to terminate + * @return {@code true} if the {@code ExecutorService} was terminated successfully, {@code false} + * if the call timed out or was interrupted + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + public static boolean shutdownAndAwaitTermination(ExecutorService service, Duration timeout) { + return shutdownAndAwaitTermination(service, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Shuts down the given executor service gradually, first disabling new submissions and later, if + * necessary, cancelling remaining tasks. + * + *

The method takes the following steps: + * + *

    + *
  1. calls {@link ExecutorService#shutdown()}, disabling acceptance of new submitted tasks. + *
  2. awaits executor service termination for half of the specified timeout. + *
  3. if the timeout expires, it calls {@link ExecutorService#shutdownNow()}, cancelling + * pending tasks and interrupting running tasks. + *
  4. awaits executor service termination for the other half of the specified timeout. + *
+ * + *

If, at any step of the process, the calling thread is interrupted, the method calls {@link + * ExecutorService#shutdownNow()} and returns. + * + * @param service the {@code ExecutorService} to shut down + * @param timeout the maximum time to wait for the {@code ExecutorService} to terminate + * @param unit the time unit of the timeout argument + * @return {@code true} if the {@code ExecutorService} was terminated successfully, {@code false} + * if the call timed out or was interrupted + * @since 17.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static boolean shutdownAndAwaitTermination( + ExecutorService service, long timeout, TimeUnit unit) { + long halfTimeoutNanos = unit.toNanos(timeout) / 2; + // Disable new tasks from being submitted + service.shutdown(); + try { + // Wait for half the duration of the timeout for existing tasks to terminate + if (!service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS)) { + // Cancel currently executing tasks + service.shutdownNow(); + // Wait the other half of the timeout for tasks to respond to being cancelled + service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS); + } + } catch (InterruptedException ie) { + // Preserve interrupt status + Thread.currentThread().interrupt(); + // (Re-)Cancel if current thread also interrupted + service.shutdownNow(); + } + return service.isTerminated(); + } + + /** + * Returns an Executor that will propagate {@link RejectedExecutionException} from the delegate + * executor to the given {@code future}. + * + *

Note, the returned executor can only be used once. + */ + static Executor rejectionPropagatingExecutor( + final Executor delegate, final AbstractFuture future) { + checkNotNull(delegate); + checkNotNull(future); + if (delegate == directExecutor()) { + // directExecutor() cannot throw RejectedExecutionException + return delegate; + } + return new Executor() { + @Override + public void execute(Runnable command) { + try { + delegate.execute(command); + } catch (RejectedExecutionException e) { + future.setException(e); + } + } + }; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/NullnessCasts.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/NullnessCasts.java new file mode 100644 index 0000000000000..302b06bb40133 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/NullnessCasts.java @@ -0,0 +1,79 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class NullnessCasts { + /** + * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that + * that conversion is safe. + * + *

This method is intended to help with usages of type parameters that have {@linkplain + * ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null + * types (or if the type is a non-variable type, like {@code String}), then code should almost + * never use this method, preferring instead to call {@code requireNonNull} so as to benefit from + * its runtime check. + * + *

An example use case for this method is in implementing an {@code Iterator} whose {@code + * next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the + * code would be responsible for populating a "real" {@code T} (which might still be the value + * {@code null}!) before returning it to callers. Depending on how the code is structured, a + * nullness analysis might not understand that the field has been populated. To avoid that problem + * without having to add {@code @SuppressWarnings}, the code can call this method. + * + *

Why not just add {@code SuppressWarnings}? The problem is that this method is + * typically useful for {@code return} statements. That leaves the code with two options: Either + * add the suppression to the whole method (which turns off checking for a large section of code), + * or extract a variable, and put the suppression on that. However, a local variable typically + * doesn't work: Because nullness analyses typically infer the nullness of local variables, + * there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the + * analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}. + * (Even if supported added {@code @NonNull}, that would not help, since the problem case + * addressed by this method is the case in which {@code T} has parametric nullness -- and thus its + * value may be legitimately {@code null}.) + */ + @SuppressWarnings("nullness") + @ParametricNullness + static T uncheckedCastNullableTToT(T t) { + return t; + } + + /** + * Returns {@code null} cast to any type. + * + *

This method is intended to help with usages of type parameters that have {@linkplain + * ParametricNullness parametric nullness}. Sometimes, code may receive a null {@code T} but store + * a "null sentinel" to take its place. When the time comes to convert it back to a {@code T} to + * return to a caller, the code needs to a way to return {@code null} from a method that returns + * "plain {@code T}." This API provides that. + */ + @SuppressWarnings({"nullness", "TypeParameterUnusedInFormals", "ReturnMissingNullable"}) + // The warnings are legitimate. Each time we use this method, we document why. + @ParametricNullness + static T uncheckedNull() { + return null; + } + + private NullnessCasts() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/OverflowAvoidingLockSupport.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/OverflowAvoidingLockSupport.java new file mode 100644 index 0000000000000..d5a1eb73794d9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/OverflowAvoidingLockSupport.java @@ -0,0 +1,45 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.locks.LockSupport; + +import static java.lang.Math.min; + +/** + * Works around an android bug, where parking for more than INT_MAX seconds can produce an abort + * signal on 32 bit devices running Android Q. + */ +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +final class OverflowAvoidingLockSupport { + // Represents the max nanoseconds representable on a linux timespec with a 32 bit tv_sec + static final long MAX_NANOSECONDS_THRESHOLD = (1L + Integer.MAX_VALUE) * 1_000_000_000L - 1L; + + private OverflowAvoidingLockSupport() {} + + static void parkNanos(Object blocker, long nanos) { + // Even in the extremely unlikely event that a thread unblocks itself early after only 68 years, + // this is indistinguishable from a spurious wakeup, which LockSupport allows. + LockSupport.parkNanos(blocker, min(nanos, MAX_NANOSECONDS_THRESHOLD)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ParametricNullness.java new file mode 100644 index 0000000000000..4391bf1b5e2aa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *

    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Partially.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Partially.java new file mode 100644 index 0000000000000..354c2ef187665 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Partially.java @@ -0,0 +1,56 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Outer class that exists solely to let us write {@code Partially.GwtIncompatible} instead of plain + * {@code GwtIncompatible}. This is more accurate for {@link Futures#catching}, which is available + * under GWT but with a slightly different signature. + * + *

We can't use {@code PartiallyGwtIncompatible} because then the GWT compiler wouldn't recognize + * it as a {@code GwtIncompatible} annotation. And for {@code Futures.catching}, we need the GWT + * compiler to autostrip the normal server method in order to expose the special, inherited GWT + * version. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +final class Partially { + /** + * The presence of this annotation on an API indicates that the method may be used with the + * Google Web Toolkit (GWT) but that it has some + * restrictions. + */ + @Retention(RetentionPolicy.CLASS) + @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) + @Documented + @interface GwtIncompatible { + String value(); + } + + private Partially() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Platform.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Platform.java new file mode 100644 index 0000000000000..3eb9668160ea6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Platform.java @@ -0,0 +1,43 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import static java.lang.Thread.currentThread; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** Methods factored out so that they can be emulated differently in GWT. */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +final class Platform { + static boolean isInstanceOfThrowableClass(Throwable t, Class expectedClass) { + return expectedClass.isInstance(t); + } + + static void restoreInterruptIfIsInterruptedException(Throwable t) { + checkNotNull(t); // to satisfy NullPointerTester + if (t instanceof InterruptedException) { + currentThread().interrupt(); + } + } + + private Platform() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/RateLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/RateLimiter.java new file mode 100644 index 0000000000000..5064c6bb05d1b --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/RateLimiter.java @@ -0,0 +1,500 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Stopwatch; +import org.apache.iotdb.google.common.util.concurrent.SmoothRateLimiter.SmoothBursty; +import org.apache.iotdb.google.common.util.concurrent.SmoothRateLimiter.SmoothWarmingUp; + +import java.time.Duration; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import static java.lang.Math.max; +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * A rate limiter. Conceptually, a rate limiter distributes permits at a configurable rate. Each + * {@link #acquire()} blocks if necessary until a permit is available, and then takes it. Once + * acquired, permits need not be released. + * + *

{@code RateLimiter} is safe for concurrent use: It will restrict the total rate of calls from + * all threads. Note, however, that it does not guarantee fairness. + * + *

Rate limiters are often used to restrict the rate at which some physical or logical resource + * is accessed. This is in contrast to {@link java.util.concurrent.Semaphore} which restricts the + * number of concurrent accesses instead of the rate (note though that concurrency and rate are + * closely related, e.g. see Little's + * Law). + * + *

A {@code RateLimiter} is defined primarily by the rate at which permits are issued. Absent + * additional configuration, permits will be distributed at a fixed rate, defined in terms of + * permits per second. Permits will be distributed smoothly, with the delay between individual + * permits being adjusted to ensure that the configured rate is maintained. + * + *

It is possible to configure a {@code RateLimiter} to have a warmup period during which time + * the permits issued each second steadily increases until it hits the stable rate. + * + *

As an example, imagine that we have a list of tasks to execute, but we don't want to submit + * more than 2 per second: + * + *

{@code
+ * final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
+ * void submitTasks(List tasks, Executor executor) {
+ *   for (Runnable task : tasks) {
+ *     rateLimiter.acquire(); // may wait
+ *     executor.execute(task);
+ *   }
+ * }
+ * }
+ * + *

As another example, imagine that we produce a stream of data, and we want to cap it at 5kb per + * second. This could be accomplished by requiring a permit per byte, and specifying a rate of 5000 + * permits per second: + * + *

{@code
+ * final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second
+ * void submitPacket(byte[] packet) {
+ *   rateLimiter.acquire(packet.length);
+ *   networkService.send(packet);
+ * }
+ * }
+ * + *

It is important to note that the number of permits requested never affects the + * throttling of the request itself (an invocation to {@code acquire(1)} and an invocation to {@code + * acquire(1000)} will result in exactly the same throttling, if any), but it affects the throttling + * of the next request. I.e., if an expensive task arrives at an idle RateLimiter, it will be + * granted immediately, but it is the next request that will experience extra throttling, + * thus paying for the cost of the expensive task. + * + * @author Dimitris Andreou + * @since 13.0 + */ +// TODO(user): switch to nano precision. A natural unit of cost is "bytes", and a micro precision +// would mean a maximum rate of "1MB/s", which might be small in some cases. +@Beta +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class RateLimiter { + /** + * Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per + * second" (commonly referred to as QPS, queries per second). + * + *

The returned {@code RateLimiter} ensures that on average no more than {@code + * permitsPerSecond} are issued during any given second, with sustained requests being smoothly + * spread over each second. When the incoming request rate exceeds {@code permitsPerSecond} the + * rate limiter will release one permit every {@code (1.0 / permitsPerSecond)} seconds. When the + * rate limiter is unused, bursts of up to {@code permitsPerSecond} permits will be allowed, with + * subsequent requests being smoothly limited at the stable rate of {@code permitsPerSecond}. + * + * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many + * permits become available per second + * @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero + */ + // TODO(user): "This is equivalent to + // {@code createWithCapacity(permitsPerSecond, 1, TimeUnit.SECONDS)}". + public static RateLimiter create(double permitsPerSecond) { + /* + * The default RateLimiter configuration can save the unused permits of up to one second. This + * is to avoid unnecessary stalls in situations like this: A RateLimiter of 1qps, and 4 threads, + * all calling acquire() at these moments: + * + * T0 at 0 seconds + * T1 at 1.05 seconds + * T2 at 2 seconds + * T3 at 3 seconds + * + * Due to the slight delay of T1, T2 would have to sleep till 2.05 seconds, and T3 would also + * have to sleep till 3.05 seconds. + */ + return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer()); + } + + @VisibleForTesting + static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) { + RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */); + rateLimiter.setRate(permitsPerSecond); + return rateLimiter; + } + + /** + * Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per + * second" (commonly referred to as QPS, queries per second), and a warmup period, + * during which the {@code RateLimiter} smoothly ramps up its rate, until it reaches its maximum + * rate at the end of the period (as long as there are enough requests to saturate it). Similarly, + * if the {@code RateLimiter} is left unused for a duration of {@code warmupPeriod}, it + * will gradually return to its "cold" state, i.e. it will go through the same warming up process + * as when it was first created. + * + *

The returned {@code RateLimiter} is intended for cases where the resource that actually + * fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being + * immediately accessed at the stable (maximum) rate. + * + *

The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period will + * follow), and if it is left unused for long enough, it will return to that state. + * + * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many + * permits become available per second + * @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its rate, + * before reaching its stable (maximum) rate + * @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero or {@code + * warmupPeriod} is negative + * @since 28.0 + */ + public static RateLimiter create(double permitsPerSecond, Duration warmupPeriod) { + return create(permitsPerSecond, toNanosSaturated(warmupPeriod), TimeUnit.NANOSECONDS); + } + + /** + * Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per + * second" (commonly referred to as QPS, queries per second), and a warmup period, + * during which the {@code RateLimiter} smoothly ramps up its rate, until it reaches its maximum + * rate at the end of the period (as long as there are enough requests to saturate it). Similarly, + * if the {@code RateLimiter} is left unused for a duration of {@code warmupPeriod}, it + * will gradually return to its "cold" state, i.e. it will go through the same warming up process + * as when it was first created. + * + *

The returned {@code RateLimiter} is intended for cases where the resource that actually + * fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being + * immediately accessed at the stable (maximum) rate. + * + *

The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period will + * follow), and if it is left unused for long enough, it will return to that state. + * + * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many + * permits become available per second + * @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its rate, + * before reaching its stable (maximum) rate + * @param unit the time unit of the warmupPeriod argument + * @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero or {@code + * warmupPeriod} is negative + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) { + checkArgument(warmupPeriod >= 0, "warmupPeriod must not be negative: %s", warmupPeriod); + return create( + permitsPerSecond, warmupPeriod, unit, 3.0, SleepingStopwatch.createFromSystemTimer()); + } + + @VisibleForTesting + static RateLimiter create( + double permitsPerSecond, + long warmupPeriod, + TimeUnit unit, + double coldFactor, + SleepingStopwatch stopwatch) { + RateLimiter rateLimiter = new SmoothWarmingUp(stopwatch, warmupPeriod, unit, coldFactor); + rateLimiter.setRate(permitsPerSecond); + return rateLimiter; + } + + /** + * The underlying timer; used both to measure elapsed time and sleep as necessary. A separate + * object to facilitate testing. + */ + private final SleepingStopwatch stopwatch; + + // Can't be initialized in the constructor because mocks don't call the constructor. + private volatile Object mutexDoNotUseDirectly; + + private Object mutex() { + Object mutex = mutexDoNotUseDirectly; + if (mutex == null) { + synchronized (this) { + mutex = mutexDoNotUseDirectly; + if (mutex == null) { + mutexDoNotUseDirectly = mutex = new Object(); + } + } + } + return mutex; + } + + RateLimiter(SleepingStopwatch stopwatch) { + this.stopwatch = checkNotNull(stopwatch); + } + + /** + * Updates the stable rate of this {@code RateLimiter}, that is, the {@code permitsPerSecond} + * argument provided in the factory method that constructed the {@code RateLimiter}. Currently + * throttled threads will not be awakened as a result of this invocation, thus they do not + * observe the new rate; only subsequent requests will. + * + *

Note though that, since each request repays (by waiting, if necessary) the cost of the + * previous request, this means that the very next request after an invocation to {@code + * setRate} will not be affected by the new rate; it will pay the cost of the previous request, + * which is in terms of the previous rate. + * + *

The behavior of the {@code RateLimiter} is not modified in any other way, e.g. if the {@code + * RateLimiter} was configured with a warmup period of 20 seconds, it still has a warmup period of + * 20 seconds after this method invocation. + * + * @param permitsPerSecond the new stable rate of this {@code RateLimiter} + * @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero + */ + public final void setRate(double permitsPerSecond) { + checkArgument( + permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive"); + synchronized (mutex()) { + doSetRate(permitsPerSecond, stopwatch.readMicros()); + } + } + + abstract void doSetRate(double permitsPerSecond, long nowMicros); + + /** + * Returns the stable rate (as {@code permits per seconds}) with which this {@code RateLimiter} is + * configured with. The initial value of this is the same as the {@code permitsPerSecond} argument + * passed in the factory method that produced this {@code RateLimiter}, and it is only updated + * after invocations to {@linkplain #setRate}. + */ + public final double getRate() { + synchronized (mutex()) { + return doGetRate(); + } + } + + abstract double doGetRate(); + + /** + * Acquires a single permit from this {@code RateLimiter}, blocking until the request can be + * granted. Tells the amount of time slept, if any. + * + *

This method is equivalent to {@code acquire(1)}. + * + * @return time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited + * @since 16.0 (present in 13.0 with {@code void} return type}) + */ + public double acquire() { + return acquire(1); + } + + /** + * Acquires the given number of permits from this {@code RateLimiter}, blocking until the request + * can be granted. Tells the amount of time slept, if any. + * + * @param permits the number of permits to acquire + * @return time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited + * @throws IllegalArgumentException if the requested number of permits is negative or zero + * @since 16.0 (present in 13.0 with {@code void} return type}) + */ + public double acquire(int permits) { + long microsToWait = reserve(permits); + stopwatch.sleepMicrosUninterruptibly(microsToWait); + return 1.0 * microsToWait / SECONDS.toMicros(1L); + } + + /** + * Reserves the given number of permits from this {@code RateLimiter} for future use, returning + * the number of microseconds until the reservation can be consumed. + * + * @return time in microseconds to wait until the resource can be acquired, never negative + */ + final long reserve(int permits) { + checkPermits(permits); + synchronized (mutex()) { + return reserveAndGetWaitLength(permits, stopwatch.readMicros()); + } + } + + /** + * Acquires a permit from this {@code RateLimiter} if it can be obtained without exceeding the + * specified {@code timeout}, or returns {@code false} immediately (without waiting) if the permit + * would not have been granted before the timeout expired. + * + *

This method is equivalent to {@code tryAcquire(1, timeout)}. + * + * @param timeout the maximum time to wait for the permit. Negative values are treated as zero. + * @return {@code true} if the permit was acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + * @since 28.0 + */ + public boolean tryAcquire(Duration timeout) { + return tryAcquire(1, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Acquires a permit from this {@code RateLimiter} if it can be obtained without exceeding the + * specified {@code timeout}, or returns {@code false} immediately (without waiting) if the permit + * would not have been granted before the timeout expired. + * + *

This method is equivalent to {@code tryAcquire(1, timeout, unit)}. + * + * @param timeout the maximum time to wait for the permit. Negative values are treated as zero. + * @param unit the time unit of the timeout argument + * @return {@code true} if the permit was acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean tryAcquire(long timeout, TimeUnit unit) { + return tryAcquire(1, timeout, unit); + } + + /** + * Acquires permits from this {@link RateLimiter} if it can be acquired immediately without delay. + * + *

This method is equivalent to {@code tryAcquire(permits, 0, anyUnit)}. + * + * @param permits the number of permits to acquire + * @return {@code true} if the permits were acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + * @since 14.0 + */ + public boolean tryAcquire(int permits) { + return tryAcquire(permits, 0, MICROSECONDS); + } + + /** + * Acquires a permit from this {@link RateLimiter} if it can be acquired immediately without + * delay. + * + *

This method is equivalent to {@code tryAcquire(1)}. + * + * @return {@code true} if the permit was acquired, {@code false} otherwise + * @since 14.0 + */ + public boolean tryAcquire() { + return tryAcquire(1, 0, MICROSECONDS); + } + + /** + * Acquires the given number of permits from this {@code RateLimiter} if it can be obtained + * without exceeding the specified {@code timeout}, or returns {@code false} immediately (without + * waiting) if the permits would not have been granted before the timeout expired. + * + * @param permits the number of permits to acquire + * @param timeout the maximum time to wait for the permits. Negative values are treated as zero. + * @return {@code true} if the permits were acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + * @since 28.0 + */ + public boolean tryAcquire(int permits, Duration timeout) { + return tryAcquire(permits, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Acquires the given number of permits from this {@code RateLimiter} if it can be obtained + * without exceeding the specified {@code timeout}, or returns {@code false} immediately (without + * waiting) if the permits would not have been granted before the timeout expired. + * + * @param permits the number of permits to acquire + * @param timeout the maximum time to wait for the permits. Negative values are treated as zero. + * @param unit the time unit of the timeout argument + * @return {@code true} if the permits were acquired, {@code false} otherwise + * @throws IllegalArgumentException if the requested number of permits is negative or zero + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public boolean tryAcquire(int permits, long timeout, TimeUnit unit) { + long timeoutMicros = max(unit.toMicros(timeout), 0); + checkPermits(permits); + long microsToWait; + synchronized (mutex()) { + long nowMicros = stopwatch.readMicros(); + if (!canAcquire(nowMicros, timeoutMicros)) { + return false; + } else { + microsToWait = reserveAndGetWaitLength(permits, nowMicros); + } + } + stopwatch.sleepMicrosUninterruptibly(microsToWait); + return true; + } + + private boolean canAcquire(long nowMicros, long timeoutMicros) { + return queryEarliestAvailable(nowMicros) - timeoutMicros <= nowMicros; + } + + /** + * Reserves next ticket and returns the wait time that the caller must wait for. + * + * @return the required wait time, never negative + */ + final long reserveAndGetWaitLength(int permits, long nowMicros) { + long momentAvailable = reserveEarliestAvailable(permits, nowMicros); + return max(momentAvailable - nowMicros, 0); + } + + /** + * Returns the earliest time that permits are available (with one caveat). + * + * @return the time that permits are available, or, if permits are available immediately, an + * arbitrary past or present time + */ + abstract long queryEarliestAvailable(long nowMicros); + + /** + * Reserves the requested number of permits and returns the time that those permits can be used + * (with one caveat). + * + * @return the time that the permits may be used, or, if the permits may be used immediately, an + * arbitrary past or present time + */ + abstract long reserveEarliestAvailable(int permits, long nowMicros); + + @Override + public String toString() { + return String.format(Locale.ROOT, "RateLimiter[stableRate=%3.1fqps]", getRate()); + } + + abstract static class SleepingStopwatch { + /** Constructor for use by subclasses. */ + protected SleepingStopwatch() {} + + /* + * We always hold the mutex when calling this. TODO(cpovirk): Is that important? Perhaps we need + * to guarantee that each call to reserveEarliestAvailable, etc. sees a value >= the previous? + * Also, is it OK that we don't hold the mutex when sleeping? + */ + protected abstract long readMicros(); + + protected abstract void sleepMicrosUninterruptibly(long micros); + + public static SleepingStopwatch createFromSystemTimer() { + return new SleepingStopwatch() { + final Stopwatch stopwatch = Stopwatch.createStarted(); + + @Override + protected long readMicros() { + return stopwatch.elapsed(MICROSECONDS); + } + + @Override + protected void sleepMicrosUninterruptibly(long micros) { + if (micros > 0) { + Uninterruptibles.sleepUninterruptibly(micros, MICROSECONDS); + } + } + }; + } + } + + private static void checkPermits(int permits) { + checkArgument(permits > 0, "Requested permits (%s) must be positive", permits); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Runnables.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Runnables.java new file mode 100644 index 0000000000000..e7ea91515ada5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Runnables.java @@ -0,0 +1,45 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Static utility methods pertaining to the {@link Runnable} interface. + * + * @since 16.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class Runnables { + + private static final Runnable EMPTY_RUNNABLE = + new Runnable() { + @Override + public void run() {} + }; + + /** Returns a {@link Runnable} instance that does nothing when run. */ + public static Runnable doNothing() { + return EMPTY_RUNNABLE; + } + + private Runnables() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SequentialExecutor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SequentialExecutor.java new file mode 100644 index 0000000000000..96206ab49e04a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SequentialExecutor.java @@ -0,0 +1,269 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.lang.System.identityHashCode; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.IDLE; +import static org.apache.iotdb.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.QUEUED; +import static org.apache.iotdb.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.QUEUING; +import static org.apache.iotdb.google.common.util.concurrent.SequentialExecutor.WorkerRunningState.RUNNING; + +/** + * Executor ensuring that all Runnables submitted are executed in order, using the provided + * Executor, and sequentially such that no two will ever be running at the same time. + * + *

Tasks submitted to {@link #execute(Runnable)} are executed in FIFO order. + * + *

The execution of tasks is done by one thread as long as there are tasks left in the queue. + * When a task is {@linkplain Thread#interrupt interrupted}, execution of subsequent tasks + * continues. See {@link QueueWorker#workOnQueue} for details. + * + *

{@code RuntimeException}s thrown by tasks are simply logged and the executor keeps trucking. + * If an {@code Error} is thrown, the error will propagate and execution will stop until it is + * restarted by a call to {@link #execute}. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class SequentialExecutor implements Executor { + private static final Logger log = Logger.getLogger(SequentialExecutor.class.getName()); + + enum WorkerRunningState { + /** Runnable is not running and not queued for execution */ + IDLE, + /** Runnable is not running, but is being queued for execution */ + QUEUING, + /** runnable has been submitted but has not yet begun execution */ + QUEUED, + RUNNING, + } + + /** Underlying executor that all submitted Runnable objects are run on. */ + private final Executor executor; + + private final Deque queue = new ArrayDeque<>(); + + /** see {@link WorkerRunningState} */ + private WorkerRunningState workerRunningState = IDLE; + + /** + * This counter prevents an ABA issue where a thread may successfully schedule the worker, the + * worker runs and exhausts the queue, another thread enqueues a task and fails to schedule the + * worker, and then the first thread's call to delegate.execute() returns. Without this counter, + * it would observe the QUEUING state and set it to QUEUED, and the worker would never be + * scheduled again for future submissions. + */ + private long workerRunCount = 0; + + private final QueueWorker worker = new QueueWorker(); + + /** Use {@link MoreExecutors#newSequentialExecutor} */ + SequentialExecutor(Executor executor) { + this.executor = Preconditions.checkNotNull(executor); + } + + /** + * Adds a task to the queue and makes sure a worker thread is running. + * + *

If this method throws, e.g. a {@code RejectedExecutionException} from the delegate executor, + * execution of tasks will stop until a call to this method is made. + */ + @Override + public void execute(Runnable task) { + checkNotNull(task); + Runnable submittedTask; + long oldRunCount; + synchronized (queue) { + // If the worker is already running (or execute() on the delegate returned successfully, and + // the worker has yet to start) then we don't need to start the worker. + if (workerRunningState == RUNNING || workerRunningState == QUEUED) { + queue.add(task); + return; + } + + oldRunCount = workerRunCount; + + // If the worker is not yet running, the delegate Executor might reject our attempt to start + // it. To preserve FIFO order and failure atomicity of rejected execution when the same + // Runnable is executed more than once, allocate a wrapper that we know is safe to remove by + // object identity. + // A data structure that returned a removal handle from add() would allow eliminating this + // allocation. + submittedTask = + new Runnable() { + @Override + public void run() { + task.run(); + } + + @Override + public String toString() { + return task.toString(); + } + }; + queue.add(submittedTask); + workerRunningState = QUEUING; + } + + try { + executor.execute(worker); + } catch (RuntimeException | Error t) { + synchronized (queue) { + boolean removed = + (workerRunningState == IDLE || workerRunningState == QUEUING) + && queue.removeLastOccurrence(submittedTask); + // If the delegate is directExecutor(), the submitted runnable could have thrown a REE. But + // that's handled by the log check that catches RuntimeExceptions in the queue worker. + if (!(t instanceof RejectedExecutionException) || removed) { + throw t; + } + } + return; + } + + /* + * This is an unsynchronized read! After the read, the function returns immediately or acquires + * the lock to check again. Since an IDLE state was observed inside the preceding synchronized + * block, and reference field assignment is atomic, this may save reacquiring the lock when + * another thread or the worker task has cleared the count and set the state. + * + *

When {@link #executor} is a directExecutor(), the value written to + * {@code workerRunningState} will be available synchronously, and behaviour will be + * deterministic. + */ + @SuppressWarnings("GuardedBy") + boolean alreadyMarkedQueued = workerRunningState != QUEUING; + if (alreadyMarkedQueued) { + return; + } + synchronized (queue) { + if (workerRunCount == oldRunCount && workerRunningState == QUEUING) { + workerRunningState = QUEUED; + } + } + } + + /** Worker that runs tasks from {@link #queue} until it is empty. */ + private final class QueueWorker implements Runnable { + Runnable task; + + @Override + public void run() { + try { + workOnQueue(); + } catch (Error e) { + synchronized (queue) { + workerRunningState = IDLE; + } + throw e; + // The execution of a task has ended abnormally. + // We could have tasks left in the queue, so should perhaps try to restart a worker, + // but then the Error will get delayed if we are using a direct (same thread) executor. + } + } + + /** + * Continues executing tasks from {@link #queue} until it is empty. + * + *

The thread's interrupt bit is cleared before execution of each task. + * + *

If the Thread in use is interrupted before or during execution of the tasks in {@link + * #queue}, the Executor will complete its tasks, and then restore the interruption. This means + * that once the Thread returns to the Executor that this Executor composes, the interruption + * will still be present. If the composed Executor is an ExecutorService, it can respond to + * shutdown() by returning tasks queued on that Thread after {@link #worker} drains the queue. + */ + private void workOnQueue() { + boolean interruptedDuringTask = false; + boolean hasSetRunning = false; + try { + while (true) { + synchronized (queue) { + // Choose whether this thread will run or not after acquiring the lock on the first + // iteration + if (!hasSetRunning) { + if (workerRunningState == RUNNING) { + // Don't want to have two workers pulling from the queue. + return; + } else { + // Increment the run counter to avoid the ABA problem of a submitter marking the + // thread as QUEUED after it already ran and exhausted the queue before returning + // from execute(). + workerRunCount++; + workerRunningState = RUNNING; + hasSetRunning = true; + } + } + task = queue.poll(); + if (task == null) { + workerRunningState = IDLE; + return; + } + } + // Remove the interrupt bit before each task. The interrupt is for the "current task" when + // it is sent, so subsequent tasks in the queue should not be caused to be interrupted + // by a previous one in the queue being interrupted. + interruptedDuringTask |= Thread.interrupted(); + try { + task.run(); + } catch (RuntimeException e) { + log.log(Level.SEVERE, "Exception while executing runnable " + task, e); + } finally { + task = null; + } + } + } finally { + // Ensure that if the thread was interrupted at all while processing the task queue, it + // is returned to the delegate Executor interrupted so that it may handle the + // interruption if it likes. + if (interruptedDuringTask) { + Thread.currentThread().interrupt(); + } + } + } + + @SuppressWarnings("GuardedBy") + @Override + public String toString() { + Runnable currentlyRunning = task; + if (currentlyRunning != null) { + return "SequentialExecutorWorker{running=" + currentlyRunning + "}"; + } + return "SequentialExecutorWorker{state=" + workerRunningState + "}"; + } + } + + @Override + public String toString() { + return "SequentialExecutor@" + identityHashCode(this) + "{" + executor + "}"; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Service.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Service.java new file mode 100644 index 0000000000000..171e056193cfa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Service.java @@ -0,0 +1,292 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * An object with an operational state, plus asynchronous {@link #startAsync()} and {@link + * #stopAsync()} lifecycle methods to transition between states. Example services include + * webservers, RPC servers and timers. + * + *

The normal lifecycle of a service is: + * + *

    + *
  • {@linkplain State#NEW NEW} -> + *
  • {@linkplain State#STARTING STARTING} -> + *
  • {@linkplain State#RUNNING RUNNING} -> + *
  • {@linkplain State#STOPPING STOPPING} -> + *
  • {@linkplain State#TERMINATED TERMINATED} + *
+ * + *

There are deviations from this if there are failures or if {@link Service#stopAsync} is called + * before the {@link Service} reaches the {@linkplain State#RUNNING RUNNING} state. The set of legal + * transitions form a DAG, + * therefore every method of the listener will be called at most once. N.B. The {@link State#FAILED} + * and {@link State#TERMINATED} states are terminal states, once a service enters either of these + * states it cannot ever leave them. + * + *

Implementors of this interface are strongly encouraged to extend one of the abstract classes + * in this package which implement this interface and make the threading and state management + * easier. + * + * @author Jesse Wilson + * @author Luke Sandberg + * @since 9.0 (in 1.0 as {@code org.apache.iotdb.google.common.base.Service}) + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface Service { + /** + * If the service state is {@link State#NEW}, this initiates service startup and returns + * immediately. A stopped service may not be restarted. + * + * @return this + * @throws IllegalStateException if the service is not {@link State#NEW} + * @since 15.0 + */ + Service startAsync(); + + /** Returns {@code true} if this service is {@linkplain State#RUNNING running}. */ + boolean isRunning(); + + /** Returns the lifecycle state of the service. */ + State state(); + + /** + * If the service is {@linkplain State#STARTING starting} or {@linkplain State#RUNNING running}, + * this initiates service shutdown and returns immediately. If the service is {@linkplain + * State#NEW new}, it is {@linkplain State#TERMINATED terminated} without having been started nor + * stopped. If the service has already been stopped, this method returns immediately without + * taking action. + * + * @return this + * @since 15.0 + */ + Service stopAsync(); + + /** + * Waits for the {@link Service} to reach the {@linkplain State#RUNNING running state}. + * + * @throws IllegalStateException if the service reaches a state from which it is not possible to + * enter the {@link State#RUNNING} state. e.g. if the {@code state} is {@code + * State#TERMINATED} when this method is called then this will throw an IllegalStateException. + * @since 15.0 + */ + void awaitRunning(); + + /** + * Waits for the {@link Service} to reach the {@linkplain State#RUNNING running state} for no more + * than the given time. + * + * @param timeout the maximum time to wait + * @throws TimeoutException if the service has not reached the given state within the deadline + * @throws IllegalStateException if the service reaches a state from which it is not possible to + * enter the {@link State#RUNNING RUNNING} state. e.g. if the {@code state} is {@code + * State#TERMINATED} when this method is called then this will throw an IllegalStateException. + * @since 28.0 + */ + default void awaitRunning(Duration timeout) throws TimeoutException { + awaitRunning(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Waits for the {@link Service} to reach the {@linkplain State#RUNNING running state} for no more + * than the given time. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @throws TimeoutException if the service has not reached the given state within the deadline + * @throws IllegalStateException if the service reaches a state from which it is not possible to + * enter the {@link State#RUNNING RUNNING} state. e.g. if the {@code state} is {@code + * State#TERMINATED} when this method is called then this will throw an IllegalStateException. + * @since 15.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException; + + /** + * Waits for the {@link Service} to reach the {@linkplain State#TERMINATED terminated state}. + * + * @throws IllegalStateException if the service {@linkplain State#FAILED fails}. + * @since 15.0 + */ + void awaitTerminated(); + + /** + * Waits for the {@link Service} to reach a terminal state (either {@link Service.State#TERMINATED + * terminated} or {@link Service.State#FAILED failed}) for no more than the given time. + * + * @param timeout the maximum time to wait + * @throws TimeoutException if the service has not reached the given state within the deadline + * @throws IllegalStateException if the service {@linkplain State#FAILED fails}. + * @since 28.0 + */ + default void awaitTerminated(Duration timeout) throws TimeoutException { + awaitTerminated(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Waits for the {@link Service} to reach a terminal state (either {@link Service.State#TERMINATED + * terminated} or {@link Service.State#FAILED failed}) for no more than the given time. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @throws TimeoutException if the service has not reached the given state within the deadline + * @throws IllegalStateException if the service {@linkplain State#FAILED fails}. + * @since 15.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException; + + /** + * Returns the {@link Throwable} that caused this service to fail. + * + * @throws IllegalStateException if this service's state isn't {@linkplain State#FAILED FAILED}. + * @since 14.0 + */ + Throwable failureCause(); + + /** + * Registers a {@link Listener} to be {@linkplain Executor#execute executed} on the given + * executor. The listener will have the corresponding transition method called whenever the + * service changes state. The listener will not have previous state changes replayed, so it is + * suggested that listeners are added before the service starts. + * + *

{@code addListener} guarantees execution ordering across calls to a given listener but not + * across calls to multiple listeners. Specifically, a given listener will have its callbacks + * invoked in the same order as the underlying service enters those states. Additionally, at most + * one of the listener's callbacks will execute at once. However, multiple listeners' callbacks + * may execute concurrently, and listeners may execute in an order different from the one in which + * they were registered. + * + *

RuntimeExceptions thrown by a listener will be caught and logged. Any exception thrown + * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException}) will be caught and + * logged. + * + * @param listener the listener to run when the service changes state is complete + * @param executor the executor in which the listeners callback methods will be run. For fast, + * lightweight listeners that would be safe to execute in any thread, consider {@link + * MoreExecutors#directExecutor}. + * @since 13.0 + */ + void addListener(Listener listener, Executor executor); + + /** + * The lifecycle states of a service. + * + *

The ordering of the {@link State} enum is defined such that if there is a state transition + * from {@code A -> B} then {@code A.compareTo(B) < 0}. N.B. The converse is not true, i.e. if + * {@code A.compareTo(B) < 0} then there is not guaranteed to be a valid state transition + * {@code A -> B}. + * + * @since 9.0 (in 1.0 as {@code org.apache.iotdb.google.common.base.Service.State}) + */ + enum State { + /** A service in this state is inactive. It does minimal work and consumes minimal resources. */ + NEW, + + /** A service in this state is transitioning to {@link #RUNNING}. */ + STARTING, + + /** A service in this state is operational. */ + RUNNING, + + /** A service in this state is transitioning to {@link #TERMINATED}. */ + STOPPING, + + /** + * A service in this state has completed execution normally. It does minimal work and consumes + * minimal resources. + */ + TERMINATED, + + /** + * A service in this state has encountered a problem and may not be operational. It cannot be + * started nor stopped. + */ + FAILED, + } + + /** + * A listener for the various state changes that a {@link Service} goes through in its lifecycle. + * + *

All methods are no-ops by default, implementors should override the ones they care about. + * + * @author Luke Sandberg + * @since 15.0 (present as an interface in 13.0) + */ + abstract class Listener { + /** + * Called when the service transitions from {@linkplain State#NEW NEW} to {@linkplain + * State#STARTING STARTING}. This occurs when {@link Service#startAsync} is called the first + * time. + */ + public void starting() {} + + /** + * Called when the service transitions from {@linkplain State#STARTING STARTING} to {@linkplain + * State#RUNNING RUNNING}. This occurs when a service has successfully started. + */ + public void running() {} + + /** + * Called when the service transitions to the {@linkplain State#STOPPING STOPPING} state. The + * only valid values for {@code from} are {@linkplain State#STARTING STARTING} or {@linkplain + * State#RUNNING RUNNING}. This occurs when {@link Service#stopAsync} is called. + * + * @param from The previous state that is being transitioned from. + */ + public void stopping(State from) {} + + /** + * Called when the service transitions to the {@linkplain State#TERMINATED TERMINATED} state. + * The {@linkplain State#TERMINATED TERMINATED} state is a terminal state in the transition + * diagram. Therefore, if this method is called, no other methods will be called on the {@link + * Listener}. + * + * @param from The previous state that is being transitioned from. Failure can occur in any + * state with the exception of {@linkplain State#FAILED FAILED} and {@linkplain + * State#TERMINATED TERMINATED}. + */ + public void terminated(State from) {} + + /** + * Called when the service transitions to the {@linkplain State#FAILED FAILED} state. The + * {@linkplain State#FAILED FAILED} state is a terminal state in the transition diagram. + * Therefore, if this method is called, no other methods will be called on the {@link Listener}. + * + * @param from The previous state that is being transitioned from. Failure can occur in any + * state with the exception of {@linkplain State#NEW NEW} or {@linkplain State#TERMINATED + * TERMINATED}. + * @param failure The exception that caused the failure. + */ + public void failed(State from, Throwable failure) {} + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ServiceManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ServiceManager.java new file mode 100644 index 0000000000000..88ba404188ab7 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ServiceManager.java @@ -0,0 +1,880 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Stopwatch; +import org.apache.iotdb.google.common.collect.Collections2; +import org.apache.iotdb.google.common.collect.ImmutableCollection; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.ImmutableMap; +import org.apache.iotdb.google.common.collect.ImmutableSet; +import org.apache.iotdb.google.common.collect.ImmutableSetMultimap; +import org.apache.iotdb.google.common.collect.Lists; +import org.apache.iotdb.google.common.collect.Maps; +import org.apache.iotdb.google.common.collect.MultimapBuilder; +import org.apache.iotdb.google.common.collect.Multimaps; +import org.apache.iotdb.google.common.collect.Multiset; +import org.apache.iotdb.google.common.collect.Ordering; +import org.apache.iotdb.google.common.collect.SetMultimap; +import org.apache.iotdb.google.common.util.concurrent.Service.State; + +import java.lang.ref.WeakReference; +import java.time.Duration; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkState; +import static org.apache.iotdb.google.common.base.Predicates.equalTo; +import static org.apache.iotdb.google.common.base.Predicates.in; +import static org.apache.iotdb.google.common.base.Predicates.instanceOf; +import static org.apache.iotdb.google.common.base.Predicates.not; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.FAILED; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.NEW; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.RUNNING; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.STARTING; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.STOPPING; +import static org.apache.iotdb.google.common.util.concurrent.Service.State.TERMINATED; + +/** + * A manager for monitoring and controlling a set of {@linkplain Service services}. This class + * provides methods for {@linkplain #startAsync() starting}, {@linkplain #stopAsync() stopping} and + * {@linkplain #servicesByState inspecting} a collection of {@linkplain Service services}. + * Additionally, users can monitor state transitions with the {@linkplain Listener listener} + * mechanism. + * + *

While it is recommended that service lifecycles be managed via this class, state transitions + * initiated via other mechanisms do not impact the correctness of its methods. For example, if the + * services are started by some mechanism besides {@link #startAsync}, the listeners will be invoked + * when appropriate and {@link #awaitHealthy} will still work as expected. + * + *

Here is a simple example of how to use a {@code ServiceManager} to start a server. + * + *

{@code
+ * class Server {
+ *   public static void main(String[] args) {
+ *     Set services = ...;
+ *     ServiceManager manager = new ServiceManager(services);
+ *     manager.addListener(new Listener() {
+ *         public void stopped() {}
+ *         public void healthy() {
+ *           // Services have been initialized and are healthy, start accepting requests...
+ *         }
+ *         public void failure(Service service) {
+ *           // Something failed, at this point we could log it, notify a load balancer, or take
+ *           // some other action.  For now we will just exit.
+ *           System.exit(1);
+ *         }
+ *       },
+ *       MoreExecutors.directExecutor());
+ *
+ *     Runtime.getRuntime().addShutdownHook(new Thread() {
+ *       public void run() {
+ *         // Give the services 5 seconds to stop to ensure that we are responsive to shutdown
+ *         // requests.
+ *         try {
+ *           manager.stopAsync().awaitStopped(5, TimeUnit.SECONDS);
+ *         } catch (TimeoutException timeout) {
+ *           // stopping timed out
+ *         }
+ *       }
+ *     });
+ *     manager.startAsync();  // start all the services asynchronously
+ *   }
+ * }
+ * }
+ * + *

This class uses the ServiceManager's methods to start all of its services, to respond to + * service failure and to ensure that when the JVM is shutting down all the services are stopped. + * + * @author Luke Sandberg + * @since 14.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class ServiceManager implements ServiceManagerBridge { + private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); + private static final ListenerCallQueue.Event HEALTHY_EVENT = + new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.healthy(); + } + + @Override + public String toString() { + return "healthy()"; + } + }; + private static final ListenerCallQueue.Event STOPPED_EVENT = + new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.stopped(); + } + + @Override + public String toString() { + return "stopped()"; + } + }; + + /** + * A listener for the aggregate state changes of the services that are under management. Users + * that need to listen to more fine-grained events (such as when each particular {@linkplain + * Service service} starts, or terminates), should attach {@linkplain Service.Listener service + * listeners} to each individual service. + * + * @author Luke Sandberg + * @since 15.0 (present as an interface in 14.0) + */ + public abstract static class Listener { + /** + * Called when the service initially becomes healthy. + * + *

This will be called at most once after all the services have entered the {@linkplain + * State#RUNNING running} state. If any services fail during start up or {@linkplain + * State#FAILED fail}/{@linkplain State#TERMINATED terminate} before all other services have + * started {@linkplain State#RUNNING running} then this method will not be called. + */ + public void healthy() {} + + /** + * Called when the all of the component services have reached a terminal state, either + * {@linkplain State#TERMINATED terminated} or {@linkplain State#FAILED failed}. + */ + public void stopped() {} + + /** + * Called when a component service has {@linkplain State#FAILED failed}. + * + * @param service The service that failed. + */ + public void failure(Service service) {} + } + + /** + * An encapsulation of all of the state that is accessed by the {@linkplain ServiceListener + * service listeners}. This is extracted into its own object so that {@link ServiceListener} could + * be made {@code static} and its instances can be safely constructed and added in the {@link + * ServiceManager} constructor without having to close over the partially constructed {@link + * ServiceManager} instance (i.e. avoid leaking a pointer to {@code this}). + */ + private final ServiceManagerState state; + + private final ImmutableList services; + + /** + * Constructs a new instance for managing the given services. + * + * @param services The services to manage + * @throws IllegalArgumentException if not all services are {@linkplain State#NEW new} or if there + * are any duplicate services. + */ + public ServiceManager(Iterable services) { + ImmutableList copy = ImmutableList.copyOf(services); + if (copy.isEmpty()) { + // Having no services causes the manager to behave strangely. Notably, listeners are never + // fired. To avoid this we substitute a placeholder service. + logger.log( + Level.WARNING, + "ServiceManager configured with no services. Is your application configured properly?", + new EmptyServiceManagerWarning()); + copy = ImmutableList.of(new NoOpService()); + } + this.state = new ServiceManagerState(copy); + this.services = copy; + WeakReference stateReference = new WeakReference<>(state); + for (Service service : copy) { + service.addListener(new ServiceListener(service, stateReference), directExecutor()); + // We check the state after adding the listener as a way to ensure that our listener was added + // to a NEW service. + checkArgument(service.state() == NEW, "Can only manage NEW services, %s", service); + } + // We have installed all of our listeners and after this point any state transition should be + // correct. + this.state.markReady(); + } + + /** + * Registers a {@link Listener} to be {@linkplain Executor#execute executed} on the given + * executor. The listener will not have previous state changes replayed, so it is suggested that + * listeners are added before any of the managed services are {@linkplain Service#startAsync + * started}. + * + *

{@code addListener} guarantees execution ordering across calls to a given listener but not + * across calls to multiple listeners. Specifically, a given listener will have its callbacks + * invoked in the same order as the underlying service enters those states. Additionally, at most + * one of the listener's callbacks will execute at once. However, multiple listeners' callbacks + * may execute concurrently, and listeners may execute in an order different from the one in which + * they were registered. + * + *

RuntimeExceptions thrown by a listener will be caught and logged. Any exception thrown + * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException}) will be caught and + * logged. + * + *

When selecting an executor, note that {@code directExecutor} is dangerous in some cases. See + * the discussion in the {@link ListenableFuture#addListener ListenableFuture.addListener} + * documentation. + * + * @param listener the listener to run when the manager changes state + * @param executor the executor in which the listeners callback methods will be run. + */ + public void addListener(Listener listener, Executor executor) { + state.addListener(listener, executor); + } + + /** + * Initiates service {@linkplain Service#startAsync startup} on all the services being managed. It + * is only valid to call this method if all of the services are {@linkplain State#NEW new}. + * + * @return this + * @throws IllegalStateException if any of the Services are not {@link State#NEW new} when the + * method is called. + */ + public ServiceManager startAsync() { + for (Service service : services) { + checkState(service.state() == NEW, "Not all services are NEW, cannot start %s", this); + } + for (Service service : services) { + try { + state.tryStartTiming(service); + service.startAsync(); + } catch (IllegalStateException e) { + // This can happen if the service has already been started or stopped (e.g. by another + // service or listener). Our contract says it is safe to call this method if + // all services were NEW when it was called, and this has already been verified above, so we + // don't propagate the exception. + logger.log(Level.WARNING, "Unable to start Service " + service, e); + } + } + return this; + } + + /** + * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy}. The manager + * will become healthy after all the component services have reached the {@linkplain State#RUNNING + * running} state. + * + * @throws IllegalStateException if the service manager reaches a state from which it cannot + * become {@linkplain #isHealthy() healthy}. + */ + public void awaitHealthy() { + state.awaitHealthy(); + } + + /** + * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy} for no more + * than the given time. The manager will become healthy after all the component services have + * reached the {@linkplain State#RUNNING running} state. + * + * @param timeout the maximum time to wait + * @throws TimeoutException if not all of the services have finished starting within the deadline + * @throws IllegalStateException if the service manager reaches a state from which it cannot + * become {@linkplain #isHealthy() healthy}. + * @since 28.0 + */ + public void awaitHealthy(Duration timeout) throws TimeoutException { + awaitHealthy(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy} for no more + * than the given time. The manager will become healthy after all the component services have + * reached the {@linkplain State#RUNNING running} state. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @throws TimeoutException if not all of the services have finished starting within the deadline + * @throws IllegalStateException if the service manager reaches a state from which it cannot + * become {@linkplain #isHealthy() healthy}. + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException { + state.awaitHealthy(timeout, unit); + } + + /** + * Initiates service {@linkplain Service#stopAsync shutdown} if necessary on all the services + * being managed. + * + * @return this + */ + public ServiceManager stopAsync() { + for (Service service : services) { + service.stopAsync(); + } + return this; + } + + /** + * Waits for the all the services to reach a terminal state. After this method returns all + * services will either be {@linkplain Service.State#TERMINATED terminated} or {@linkplain + * Service.State#FAILED failed}. + */ + public void awaitStopped() { + state.awaitStopped(); + } + + /** + * Waits for the all the services to reach a terminal state for no more than the given time. After + * this method returns all services will either be {@linkplain Service.State#TERMINATED + * terminated} or {@linkplain Service.State#FAILED failed}. + * + * @param timeout the maximum time to wait + * @throws TimeoutException if not all of the services have stopped within the deadline + * @since 28.0 + */ + public void awaitStopped(Duration timeout) throws TimeoutException { + awaitStopped(toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Waits for the all the services to reach a terminal state for no more than the given time. After + * this method returns all services will either be {@linkplain Service.State#TERMINATED + * terminated} or {@linkplain Service.State#FAILED failed}. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @throws TimeoutException if not all of the services have stopped within the deadline + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { + state.awaitStopped(timeout, unit); + } + + /** + * Returns true if all services are currently in the {@linkplain State#RUNNING running} state. + * + *

Users who want more detailed information should use the {@link #servicesByState} method to + * get detailed information about which services are not running. + */ + public boolean isHealthy() { + for (Service service : services) { + if (!service.isRunning()) { + return false; + } + } + return true; + } + + /** + * Provides a snapshot of the current state of all the services under management. + * + *

N.B. This snapshot is guaranteed to be consistent, i.e. the set of states returned will + * correspond to a point in time view of the services. + * + * @since 29.0 (present with return type {@code ImmutableMultimap} since 14.0) + */ + @Override + public ImmutableSetMultimap servicesByState() { + return state.servicesByState(); + } + + /** + * Returns the service load times. This value will only return startup times for services that + * have finished starting. + * + * @return Map of services and their corresponding startup time in millis, the map entries will be + * ordered by startup time. + */ + public ImmutableMap startupTimes() { + return state.startupTimes(); + } + + /** + * Returns the service load times. This value will only return startup times for services that + * have finished starting. + * + * @return Map of services and their corresponding startup time, the map entries will be ordered + * by startup time. + * @since 31.0 + */ + public ImmutableMap startupDurations() { + return ImmutableMap.copyOf( + Maps.transformValues(startupTimes(), Duration::ofMillis)); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(ServiceManager.class) + .add("services", Collections2.filter(services, not(instanceOf(NoOpService.class)))) + .toString(); + } + + /** + * An encapsulation of all the mutable state of the {@link ServiceManager} that needs to be + * accessed by instances of {@link ServiceListener}. + */ + private static final class ServiceManagerState { + final Monitor monitor = new Monitor(); + + final SetMultimap servicesByState = + MultimapBuilder.enumKeys(State.class).linkedHashSetValues().build(); + + final Multiset states = servicesByState.keys(); + + final Map startupTimers = Maps.newIdentityHashMap(); + + /** + * These two booleans are used to mark the state as ready to start. + * + *

{@link #ready}: is set by {@link #markReady} to indicate that all listeners have been + * correctly installed + * + *

{@link #transitioned}: is set by {@link #transitionService} to indicate that some + * transition has been performed. + * + *

Together, they allow us to enforce that all services have their listeners installed prior + * to any service performing a transition, then we can fail in the ServiceManager constructor + * rather than in a Service.Listener callback. + */ + boolean ready; + + boolean transitioned; + + final int numberOfServices; + + /** + * Controls how long to wait for all the services to either become healthy or reach a state from + * which it is guaranteed that it can never become healthy. + */ + final Monitor.Guard awaitHealthGuard = new AwaitHealthGuard(); + + final class AwaitHealthGuard extends Monitor.Guard { + AwaitHealthGuard() { + super(ServiceManagerState.this.monitor); + } + + @Override + public boolean isSatisfied() { + // All services have started or some service has terminated/failed. + return states.count(RUNNING) == numberOfServices + || states.contains(STOPPING) + || states.contains(TERMINATED) + || states.contains(FAILED); + } + } + + /** Controls how long to wait for all services to reach a terminal state. */ + final Monitor.Guard stoppedGuard = new StoppedGuard(); + + final class StoppedGuard extends Monitor.Guard { + StoppedGuard() { + super(ServiceManagerState.this.monitor); + } + + @Override + public boolean isSatisfied() { + return states.count(TERMINATED) + states.count(FAILED) == numberOfServices; + } + } + + /** The listeners to notify during a state transition. */ + final ListenerCallQueue listeners = new ListenerCallQueue<>(); + + /** + * It is implicitly assumed that all the services are NEW and that they will all remain NEW + * until all the Listeners are installed and {@link #markReady()} is called. It is our caller's + * responsibility to only call {@link #markReady()} if all services were new at the time this + * method was called and when all the listeners were installed. + */ + ServiceManagerState(ImmutableCollection services) { + this.numberOfServices = services.size(); + servicesByState.putAll(NEW, services); + } + + /** + * Attempts to start the timer immediately prior to the service being started via {@link + * Service#startAsync()}. + */ + void tryStartTiming(Service service) { + monitor.enter(); + try { + Stopwatch stopwatch = startupTimers.get(service); + if (stopwatch == null) { + startupTimers.put(service, Stopwatch.createStarted()); + } + } finally { + monitor.leave(); + } + } + + /** + * Marks the {@link State} as ready to receive transitions. Returns true if no transitions have + * been observed yet. + */ + void markReady() { + monitor.enter(); + try { + if (!transitioned) { + // nothing has transitioned since construction, good. + ready = true; + } else { + // This should be an extremely rare race condition. + List servicesInBadStates = Lists.newArrayList(); + for (Service service : servicesByState().values()) { + if (service.state() != NEW) { + servicesInBadStates.add(service); + } + } + throw new IllegalArgumentException( + "Services started transitioning asynchronously before " + + "the ServiceManager was constructed: " + + servicesInBadStates); + } + } finally { + monitor.leave(); + } + } + + void addListener(Listener listener, Executor executor) { + listeners.addListener(listener, executor); + } + + void awaitHealthy() { + monitor.enterWhenUninterruptibly(awaitHealthGuard); + try { + checkHealthy(); + } finally { + monitor.leave(); + } + } + + void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException { + monitor.enter(); + try { + if (!monitor.waitForUninterruptibly(awaitHealthGuard, timeout, unit)) { + throw new TimeoutException( + "Timeout waiting for the services to become healthy. The " + + "following services have not started: " + + Multimaps.filterKeys(servicesByState, in(ImmutableSet.of(NEW, STARTING)))); + } + checkHealthy(); + } finally { + monitor.leave(); + } + } + + void awaitStopped() { + monitor.enterWhenUninterruptibly(stoppedGuard); + monitor.leave(); + } + + void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { + monitor.enter(); + try { + if (!monitor.waitForUninterruptibly(stoppedGuard, timeout, unit)) { + throw new TimeoutException( + "Timeout waiting for the services to stop. The following " + + "services have not stopped: " + + Multimaps.filterKeys(servicesByState, not(in(EnumSet.of(TERMINATED, FAILED))))); + } + } finally { + monitor.leave(); + } + } + + ImmutableSetMultimap servicesByState() { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + monitor.enter(); + try { + for (Entry entry : servicesByState.entries()) { + if (!(entry.getValue() instanceof NoOpService)) { + builder.put(entry); + } + } + } finally { + monitor.leave(); + } + return builder.build(); + } + + ImmutableMap startupTimes() { + List> loadTimes; + monitor.enter(); + try { + loadTimes = Lists.newArrayListWithCapacity(startupTimers.size()); + // N.B. There will only be an entry in the map if the service has started + for (Entry entry : startupTimers.entrySet()) { + Service service = entry.getKey(); + Stopwatch stopwatch = entry.getValue(); + if (!stopwatch.isRunning() && !(service instanceof NoOpService)) { + loadTimes.add(Maps.immutableEntry(service, stopwatch.elapsed(MILLISECONDS))); + } + } + } finally { + monitor.leave(); + } + Collections.sort( + loadTimes, + Ordering.natural() + .onResultOf( + new Function, Long>() { + @Override + public Long apply(Entry input) { + return input.getValue(); + } + })); + return ImmutableMap.copyOf(loadTimes); + } + + /** + * Updates the state with the given service transition. + * + *

This method performs the main logic of ServiceManager in the following steps. + * + *

    + *
  1. Update the {@link #servicesByState()} + *
  2. Update the {@link #startupTimers} + *
  3. Based on the new state queue listeners to run + *
  4. Run the listeners (outside of the lock) + *
+ */ + void transitionService(final Service service, State from, State to) { + checkNotNull(service); + checkArgument(from != to); + monitor.enter(); + try { + transitioned = true; + if (!ready) { + return; + } + // Update state. + checkState( + servicesByState.remove(from, service), + "Service %s not at the expected location in the state map %s", + service, + from); + checkState( + servicesByState.put(to, service), + "Service %s in the state map unexpectedly at %s", + service, + to); + // Update the timer + Stopwatch stopwatch = startupTimers.get(service); + if (stopwatch == null) { + // This means the service was started by some means other than ServiceManager.startAsync + stopwatch = Stopwatch.createStarted(); + startupTimers.put(service, stopwatch); + } + if (to.compareTo(RUNNING) >= 0 && stopwatch.isRunning()) { + // N.B. if we miss the STARTING event then we may never record a startup time. + stopwatch.stop(); + if (!(service instanceof NoOpService)) { + logger.log(Level.FINE, "Started {0} in {1}.", new Object[] {service, stopwatch}); + } + } + // Queue our listeners + + // Did a service fail? + if (to == FAILED) { + enqueueFailedEvent(service); + } + + if (states.count(RUNNING) == numberOfServices) { + // This means that the manager is currently healthy. N.B. If other threads call isHealthy + // they are not guaranteed to get 'true', because any service could fail right now. + enqueueHealthyEvent(); + } else if (states.count(TERMINATED) + states.count(FAILED) == numberOfServices) { + enqueueStoppedEvent(); + } + } finally { + monitor.leave(); + // Run our executors outside of the lock + dispatchListenerEvents(); + } + } + + void enqueueStoppedEvent() { + listeners.enqueue(STOPPED_EVENT); + } + + void enqueueHealthyEvent() { + listeners.enqueue(HEALTHY_EVENT); + } + + void enqueueFailedEvent(final Service service) { + listeners.enqueue( + new ListenerCallQueue.Event() { + @Override + public void call(Listener listener) { + listener.failure(service); + } + + @Override + public String toString() { + return "failed({service=" + service + "})"; + } + }); + } + + /** Attempts to execute all the listeners in {@link #listeners}. */ + void dispatchListenerEvents() { + checkState( + !monitor.isOccupiedByCurrentThread(), + "It is incorrect to execute listeners with the monitor held."); + listeners.dispatch(); + } + + void checkHealthy() { + if (states.count(RUNNING) != numberOfServices) { + IllegalStateException exception = + new IllegalStateException( + "Expected to be healthy after starting. The following services are not running: " + + Multimaps.filterKeys(servicesByState, not(equalTo(RUNNING)))); + for (Service service : servicesByState.get(State.FAILED)) { + exception.addSuppressed(new FailedService(service)); + } + throw exception; + } + } + } + + /** + * A {@link Service} that wraps another service and times how long it takes for it to start and + * also calls the {@link ServiceManagerState#transitionService(Service, State, State)}, to record + * the state transitions. + */ + private static final class ServiceListener extends Service.Listener { + final Service service; + // We store the state in a weak reference to ensure that if something went wrong while + // constructing the ServiceManager we don't pointlessly keep updating the state. + final WeakReference state; + + ServiceListener(Service service, WeakReference state) { + this.service = service; + this.state = state; + } + + @Override + public void starting() { + ServiceManagerState state = this.state.get(); + if (state != null) { + state.transitionService(service, NEW, STARTING); + if (!(service instanceof NoOpService)) { + logger.log(Level.FINE, "Starting {0}.", service); + } + } + } + + @Override + public void running() { + ServiceManagerState state = this.state.get(); + if (state != null) { + state.transitionService(service, STARTING, RUNNING); + } + } + + @Override + public void stopping(State from) { + ServiceManagerState state = this.state.get(); + if (state != null) { + state.transitionService(service, from, STOPPING); + } + } + + @Override + public void terminated(State from) { + ServiceManagerState state = this.state.get(); + if (state != null) { + if (!(service instanceof NoOpService)) { + logger.log( + Level.FINE, + "Service {0} has terminated. Previous state was: {1}", + new Object[] {service, from}); + } + state.transitionService(service, from, TERMINATED); + } + } + + @Override + public void failed(State from, Throwable failure) { + ServiceManagerState state = this.state.get(); + if (state != null) { + // Log before the transition, so that if the process exits in response to server failure, + // there is a higher likelihood that the cause will be in the logs. + boolean log = !(service instanceof NoOpService); + /* + * We have already exposed startup exceptions to the user in the form of suppressed + * exceptions. We don't need to log those exceptions again. + */ + log &= from != State.STARTING; + if (log) { + logger.log( + Level.SEVERE, + "Service " + service + " has failed in the " + from + " state.", + failure); + } + state.transitionService(service, from, FAILED); + } + } + } + + /** + * A {@link Service} instance that does nothing. This is only useful as a placeholder to ensure + * that the {@link ServiceManager} functions properly even when it is managing no services. + * + *

The use of this class is considered an implementation detail of ServiceManager and as such + * it is excluded from {@link #servicesByState}, {@link #startupTimes}, {@link #toString} and all + * logging statements. + */ + private static final class NoOpService extends AbstractService { + @Override + protected void doStart() { + notifyStarted(); + } + + @Override + protected void doStop() { + notifyStopped(); + } + } + + /** This is never thrown but only used for logging. */ + private static final class EmptyServiceManagerWarning extends Throwable {} + + private static final class FailedService extends Throwable { + FailedService(Service service) { + super( + service.toString(), + service.failureCause(), + false /* don't enable suppression */, + false /* don't calculate a stack trace. */); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ServiceManagerBridge.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ServiceManagerBridge.java new file mode 100644 index 0000000000000..d40ba554331ed --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ServiceManagerBridge.java @@ -0,0 +1,37 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ImmutableMultimap; +import org.apache.iotdb.google.common.util.concurrent.Service.State; + +/** + * Superinterface of {@link ServiceManager} to introduce a bridge method for {@code + * servicesByState()}, to ensure binary compatibility with older Guava versions that specified + * {@code servicesByState()} to return {@code ImmutableMultimap}. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +interface ServiceManagerBridge { + ImmutableMultimap servicesByState(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SettableFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SettableFuture.java new file mode 100644 index 0000000000000..d61783814895a --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SettableFuture.java @@ -0,0 +1,64 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * A {@link ListenableFuture} whose result can be set by a {@link #set(Object)}, {@link + * #setException(Throwable)} or {@link #setFuture(ListenableFuture)} call. It can also, like any + * other {@code Future}, be {@linkplain #cancel cancelled}. + * + *

{@code SettableFuture} is the recommended {@code ListenableFuture} implementation when your + * task cannot be implemented with {@link ListeningExecutorService}, the various {@link Futures} + * utility methods, or {@link ListenableFutureTask}. Those APIs have less opportunity for developer + * error. If your needs are more complex than {@code SettableFuture} supports, use {@link + * AbstractFuture}, which offers an extensible version of the API. + * + * @author Sven Mawson + * @since 9.0 (in 1.0 as {@code ValueFuture}) + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public final class SettableFuture extends AbstractFuture.TrustedFuture { + /** + * Creates a new {@code SettableFuture} that can be completed or cancelled by a later method call. + */ + public static SettableFuture create() { + return new SettableFuture<>(); + } + + @Override + public boolean set(@ParametricNullness V value) { + return super.set(value); + } + + @Override + public boolean setException(Throwable throwable) { + return super.setException(throwable); + } + + @Override + public boolean setFuture(ListenableFuture future) { + return super.setFuture(future); + } + + private SettableFuture() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SimpleTimeLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SimpleTimeLimiter.java new file mode 100644 index 0000000000000..570f7c092b7f3 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SimpleTimeLimiter.java @@ -0,0 +1,286 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ObjectArrays; +import org.apache.iotdb.google.common.collect.Sets; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; + +/** + * A TimeLimiter that runs method calls in the background using an {@link ExecutorService}. If the + * time limit expires for a given method call, the thread running the call will be interrupted. + * + * @author Kevin Bourrillion + * @author Jens Nyman + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class SimpleTimeLimiter implements TimeLimiter { + + private final ExecutorService executor; + + private SimpleTimeLimiter(ExecutorService executor) { + this.executor = checkNotNull(executor); + } + + /** + * Creates a TimeLimiter instance using the given executor service to execute method calls. + * + *

Warning: using a bounded executor may be counterproductive! If the thread pool fills + * up, any time callers spend waiting for a thread may count toward their time limit, and in this + * case the call may even time out before the target method is ever invoked. + * + * @param executor the ExecutorService that will execute the method calls on the target objects; + * for example, a {@link Executors#newCachedThreadPool()}. + * @since 22.0 + */ + public static SimpleTimeLimiter create(ExecutorService executor) { + return new SimpleTimeLimiter(executor); + } + + @Override + public T newProxy( + T target, Class interfaceType, long timeoutDuration, TimeUnit timeoutUnit) { + checkNotNull(target); + checkNotNull(interfaceType); + checkNotNull(timeoutUnit); + checkPositiveTimeout(timeoutDuration); + checkArgument(interfaceType.isInterface(), "interfaceType must be an interface type"); + + Set interruptibleMethods = findInterruptibleMethods(interfaceType); + + InvocationHandler handler = + new InvocationHandler() { + @Override + public Object invoke(Object obj, Method method, Object[] args) throws Throwable { + Callable callable = + () -> { + try { + return method.invoke(target, args); + } catch (InvocationTargetException e) { + throw throwCause(e, false /* combineStackTraces */); + } + }; + return callWithTimeout( + callable, timeoutDuration, timeoutUnit, interruptibleMethods.contains(method)); + } + }; + return newProxy(interfaceType, handler); + } + + // TODO: replace with version in common.reflect if and when it's open-sourced + private static T newProxy(Class interfaceType, InvocationHandler handler) { + Object object = + Proxy.newProxyInstance( + interfaceType.getClassLoader(), new Class[] {interfaceType}, handler); + return interfaceType.cast(object); + } + + @ParametricNullness + private T callWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit, boolean amInterruptible) + throws Exception { + checkNotNull(callable); + checkNotNull(timeoutUnit); + checkPositiveTimeout(timeoutDuration); + + Future future = executor.submit(callable); + + try { + return amInterruptible + ? future.get(timeoutDuration, timeoutUnit) + : getUninterruptibly(future, timeoutDuration, timeoutUnit); + } catch (InterruptedException e) { + future.cancel(true); + throw e; + } catch (ExecutionException e) { + throw throwCause(e, true /* combineStackTraces */); + } catch (TimeoutException e) { + future.cancel(true); + throw new UncheckedTimeoutException(e); + } + } + + @Override + @ParametricNullness + public T callWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException, InterruptedException, ExecutionException { + checkNotNull(callable); + checkNotNull(timeoutUnit); + checkPositiveTimeout(timeoutDuration); + + Future future = executor.submit(callable); + + try { + return future.get(timeoutDuration, timeoutUnit); + } catch (InterruptedException | TimeoutException e) { + future.cancel(true /* mayInterruptIfRunning */); + throw e; + } catch (ExecutionException e) { + wrapAndThrowExecutionExceptionOrError(e.getCause()); + throw new AssertionError(); + } + } + + @Override + @ParametricNullness + public T callUninterruptiblyWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException, ExecutionException { + checkNotNull(callable); + checkNotNull(timeoutUnit); + checkPositiveTimeout(timeoutDuration); + + Future future = executor.submit(callable); + + try { + return getUninterruptibly(future, timeoutDuration, timeoutUnit); + } catch (TimeoutException e) { + future.cancel(true /* mayInterruptIfRunning */); + throw e; + } catch (ExecutionException e) { + wrapAndThrowExecutionExceptionOrError(e.getCause()); + throw new AssertionError(); + } + } + + @Override + public void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException, InterruptedException { + checkNotNull(runnable); + checkNotNull(timeoutUnit); + checkPositiveTimeout(timeoutDuration); + + Future future = executor.submit(runnable); + + try { + future.get(timeoutDuration, timeoutUnit); + } catch (InterruptedException | TimeoutException e) { + future.cancel(true /* mayInterruptIfRunning */); + throw e; + } catch (ExecutionException e) { + wrapAndThrowRuntimeExecutionExceptionOrError(e.getCause()); + throw new AssertionError(); + } + } + + @Override + public void runUninterruptiblyWithTimeout( + Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) throws TimeoutException { + checkNotNull(runnable); + checkNotNull(timeoutUnit); + checkPositiveTimeout(timeoutDuration); + + Future future = executor.submit(runnable); + + try { + getUninterruptibly(future, timeoutDuration, timeoutUnit); + } catch (TimeoutException e) { + future.cancel(true /* mayInterruptIfRunning */); + throw e; + } catch (ExecutionException e) { + wrapAndThrowRuntimeExecutionExceptionOrError(e.getCause()); + throw new AssertionError(); + } + } + + private static Exception throwCause(Exception e, boolean combineStackTraces) throws Exception { + Throwable cause = e.getCause(); + if (cause == null) { + throw e; + } + if (combineStackTraces) { + StackTraceElement[] combined = + ObjectArrays.concat(cause.getStackTrace(), e.getStackTrace(), StackTraceElement.class); + cause.setStackTrace(combined); + } + if (cause instanceof Exception) { + throw (Exception) cause; + } + if (cause instanceof Error) { + throw (Error) cause; + } + // The cause is a weird kind of Throwable, so throw the outer exception. + throw e; + } + + private static Set findInterruptibleMethods(Class interfaceType) { + Set set = Sets.newHashSet(); + for (Method m : interfaceType.getMethods()) { + if (declaresInterruptedEx(m)) { + set.add(m); + } + } + return set; + } + + private static boolean declaresInterruptedEx(Method method) { + for (Class exType : method.getExceptionTypes()) { + // debate: == or isAssignableFrom? + if (exType == InterruptedException.class) { + return true; + } + } + return false; + } + + private void wrapAndThrowExecutionExceptionOrError(Throwable cause) throws ExecutionException { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } else if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } else { + throw new ExecutionException(cause); + } + } + + private void wrapAndThrowRuntimeExecutionExceptionOrError(Throwable cause) { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } else { + throw new UncheckedExecutionException(cause); + } + } + + private static void checkPositiveTimeout(long timeoutDuration) { + checkArgument(timeoutDuration > 0, "timeout must be positive: %s", timeoutDuration); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SmoothRateLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SmoothRateLimiter.java new file mode 100644 index 0000000000000..7d66f44f2f370 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/SmoothRateLimiter.java @@ -0,0 +1,401 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.math.LongMath; + +import java.util.concurrent.TimeUnit; + +import static java.lang.Math.min; +import static java.util.concurrent.TimeUnit.SECONDS; + +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class SmoothRateLimiter extends RateLimiter { + /* + * How is the RateLimiter designed, and why? + * + * The primary feature of a RateLimiter is its "stable rate", the maximum rate that it should + * allow in normal conditions. This is enforced by "throttling" incoming requests as needed. For + * example, we could compute the appropriate throttle time for an incoming request, and make the + * calling thread wait for that time. + * + * The simplest way to maintain a rate of QPS is to keep the timestamp of the last granted + * request, and ensure that (1/QPS) seconds have elapsed since then. For example, for a rate of + * QPS=5 (5 tokens per second), if we ensure that a request isn't granted earlier than 200ms after + * the last one, then we achieve the intended rate. If a request comes and the last request was + * granted only 100ms ago, then we wait for another 100ms. At this rate, serving 15 fresh permits + * (i.e. for an acquire(15) request) naturally takes 3 seconds. + * + * It is important to realize that such a RateLimiter has a very superficial memory of the past: + * it only remembers the last request. What if the RateLimiter was unused for a long period of + * time, then a request arrived and was immediately granted? This RateLimiter would immediately + * forget about that past underutilization. This may result in either underutilization or + * overflow, depending on the real world consequences of not using the expected rate. + * + * Past underutilization could mean that excess resources are available. Then, the RateLimiter + * should speed up for a while, to take advantage of these resources. This is important when the + * rate is applied to networking (limiting bandwidth), where past underutilization typically + * translates to "almost empty buffers", which can be filled immediately. + * + * On the other hand, past underutilization could mean that "the server responsible for handling + * the request has become less ready for future requests", i.e. its caches become stale, and + * requests become more likely to trigger expensive operations (a more extreme case of this + * example is when a server has just booted, and it is mostly busy with getting itself up to + * speed). + * + * To deal with such scenarios, we add an extra dimension, that of "past underutilization", + * modeled by "storedPermits" variable. This variable is zero when there is no underutilization, + * and it can grow up to maxStoredPermits, for sufficiently large underutilization. So, the + * requested permits, by an invocation acquire(permits), are served from: + * + * - stored permits (if available) + * + * - fresh permits (for any remaining permits) + * + * How this works is best explained with an example: + * + * For a RateLimiter that produces 1 token per second, every second that goes by with the + * RateLimiter being unused, we increase storedPermits by 1. Say we leave the RateLimiter unused + * for 10 seconds (i.e., we expected a request at time X, but we are at time X + 10 seconds before + * a request actually arrives; this is also related to the point made in the last paragraph), thus + * storedPermits becomes 10.0 (assuming maxStoredPermits >= 10.0). At that point, a request of + * acquire(3) arrives. We serve this request out of storedPermits, and reduce that to 7.0 (how + * this is translated to throttling time is discussed later). Immediately after, assume that an + * acquire(10) request arriving. We serve the request partly from storedPermits, using all the + * remaining 7.0 permits, and the remaining 3.0, we serve them by fresh permits produced by the + * rate limiter. + * + * We already know how much time it takes to serve 3 fresh permits: if the rate is + * "1 token per second", then this will take 3 seconds. But what does it mean to serve 7 stored + * permits? As explained above, there is no unique answer. If we are primarily interested to deal + * with underutilization, then we want stored permits to be given out /faster/ than fresh ones, + * because underutilization = free resources for the taking. If we are primarily interested to + * deal with overflow, then stored permits could be given out /slower/ than fresh ones. Thus, we + * require a (different in each case) function that translates storedPermits to throttling time. + * + * This role is played by storedPermitsToWaitTime(double storedPermits, double permitsToTake). The + * underlying model is a continuous function mapping storedPermits (from 0.0 to maxStoredPermits) + * onto the 1/rate (i.e. intervals) that is effective at the given storedPermits. "storedPermits" + * essentially measure unused time; we spend unused time buying/storing permits. Rate is + * "permits / time", thus "1 / rate = time / permits". Thus, "1/rate" (time / permits) times + * "permits" gives time, i.e., integrals on this function (which is what storedPermitsToWaitTime() + * computes) correspond to minimum intervals between subsequent requests, for the specified number + * of requested permits. + * + * Here is an example of storedPermitsToWaitTime: If storedPermits == 10.0, and we want 3 permits, + * we take them from storedPermits, reducing them to 7.0, and compute the throttling for these as + * a call to storedPermitsToWaitTime(storedPermits = 10.0, permitsToTake = 3.0), which will + * evaluate the integral of the function from 7.0 to 10.0. + * + * Using integrals guarantees that the effect of a single acquire(3) is equivalent to { + * acquire(1); acquire(1); acquire(1); }, or { acquire(2); acquire(1); }, etc, since the integral + * of the function in [7.0, 10.0] is equivalent to the sum of the integrals of [7.0, 8.0], [8.0, + * 9.0], [9.0, 10.0] (and so on), no matter what the function is. This guarantees that we handle + * correctly requests of varying weight (permits), /no matter/ what the actual function is - so we + * can tweak the latter freely. (The only requirement, obviously, is that we can compute its + * integrals). + * + * Note well that if, for this function, we chose a horizontal line, at height of exactly (1/QPS), + * then the effect of the function is non-existent: we serve storedPermits at exactly the same + * cost as fresh ones (1/QPS is the cost for each). We use this trick later. + * + * If we pick a function that goes /below/ that horizontal line, it means that we reduce the area + * of the function, thus time. Thus, the RateLimiter becomes /faster/ after a period of + * underutilization. If, on the other hand, we pick a function that goes /above/ that horizontal + * line, then it means that the area (time) is increased, thus storedPermits are more costly than + * fresh permits, thus the RateLimiter becomes /slower/ after a period of underutilization. + * + * Last, but not least: consider a RateLimiter with rate of 1 permit per second, currently + * completely unused, and an expensive acquire(100) request comes. It would be nonsensical to just + * wait for 100 seconds, and /then/ start the actual task. Why wait without doing anything? A much + * better approach is to /allow/ the request right away (as if it was an acquire(1) request + * instead), and postpone /subsequent/ requests as needed. In this version, we allow starting the + * task immediately, and postpone by 100 seconds future requests, thus we allow for work to get + * done in the meantime instead of waiting idly. + * + * This has important consequences: it means that the RateLimiter doesn't remember the time of the + * _last_ request, but it remembers the (expected) time of the _next_ request. This also enables + * us to tell immediately (see tryAcquire(timeout)) whether a particular timeout is enough to get + * us to the point of the next scheduling time, since we always maintain that. And what we mean by + * "an unused RateLimiter" is also defined by that notion: when we observe that the + * "expected arrival time of the next request" is actually in the past, then the difference (now - + * past) is the amount of time that the RateLimiter was formally unused, and it is that amount of + * time which we translate to storedPermits. (We increase storedPermits with the amount of permits + * that would have been produced in that idle time). So, if rate == 1 permit per second, and + * arrivals come exactly one second after the previous, then storedPermits is _never_ increased -- + * we would only increase it for arrivals _later_ than the expected one second. + */ + + /** + * This implements the following function where coldInterval = coldFactor * stableInterval. + * + *
+   *          ^ throttling
+   *          |
+   *    cold  +                  /
+   * interval |                 /.
+   *          |                / .
+   *          |               /  .   ← "warmup period" is the area of the trapezoid between
+   *          |              /   .     thresholdPermits and maxPermits
+   *          |             /    .
+   *          |            /     .
+   *          |           /      .
+   *   stable +----------/  WARM .
+   * interval |          .   UP  .
+   *          |          . PERIOD.
+   *          |          .       .
+   *        0 +----------+-------+--------------→ storedPermits
+   *          0 thresholdPermits maxPermits
+   * 
+ * + * Before going into the details of this particular function, let's keep in mind the basics: + * + *
    + *
  1. The state of the RateLimiter (storedPermits) is a vertical line in this figure. + *
  2. When the RateLimiter is not used, this goes right (up to maxPermits) + *
  3. When the RateLimiter is used, this goes left (down to zero), since if we have + * storedPermits, we serve from those first + *
  4. When _unused_, we go right at a constant rate! The rate at which we move to the right is + * chosen as maxPermits / warmupPeriod. This ensures that the time it takes to go from 0 to + * maxPermits is equal to warmupPeriod. + *
  5. When _used_, the time it takes, as explained in the introductory class note, is equal to + * the integral of our function, between X permits and X-K permits, assuming we want to + * spend K saved permits. + *
+ * + *

In summary, the time it takes to move to the left (spend K permits), is equal to the area of + * the function of width == K. + * + *

Assuming we have saturated demand, the time to go from maxPermits to thresholdPermits is + * equal to warmupPeriod. And the time to go from thresholdPermits to 0 is warmupPeriod/2. (The + * reason that this is warmupPeriod/2 is to maintain the behavior of the original implementation + * where coldFactor was hard coded as 3.) + * + *

It remains to calculate thresholdsPermits and maxPermits. + * + *

    + *
  • The time to go from thresholdPermits to 0 is equal to the integral of the function + * between 0 and thresholdPermits. This is thresholdPermits * stableIntervals. By (5) it is + * also equal to warmupPeriod/2. Therefore + *
    + * thresholdPermits = 0.5 * warmupPeriod / stableInterval + *
    + *
  • The time to go from maxPermits to thresholdPermits is equal to the integral of the + * function between thresholdPermits and maxPermits. This is the area of the pictured + * trapezoid, and it is equal to 0.5 * (stableInterval + coldInterval) * (maxPermits - + * thresholdPermits). It is also equal to warmupPeriod, so + *
    + * maxPermits = thresholdPermits + 2 * warmupPeriod / (stableInterval + coldInterval) + *
    + *
+ */ + static final class SmoothWarmingUp extends SmoothRateLimiter { + private final long warmupPeriodMicros; + + /** + * The slope of the line from the stable interval (when permits == 0), to the cold interval + * (when permits == maxPermits) + */ + private double slope; + + private double thresholdPermits; + private double coldFactor; + + SmoothWarmingUp( + SleepingStopwatch stopwatch, long warmupPeriod, TimeUnit timeUnit, double coldFactor) { + super(stopwatch); + this.warmupPeriodMicros = timeUnit.toMicros(warmupPeriod); + this.coldFactor = coldFactor; + } + + @Override + void doSetRate(double permitsPerSecond, double stableIntervalMicros) { + double oldMaxPermits = maxPermits; + double coldIntervalMicros = stableIntervalMicros * coldFactor; + thresholdPermits = 0.5 * warmupPeriodMicros / stableIntervalMicros; + maxPermits = + thresholdPermits + 2.0 * warmupPeriodMicros / (stableIntervalMicros + coldIntervalMicros); + slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits); + if (oldMaxPermits == Double.POSITIVE_INFINITY) { + // if we don't special-case this, we would get storedPermits == NaN, below + storedPermits = 0.0; + } else { + storedPermits = + (oldMaxPermits == 0.0) + ? maxPermits // initial state is cold + : storedPermits * maxPermits / oldMaxPermits; + } + } + + @Override + long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { + double availablePermitsAboveThreshold = storedPermits - thresholdPermits; + long micros = 0; + // measuring the integral on the right part of the function (the climbing line) + if (availablePermitsAboveThreshold > 0.0) { + double permitsAboveThresholdToTake = min(availablePermitsAboveThreshold, permitsToTake); + // TODO(cpovirk): Figure out a good name for this variable. + double length = + permitsToTime(availablePermitsAboveThreshold) + + permitsToTime(availablePermitsAboveThreshold - permitsAboveThresholdToTake); + micros = (long) (permitsAboveThresholdToTake * length / 2.0); + permitsToTake -= permitsAboveThresholdToTake; + } + // measuring the integral on the left part of the function (the horizontal line) + micros += (long) (stableIntervalMicros * permitsToTake); + return micros; + } + + private double permitsToTime(double permits) { + return stableIntervalMicros + permits * slope; + } + + @Override + double coolDownIntervalMicros() { + return warmupPeriodMicros / maxPermits; + } + } + + /** + * This implements a "bursty" RateLimiter, where storedPermits are translated to zero throttling. + * The maximum number of permits that can be saved (when the RateLimiter is unused) is defined in + * terms of time, in this sense: if a RateLimiter is 2qps, and this time is specified as 10 + * seconds, we can save up to 2 * 10 = 20 permits. + */ + static final class SmoothBursty extends SmoothRateLimiter { + /** The work (permits) of how many seconds can be saved up if this RateLimiter is unused? */ + final double maxBurstSeconds; + + SmoothBursty(SleepingStopwatch stopwatch, double maxBurstSeconds) { + super(stopwatch); + this.maxBurstSeconds = maxBurstSeconds; + } + + @Override + void doSetRate(double permitsPerSecond, double stableIntervalMicros) { + double oldMaxPermits = this.maxPermits; + maxPermits = maxBurstSeconds * permitsPerSecond; + if (oldMaxPermits == Double.POSITIVE_INFINITY) { + // if we don't special-case this, we would get storedPermits == NaN, below + storedPermits = maxPermits; + } else { + storedPermits = + (oldMaxPermits == 0.0) + ? 0.0 // initial state + : storedPermits * maxPermits / oldMaxPermits; + } + } + + @Override + long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { + return 0L; + } + + @Override + double coolDownIntervalMicros() { + return stableIntervalMicros; + } + } + + /** The currently stored permits. */ + double storedPermits; + + /** The maximum number of stored permits. */ + double maxPermits; + + /** + * The interval between two unit requests, at our stable rate. E.g., a stable rate of 5 permits + * per second has a stable interval of 200ms. + */ + double stableIntervalMicros; + + /** + * The time when the next request (no matter its size) will be granted. After granting a request, + * this is pushed further in the future. Large requests push this further than small requests. + */ + private long nextFreeTicketMicros = 0L; // could be either in the past or future + + private SmoothRateLimiter(SleepingStopwatch stopwatch) { + super(stopwatch); + } + + @Override + final void doSetRate(double permitsPerSecond, long nowMicros) { + resync(nowMicros); + double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond; + this.stableIntervalMicros = stableIntervalMicros; + doSetRate(permitsPerSecond, stableIntervalMicros); + } + + abstract void doSetRate(double permitsPerSecond, double stableIntervalMicros); + + @Override + final double doGetRate() { + return SECONDS.toMicros(1L) / stableIntervalMicros; + } + + @Override + final long queryEarliestAvailable(long nowMicros) { + return nextFreeTicketMicros; + } + + @Override + final long reserveEarliestAvailable(int requiredPermits, long nowMicros) { + resync(nowMicros); + long returnValue = nextFreeTicketMicros; + double storedPermitsToSpend = min(requiredPermits, this.storedPermits); + double freshPermits = requiredPermits - storedPermitsToSpend; + long waitMicros = + storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) + + (long) (freshPermits * stableIntervalMicros); + + this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros); + this.storedPermits -= storedPermitsToSpend; + return returnValue; + } + + /** + * Translates a specified portion of our currently stored permits which we want to spend/acquire, + * into a throttling time. Conceptually, this evaluates the integral of the underlying function we + * use, for the range of [(storedPermits - permitsToTake), storedPermits]. + * + *

This always holds: {@code 0 <= permitsToTake <= storedPermits} + */ + abstract long storedPermitsToWaitTime(double storedPermits, double permitsToTake); + + /** + * Returns the number of microseconds during cool down that we have to wait to get a new permit. + */ + abstract double coolDownIntervalMicros(); + + /** Updates {@code storedPermits} and {@code nextFreeTicketMicros} based on the current time. */ + void resync(long nowMicros) { + // if nextFreeTicket is in the past, resync to now + if (nowMicros > nextFreeTicketMicros) { + double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros(); + storedPermits = min(maxPermits, storedPermits + newPermits); + nextFreeTicketMicros = nowMicros; + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Striped.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Striped.java new file mode 100644 index 0000000000000..7c1b24dec2c92 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Striped.java @@ -0,0 +1,551 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.MoreObjects; +import org.apache.iotdb.google.common.base.Preconditions; +import org.apache.iotdb.google.common.base.Supplier; +import org.apache.iotdb.google.common.collect.ImmutableList; +import org.apache.iotdb.google.common.collect.MapMaker; +import org.apache.iotdb.google.common.math.IntMath; +import org.apache.iotdb.google.common.primitives.Ints; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static org.apache.iotdb.google.common.collect.Lists.newArrayList; + +/** + * A striped {@code Lock/Semaphore/ReadWriteLock}. This offers the underlying lock striping similar + * to that of {@code ConcurrentHashMap} in a reusable form, and extends it for semaphores and + * read-write locks. Conceptually, lock striping is the technique of dividing a lock into many + * stripes, increasing the granularity of a single lock and allowing independent operations + * to lock different stripes and proceed concurrently, instead of creating contention for a single + * lock. + * + *

The guarantee provided by this class is that equal keys lead to the same lock (or semaphore), + * i.e. {@code if (key1.equals(key2))} then {@code striped.get(key1) == striped.get(key2)} (assuming + * {@link Object#hashCode()} is correctly implemented for the keys). Note that if {@code key1} is + * not equal to {@code key2}, it is not guaranteed that {@code + * striped.get(key1) != striped.get(key2)}; the elements might nevertheless be mapped to the same + * lock. The lower the number of stripes, the higher the probability of this happening. + * + *

There are three flavors of this class: {@code Striped}, {@code Striped}, and + * {@code Striped}. For each type, two implementations are offered: {@linkplain + * #lock(int) strong} and {@linkplain #lazyWeakLock(int) weak} {@code Striped}, {@linkplain + * #semaphore(int, int) strong} and {@linkplain #lazyWeakSemaphore(int, int) weak} {@code + * Striped}, and {@linkplain #readWriteLock(int) strong} and {@linkplain + * #lazyWeakReadWriteLock(int) weak} {@code Striped}. Strong means that all + * stripes (locks/semaphores) are initialized eagerly, and are not reclaimed unless {@code Striped} + * itself is reclaimable. Weak means that locks/semaphores are created lazily, and they are + * allowed to be reclaimed if nobody is holding on to them. This is useful, for example, if one + * wants to create a {@code Striped} of many locks, but worries that in most cases only a + * small portion of these would be in use. + * + *

Prior to this class, one might be tempted to use {@code Map}, where {@code K} + * represents the task. This maximizes concurrency by having each unique key mapped to a unique + * lock, but also maximizes memory footprint. On the other extreme, one could use a single lock for + * all tasks, which minimizes memory footprint but also minimizes concurrency. Instead of choosing + * either of these extremes, {@code Striped} allows the user to trade between required concurrency + * and memory footprint. For example, if a set of tasks are CPU-bound, one could easily create a + * very compact {@code Striped} of {@code availableProcessors() * 4} stripes, instead of + * possibly thousands of locks which could be created in a {@code Map} structure. + * + * @author Dimitris Andreou + * @since 13.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public abstract class Striped { + /** + * If there are at least this many stripes, we assume the memory usage of a ConcurrentMap will be + * smaller than a large array. (This assumes that in the lazy case, most stripes are unused. As + * always, if many stripes are in use, a non-lazy striped makes more sense.) + */ + private static final int LARGE_LAZY_CUTOFF = 1024; + + private Striped() {} + + /** + * Returns the stripe that corresponds to the passed key. It is always guaranteed that if {@code + * key1.equals(key2)}, then {@code get(key1) == get(key2)}. + * + * @param key an arbitrary, non-null key + * @return the stripe that the passed key corresponds to + */ + public abstract L get(Object key); + + /** + * Returns the stripe at the specified index. Valid indexes are 0, inclusively, to {@code size()}, + * exclusively. + * + * @param index the index of the stripe to return; must be in {@code [0...size())} + * @return the stripe at the specified index + */ + public abstract L getAt(int index); + + /** + * Returns the index to which the given key is mapped, so that getAt(indexFor(key)) == get(key). + */ + abstract int indexFor(Object key); + + /** Returns the total number of stripes in this instance. */ + public abstract int size(); + + /** + * Returns the stripes that correspond to the passed objects, in ascending (as per {@link + * #getAt(int)}) order. Thus, threads that use the stripes in the order returned by this method + * are guaranteed to not deadlock each other. + * + *

It should be noted that using a {@code Striped} with relatively few stripes, and {@code + * bulkGet(keys)} with a relative large number of keys can cause an excessive number of shared + * stripes (much like the birthday paradox, where much fewer than anticipated birthdays are needed + * for a pair of them to match). Please consider carefully the implications of the number of + * stripes, the intended concurrency level, and the typical number of keys used in a {@code + * bulkGet(keys)} operation. See Balls in + * Bins model for mathematical formulas that can be used to estimate the probability of + * collisions. + * + * @param keys arbitrary non-null keys + * @return the stripes corresponding to the objects (one per each object, derived by delegating to + * {@link #get(Object)}; may contain duplicates), in an increasing index order. + */ + public Iterable bulkGet(Iterable keys) { + // Initially using the list to store the keys, then reusing it to store the respective L's + List result = newArrayList(keys); + if (result.isEmpty()) { + return ImmutableList.of(); + } + int[] stripes = new int[result.size()]; + for (int i = 0; i < result.size(); i++) { + stripes[i] = indexFor(result.get(i)); + } + Arrays.sort(stripes); + // optimize for runs of identical stripes + int previousStripe = stripes[0]; + result.set(0, getAt(previousStripe)); + for (int i = 1; i < result.size(); i++) { + int currentStripe = stripes[i]; + if (currentStripe == previousStripe) { + result.set(i, result.get(i - 1)); + } else { + result.set(i, getAt(currentStripe)); + previousStripe = currentStripe; + } + } + /* + * Note that the returned Iterable holds references to the returned stripes, to avoid + * error-prone code like: + * + * Striped stripedLock = Striped.lazyWeakXXX(...)' + * Iterable locks = stripedLock.bulkGet(keys); + * for (Lock lock : locks) { + * lock.lock(); + * } + * operation(); + * for (Lock lock : locks) { + * lock.unlock(); + * } + * + * If we only held the int[] stripes, translating it on the fly to L's, the original locks might + * be garbage collected after locking them, ending up in a huge mess. + */ + @SuppressWarnings("unchecked") // we carefully replaced all keys with their respective L's + List asStripes = (List) result; + return Collections.unmodifiableList(asStripes); + } + + // Static factories + + /** + * Creates a {@code Striped} with eagerly initialized, strongly referenced locks. Every lock is + * obtained from the passed supplier. + * + * @param stripes the minimum number of stripes (locks) required + * @param supplier a {@code Supplier} object to obtain locks from + * @return a new {@code Striped} + */ + static Striped custom(int stripes, Supplier supplier) { + return new CompactStriped<>(stripes, supplier); + } + + /** + * Creates a {@code Striped} with eagerly initialized, strongly referenced locks. Every lock + * is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped} + */ + public static Striped lock(int stripes) { + return custom(stripes, PaddedLock::new); + } + + /** + * Creates a {@code Striped} with lazily initialized, weakly referenced locks. Every lock is + * reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped} + */ + public static Striped lazyWeakLock(int stripes) { + return lazy(stripes, () -> new ReentrantLock(false)); + } + + private static Striped lazy(int stripes, Supplier supplier) { + return stripes < LARGE_LAZY_CUTOFF + ? new SmallLazyStriped(stripes, supplier) + : new LargeLazyStriped(stripes, supplier); + } + + /** + * Creates a {@code Striped} with eagerly initialized, strongly referenced semaphores, + * with the specified number of permits. + * + * @param stripes the minimum number of stripes (semaphores) required + * @param permits the number of permits in each semaphore + * @return a new {@code Striped} + */ + public static Striped semaphore(int stripes, int permits) { + return custom(stripes, () -> new PaddedSemaphore(permits)); + } + + /** + * Creates a {@code Striped} with lazily initialized, weakly referenced semaphores, + * with the specified number of permits. + * + * @param stripes the minimum number of stripes (semaphores) required + * @param permits the number of permits in each semaphore + * @return a new {@code Striped} + */ + public static Striped lazyWeakSemaphore(int stripes, int permits) { + return lazy(stripes, () -> new Semaphore(permits, false)); + } + + /** + * Creates a {@code Striped} with eagerly initialized, strongly referenced + * read-write locks. Every lock is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped} + */ + public static Striped readWriteLock(int stripes) { + return custom(stripes, ReentrantReadWriteLock::new); + } + + /** + * Creates a {@code Striped} with lazily initialized, weakly referenced read-write + * locks. Every lock is reentrant. + * + * @param stripes the minimum number of stripes (locks) required + * @return a new {@code Striped} + */ + public static Striped lazyWeakReadWriteLock(int stripes) { + return lazy(stripes, WeakSafeReadWriteLock::new); + } + + /** + * ReadWriteLock implementation whose read and write locks retain a reference back to this lock. + * Otherwise, a reference to just the read lock or just the write lock would not suffice to ensure + * the {@code ReadWriteLock} is retained. + */ + private static final class WeakSafeReadWriteLock implements ReadWriteLock { + private final ReadWriteLock delegate; + + WeakSafeReadWriteLock() { + this.delegate = new ReentrantReadWriteLock(); + } + + @Override + public Lock readLock() { + return new WeakSafeLock(delegate.readLock(), this); + } + + @Override + public Lock writeLock() { + return new WeakSafeLock(delegate.writeLock(), this); + } + } + + /** Lock object that ensures a strong reference is retained to a specified object. */ + private static final class WeakSafeLock extends ForwardingLock { + private final Lock delegate; + + @SuppressWarnings("unused") + private final WeakSafeReadWriteLock strongReference; + + WeakSafeLock(Lock delegate, WeakSafeReadWriteLock strongReference) { + this.delegate = delegate; + this.strongReference = strongReference; + } + + @Override + Lock delegate() { + return delegate; + } + + @Override + public Condition newCondition() { + return new WeakSafeCondition(delegate.newCondition(), strongReference); + } + } + + /** Condition object that ensures a strong reference is retained to a specified object. */ + private static final class WeakSafeCondition extends ForwardingCondition { + private final Condition delegate; + + @SuppressWarnings("unused") + private final WeakSafeReadWriteLock strongReference; + + WeakSafeCondition(Condition delegate, WeakSafeReadWriteLock strongReference) { + this.delegate = delegate; + this.strongReference = strongReference; + } + + @Override + Condition delegate() { + return delegate; + } + } + + private abstract static class PowerOfTwoStriped extends Striped { + /** Capacity (power of two) minus one, for fast mod evaluation */ + final int mask; + + PowerOfTwoStriped(int stripes) { + Preconditions.checkArgument(stripes > 0, "Stripes must be positive"); + this.mask = stripes > Ints.MAX_POWER_OF_TWO ? ALL_SET : ceilToPowerOfTwo(stripes) - 1; + } + + @Override + final int indexFor(Object key) { + int hash = smear(key.hashCode()); + return hash & mask; + } + + @Override + public final L get(Object key) { + return getAt(indexFor(key)); + } + } + + /** + * Implementation of Striped where 2^k stripes are represented as an array of the same length, + * eagerly initialized. + */ + private static class CompactStriped extends PowerOfTwoStriped { + /** Size is a power of two. */ + private final Object[] array; + + private CompactStriped(int stripes, Supplier supplier) { + super(stripes); + Preconditions.checkArgument(stripes <= Ints.MAX_POWER_OF_TWO, "Stripes must be <= 2^30)"); + + this.array = new Object[mask + 1]; + for (int i = 0; i < array.length; i++) { + array[i] = supplier.get(); + } + } + + @SuppressWarnings("unchecked") // we only put L's in the array + @Override + public L getAt(int index) { + return (L) array[index]; + } + + @Override + public int size() { + return array.length; + } + } + + /** + * Implementation of Striped where up to 2^k stripes can be represented, using an + * AtomicReferenceArray of size 2^k. To map a user key into a stripe, we take a k-bit slice of the + * user key's (smeared) hashCode(). The stripes are lazily initialized and are weakly referenced. + */ + @VisibleForTesting + static class SmallLazyStriped extends PowerOfTwoStriped { + final AtomicReferenceArray> locks; + final Supplier supplier; + final int size; + final ReferenceQueue queue = new ReferenceQueue<>(); + + SmallLazyStriped(int stripes, Supplier supplier) { + super(stripes); + this.size = (mask == ALL_SET) ? Integer.MAX_VALUE : mask + 1; + this.locks = new AtomicReferenceArray<>(size); + this.supplier = supplier; + } + + @Override + public L getAt(int index) { + if (size != Integer.MAX_VALUE) { + Preconditions.checkElementIndex(index, size()); + } // else no check necessary, all index values are valid + ArrayReference existingRef = locks.get(index); + L existing = existingRef == null ? null : existingRef.get(); + if (existing != null) { + return existing; + } + L created = supplier.get(); + ArrayReference newRef = new ArrayReference<>(created, index, queue); + while (!locks.compareAndSet(index, existingRef, newRef)) { + // we raced, we need to re-read and try again + existingRef = locks.get(index); + existing = existingRef == null ? null : existingRef.get(); + if (existing != null) { + return existing; + } + } + drainQueue(); + return created; + } + + // N.B. Draining the queue is only necessary to ensure that we don't accumulate empty references + // in the array. We could skip this if we decide we don't care about holding on to Reference + // objects indefinitely. + private void drainQueue() { + Reference ref; + while ((ref = queue.poll()) != null) { + // We only ever register ArrayReferences with the queue so this is always safe. + ArrayReference arrayRef = (ArrayReference) ref; + // Try to clear out the array slot, n.b. if we fail that is fine, in either case the + // arrayRef will be out of the array after this step. + locks.compareAndSet(arrayRef.index, arrayRef, null); + } + } + + @Override + public int size() { + return size; + } + + private static final class ArrayReference extends WeakReference { + final int index; + + ArrayReference(L referent, int index, ReferenceQueue queue) { + super(referent, queue); + this.index = index; + } + } + } + + /** + * Implementation of Striped where up to 2^k stripes can be represented, using a ConcurrentMap + * where the key domain is [0..2^k). To map a user key into a stripe, we take a k-bit slice of the + * user key's (smeared) hashCode(). The stripes are lazily initialized and are weakly referenced. + */ + @VisibleForTesting + static class LargeLazyStriped extends PowerOfTwoStriped { + final ConcurrentMap locks; + final Supplier supplier; + final int size; + + LargeLazyStriped(int stripes, Supplier supplier) { + super(stripes); + this.size = (mask == ALL_SET) ? Integer.MAX_VALUE : mask + 1; + this.supplier = supplier; + this.locks = new MapMaker().weakValues().makeMap(); + } + + @Override + public L getAt(int index) { + if (size != Integer.MAX_VALUE) { + Preconditions.checkElementIndex(index, size()); + } // else no check necessary, all index values are valid + L existing = locks.get(index); + if (existing != null) { + return existing; + } + L created = supplier.get(); + existing = locks.putIfAbsent(index, created); + return MoreObjects.firstNonNull(existing, created); + } + + @Override + public int size() { + return size; + } + } + + /** A bit mask were all bits are set. */ + private static final int ALL_SET = ~0; + + private static int ceilToPowerOfTwo(int x) { + return 1 << IntMath.log2(x, RoundingMode.CEILING); + } + + /* + * This method was written by Doug Lea with assistance from members of JCP JSR-166 Expert Group + * and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + * + * As of 2010/06/11, this method is identical to the (package private) hash method in OpenJDK 7's + * java.util.HashMap class. + */ + // Copied from java/com/google/common/collect/Hashing.java + private static int smear(int hashCode) { + hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12); + return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4); + } + + private static class PaddedLock extends ReentrantLock { + /* + * Padding from 40 into 64 bytes, same size as cache line. Might be beneficial to add a fourth + * long here, to minimize chance of interference between consecutive locks, but I couldn't + * observe any benefit from that. + */ + long unused1; + long unused2; + long unused3; + + PaddedLock() { + super(false); + } + } + + private static class PaddedSemaphore extends Semaphore { + // See PaddedReentrantLock comment + long unused1; + long unused2; + long unused3; + + PaddedSemaphore(int permits) { + super(permits, false); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ThreadFactoryBuilder.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ThreadFactoryBuilder.java new file mode 100644 index 0000000000000..5248254a225e9 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/ThreadFactoryBuilder.java @@ -0,0 +1,196 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A ThreadFactory builder, providing any combination of these features: + * + *
    + *
  • whether threads should be marked as {@linkplain Thread#setDaemon daemon} threads + *
  • a {@linkplain ThreadFactoryBuilder#setNameFormat naming format} + *
  • a {@linkplain Thread#setPriority thread priority} + *
  • an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception handler} + *
  • a {@linkplain ThreadFactory#newThread backing thread factory} + *
+ * + *

If no backing thread factory is provided, a default backing thread factory is used as if by + * calling {@code setThreadFactory(}{@link Executors#defaultThreadFactory()}{@code )}. + * + * @author Kurt Alfred Kluever + * @since 4.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class ThreadFactoryBuilder { + private String nameFormat = null; + private Boolean daemon = null; + private Integer priority = null; + private UncaughtExceptionHandler uncaughtExceptionHandler = null; + private ThreadFactory backingThreadFactory = null; + + /** Creates a new {@link ThreadFactory} builder. */ + public ThreadFactoryBuilder() {} + + /** + * Sets the naming format to use when naming threads ({@link Thread#setName}) which are created + * with this ThreadFactory. + * + * @param nameFormat a {@link String#format(String, Object...)}-compatible format String, to which + * a unique integer (0, 1, etc.) will be supplied as the single parameter. This integer will + * be unique to the built instance of the ThreadFactory and will be assigned sequentially. For + * example, {@code "rpc-pool-%d"} will generate thread names like {@code "rpc-pool-0"}, {@code + * "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setNameFormat(String nameFormat) { + String unused = format(nameFormat, 0); // fail fast if the format is bad or null + this.nameFormat = nameFormat; + return this; + } + + /** + * Sets daemon or not for new threads created with this ThreadFactory. + * + * @param daemon whether or not new Threads created with this ThreadFactory will be daemon threads + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setDaemon(boolean daemon) { + this.daemon = daemon; + return this; + } + + /** + * Sets the priority for new threads created with this ThreadFactory. + * + *

Warning: relying on the thread scheduler is discouraged. + * + * @param priority the priority for new Threads created with this ThreadFactory + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setPriority(int priority) { + // Thread#setPriority() already checks for validity. These error messages + // are nicer though and will fail-fast. + checkArgument( + priority >= Thread.MIN_PRIORITY, + "Thread priority (%s) must be >= %s", + priority, + Thread.MIN_PRIORITY); + checkArgument( + priority <= Thread.MAX_PRIORITY, + "Thread priority (%s) must be <= %s", + priority, + Thread.MAX_PRIORITY); + this.priority = priority; + return this; + } + + /** + * Sets the {@link UncaughtExceptionHandler} for new threads created with this ThreadFactory. + * + * @param uncaughtExceptionHandler the uncaught exception handler for new Threads created with + * this ThreadFactory + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setUncaughtExceptionHandler( + UncaughtExceptionHandler uncaughtExceptionHandler) { + this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler); + return this; + } + + /** + * Sets the backing {@link ThreadFactory} for new threads created with this ThreadFactory. Threads + * will be created by invoking #newThread(Runnable) on this backing {@link ThreadFactory}. + * + * @param backingThreadFactory the backing {@link ThreadFactory} which will be delegated to during + * thread creation. + * @return this for the builder pattern + * @see MoreExecutors + */ + public ThreadFactoryBuilder setThreadFactory(ThreadFactory backingThreadFactory) { + this.backingThreadFactory = checkNotNull(backingThreadFactory); + return this; + } + + /** + * Returns a new thread factory using the options supplied during the building process. After + * building, it is still possible to change the options used to build the ThreadFactory and/or + * build again. State is not shared amongst built instances. + * + * @return the fully constructed {@link ThreadFactory} + */ + public ThreadFactory build() { + return doBuild(this); + } + + // Split out so that the anonymous ThreadFactory can't contain a reference back to the builder. + // At least, I assume that's why. TODO(cpovirk): Check, and maybe add a test for this. + private static ThreadFactory doBuild(ThreadFactoryBuilder builder) { + String nameFormat = builder.nameFormat; + Boolean daemon = builder.daemon; + Integer priority = builder.priority; + UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler; + ThreadFactory backingThreadFactory = + (builder.backingThreadFactory != null) + ? builder.backingThreadFactory + : Executors.defaultThreadFactory(); + AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; + return new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = backingThreadFactory.newThread(runnable); + // TODO(b/139735208): Figure out what to do when the factory returns null. + requireNonNull(thread); + if (nameFormat != null) { + // requireNonNull is safe because we create `count` if (and only if) we have a nameFormat. + thread.setName(format(nameFormat, requireNonNull(count).getAndIncrement())); + } + if (daemon != null) { + thread.setDaemon(daemon); + } + if (priority != null) { + thread.setPriority(priority); + } + if (uncaughtExceptionHandler != null) { + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); + } + return thread; + } + }; + } + + private static String format(String format, Object... args) { + return String.format(Locale.ROOT, format, args); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TimeLimiter.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TimeLimiter.java new file mode 100644 index 0000000000000..77b1a365aaa2f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TimeLimiter.java @@ -0,0 +1,306 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * Imposes a time limit on method calls. + * + * @author Kevin Bourrillion + * @author Jens Nyman + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public interface TimeLimiter { + + /** + * Returns an instance of {@code interfaceType} that delegates all method calls to the {@code + * target} object, enforcing the specified time limit on each call. This time-limited delegation + * is also performed for calls to {@link Object#equals}, {@link Object#hashCode}, and {@link + * Object#toString}. + * + *

If the target method call finishes before the limit is reached, the return value or + * exception is propagated to the caller exactly as-is. If, on the other hand, the time limit is + * reached, the proxy will attempt to abort the call to the target, and will throw an {@link + * UncheckedTimeoutException} to the caller. + * + *

It is important to note that the primary purpose of the proxy object is to return control to + * the caller when the timeout elapses; aborting the target method call is of secondary concern. + * The particular nature and strength of the guarantees made by the proxy is + * implementation-dependent. However, it is important that each of the methods on the target + * object behaves appropriately when its thread is interrupted. + * + *

For example, to return the value of {@code target.someMethod()}, but substitute {@code + * DEFAULT_VALUE} if this method call takes over 50 ms, you can use this code: + * + *

+   *   TimeLimiter limiter = . . .;
+   *   TargetType proxy = limiter.newProxy(
+   *       target, TargetType.class, 50, TimeUnit.MILLISECONDS);
+   *   try {
+   *     return proxy.someMethod();
+   *   } catch (UncheckedTimeoutException e) {
+   *     return DEFAULT_VALUE;
+   *   }
+   * 
+ * + * @param target the object to proxy + * @param interfaceType the interface you wish the returned proxy to implement + * @param timeoutDuration with timeoutUnit, the maximum length of time that callers are willing to + * wait on each method call to the proxy + * @param timeoutUnit with timeoutDuration, the maximum length of time that callers are willing to + * wait on each method call to the proxy + * @return a time-limiting proxy + * @throws IllegalArgumentException if {@code interfaceType} is a regular class, enum, or + * annotation type, rather than an interface + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + T newProxy(T target, Class interfaceType, long timeoutDuration, TimeUnit timeoutUnit); + + /** + * Returns an instance of {@code interfaceType} that delegates all method calls to the {@code + * target} object, enforcing the specified time limit on each call. This time-limited delegation + * is also performed for calls to {@link Object#equals}, {@link Object#hashCode}, and {@link + * Object#toString}. + * + *

If the target method call finishes before the limit is reached, the return value or + * exception is propagated to the caller exactly as-is. If, on the other hand, the time limit is + * reached, the proxy will attempt to abort the call to the target, and will throw an {@link + * UncheckedTimeoutException} to the caller. + * + *

It is important to note that the primary purpose of the proxy object is to return control to + * the caller when the timeout elapses; aborting the target method call is of secondary concern. + * The particular nature and strength of the guarantees made by the proxy is + * implementation-dependent. However, it is important that each of the methods on the target + * object behaves appropriately when its thread is interrupted. + * + *

For example, to return the value of {@code target.someMethod()}, but substitute {@code + * DEFAULT_VALUE} if this method call takes over 50 ms, you can use this code: + * + *

+   *   TimeLimiter limiter = . . .;
+   *   TargetType proxy = limiter.newProxy(target, TargetType.class, Duration.ofMillis(50));
+   *   try {
+   *     return proxy.someMethod();
+   *   } catch (UncheckedTimeoutException e) {
+   *     return DEFAULT_VALUE;
+   *   }
+   * 
+ * + * @param target the object to proxy + * @param interfaceType the interface you wish the returned proxy to implement + * @param timeout the maximum length of time that callers are willing to wait on each method call + * to the proxy + * @return a time-limiting proxy + * @throws IllegalArgumentException if {@code interfaceType} is a regular class, enum, or + * annotation type, rather than an interface + * @since 28.0 + */ + default T newProxy(T target, Class interfaceType, Duration timeout) { + return newProxy(target, interfaceType, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes a specified Callable, timing out after the specified time limit. If the target method + * call finishes before the limit is reached, the return value or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to + * the target, and throw a {@link TimeoutException} to the caller. + * + * @param callable the Callable to execute + * @param timeoutDuration with timeoutUnit, the maximum length of time to wait + * @param timeoutUnit with timeoutDuration, the maximum length of time to wait + * @return the result returned by the Callable + * @throws TimeoutException if the time limit is reached + * @throws InterruptedException if the current thread was interrupted during execution + * @throws ExecutionException if {@code callable} throws a checked exception + * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code callable} throws an {@code Error} + * @since 22.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @ParametricNullness + T callWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException, InterruptedException, ExecutionException; + + /** + * Invokes a specified Callable, timing out after the specified time limit. If the target method + * call finishes before the limit is reached, the return value or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to + * the target, and throw a {@link TimeoutException} to the caller. + * + * @param callable the Callable to execute + * @param timeout the maximum length of time to wait + * @return the result returned by the Callable + * @throws TimeoutException if the time limit is reached + * @throws InterruptedException if the current thread was interrupted during execution + * @throws ExecutionException if {@code callable} throws a checked exception + * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code callable} throws an {@code Error} + * @since 28.0 + */ + @ParametricNullness + default T callWithTimeout(Callable callable, Duration timeout) + throws TimeoutException, InterruptedException, ExecutionException { + return callWithTimeout(callable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes a specified Callable, timing out after the specified time limit. If the target method + * call finishes before the limit is reached, the return value or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to + * the target, and throw a {@link TimeoutException} to the caller. + * + *

The difference with {@link #callWithTimeout(Callable, long, TimeUnit)} is that this method + * will ignore interrupts on the current thread. + * + * @param callable the Callable to execute + * @param timeoutDuration with timeoutUnit, the maximum length of time to wait + * @param timeoutUnit with timeoutDuration, the maximum length of time to wait + * @return the result returned by the Callable + * @throws TimeoutException if the time limit is reached + * @throws ExecutionException if {@code callable} throws a checked exception + * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code callable} throws an {@code Error} + * @since 22.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @ParametricNullness + T callUninterruptiblyWithTimeout( + Callable callable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException, ExecutionException; + + /** + * Invokes a specified Callable, timing out after the specified time limit. If the target method + * call finishes before the limit is reached, the return value or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the call to + * the target, and throw a {@link TimeoutException} to the caller. + * + *

The difference with {@link #callWithTimeout(Callable, Duration)} is that this method will + * ignore interrupts on the current thread. + * + * @param callable the Callable to execute + * @param timeout the maximum length of time to wait + * @return the result returned by the Callable + * @throws TimeoutException if the time limit is reached + * @throws ExecutionException if {@code callable} throws a checked exception + * @throws UncheckedExecutionException if {@code callable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code callable} throws an {@code Error} + * @since 28.0 + */ + @ParametricNullness + default T callUninterruptiblyWithTimeout( + Callable callable, Duration timeout) throws TimeoutException, ExecutionException { + return callUninterruptiblyWithTimeout( + callable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes a specified Runnable, timing out after the specified time limit. If the target method + * run finishes before the limit is reached, this method returns or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and + * throw a {@link TimeoutException} to the caller. + * + * @param runnable the Runnable to execute + * @param timeoutDuration with timeoutUnit, the maximum length of time to wait + * @param timeoutUnit with timeoutDuration, the maximum length of time to wait + * @throws TimeoutException if the time limit is reached + * @throws InterruptedException if the current thread was interrupted during execution + * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code runnable} throws an {@code Error} + * @since 22.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + void runWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException, InterruptedException; + + /** + * Invokes a specified Runnable, timing out after the specified time limit. If the target method + * run finishes before the limit is reached, this method returns or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and + * throw a {@link TimeoutException} to the caller. + * + * @param runnable the Runnable to execute + * @param timeout the maximum length of time to wait + * @throws TimeoutException if the time limit is reached + * @throws InterruptedException if the current thread was interrupted during execution + * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code runnable} throws an {@code Error} + * @since 28.0 + */ + default void runWithTimeout(Runnable runnable, Duration timeout) + throws TimeoutException, InterruptedException { + runWithTimeout(runnable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes a specified Runnable, timing out after the specified time limit. If the target method + * run finishes before the limit is reached, this method returns or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and + * throw a {@link TimeoutException} to the caller. + * + *

The difference with {@link #runWithTimeout(Runnable, long, TimeUnit)} is that this method + * will ignore interrupts on the current thread. + * + * @param runnable the Runnable to execute + * @param timeoutDuration with timeoutUnit, the maximum length of time to wait + * @param timeoutUnit with timeoutDuration, the maximum length of time to wait + * @throws TimeoutException if the time limit is reached + * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code runnable} throws an {@code Error} + * @since 22.0 + */ + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + void runUninterruptiblyWithTimeout(Runnable runnable, long timeoutDuration, TimeUnit timeoutUnit) + throws TimeoutException; + + /** + * Invokes a specified Runnable, timing out after the specified time limit. If the target method + * run finishes before the limit is reached, this method returns or a wrapped exception is + * propagated. If, on the other hand, the time limit is reached, we attempt to abort the run, and + * throw a {@link TimeoutException} to the caller. + * + *

The difference with {@link #runWithTimeout(Runnable, Duration)} is that this method will + * ignore interrupts on the current thread. + * + * @param runnable the Runnable to execute + * @param timeout the maximum length of time to wait + * @throws TimeoutException if the time limit is reached + * @throws UncheckedExecutionException if {@code runnable} throws a {@code RuntimeException} + * @throws ExecutionError if {@code runnable} throws an {@code Error} + * @since 28.0 + */ + default void runUninterruptiblyWithTimeout(Runnable runnable, Duration timeout) + throws TimeoutException { + runUninterruptiblyWithTimeout(runnable, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TimeoutFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TimeoutFuture.java new file mode 100644 index 0000000000000..f632ef8deb9d4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TimeoutFuture.java @@ -0,0 +1,195 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.directExecutor; + +/** + * Implementation of {@code Futures#withTimeout}. + * + *

Future that delegates to another but will finish early (via a {@link TimeoutException} wrapped + * in an {@link ExecutionException}) if the specified duration expires. The delegate future is + * interrupted and cancelled if it times out. + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class TimeoutFuture extends FluentFuture.TrustedFuture { + static ListenableFuture create( + ListenableFuture delegate, + long time, + TimeUnit unit, + ScheduledExecutorService scheduledExecutor) { + TimeoutFuture result = new TimeoutFuture<>(delegate); + Fire fire = new Fire<>(result); + result.timer = scheduledExecutor.schedule(fire, time, unit); + delegate.addListener(fire, directExecutor()); + return result; + } + + /* + * Memory visibility of these fields. There are two cases to consider. + * + * 1. visibility of the writes to these fields to Fire.run: + * + * The initial write to delegateRef is made definitely visible via the semantics of + * addListener/SES.schedule. The later racy write in cancel() is not guaranteed to be observed, + * however that is fine since the correctness is based on the atomic state in our base class. The + * initial write to timer is never definitely visible to Fire.run since it is assigned after + * SES.schedule is called. Therefore Fire.run has to check for null. However, it should be visible + * if Fire.run is called by delegate.addListener since addListener is called after the assignment + * to timer, and importantly this is the main situation in which we need to be able to see the + * write. + * + * 2. visibility of the writes to an afterDone() call triggered by cancel(): + * + * Since these fields are non-final that means that TimeoutFuture is not being 'safely published', + * thus a motivated caller may be able to expose the reference to another thread that would then + * call cancel() and be unable to cancel the delegate. + * There are a number of ways to solve this, none of which are very pretty, and it is currently + * believed to be a purely theoretical problem (since the other actions should supply sufficient + * write-barriers). + */ + + private ListenableFuture delegateRef; + private ScheduledFuture timer; + + private TimeoutFuture(ListenableFuture delegate) { + this.delegateRef = Preconditions.checkNotNull(delegate); + } + + /** A runnable that is called when the delegate or the timer completes. */ + private static final class Fire implements Runnable { + TimeoutFuture timeoutFutureRef; + + Fire(TimeoutFuture timeoutFuture) { + this.timeoutFutureRef = timeoutFuture; + } + + @Override + public void run() { + // If either of these reads return null then we must be after a successful cancel or another + // call to this method. + TimeoutFuture timeoutFuture = timeoutFutureRef; + if (timeoutFuture == null) { + return; + } + ListenableFuture delegate = timeoutFuture.delegateRef; + if (delegate == null) { + return; + } + + /* + * If we're about to complete the TimeoutFuture, we want to release our reference to it. + * Otherwise, we'll pin it (and its result) in memory until the timeout task is GCed. (The + * need to clear our reference to the TimeoutFuture is the reason we use a *static* nested + * class with a manual reference back to the "containing" class.) + * + * This has the nice-ish side effect of limiting reentrancy: run() calls + * timeoutFuture.setException() calls run(). That reentrancy would already be harmless, since + * timeoutFuture can be set (and delegate cancelled) only once. (And "set only once" is + * important for other reasons: run() can still be invoked concurrently in different threads, + * even with the above null checks.) + */ + timeoutFutureRef = null; + if (delegate.isDone()) { + timeoutFuture.setFuture(delegate); + } else { + try { + ScheduledFuture timer = timeoutFuture.timer; + timeoutFuture.timer = null; // Don't include already elapsed delay in delegate.toString() + String message = "Timed out"; + // This try-finally block ensures that we complete the timeout future, even if attempting + // to produce the message throws (probably StackOverflowError from delegate.toString()) + try { + if (timer != null) { + long overDelayMs = Math.abs(timer.getDelay(TimeUnit.MILLISECONDS)); + if (overDelayMs > 10) { // Not all timing drift is worth reporting + message += " (timeout delayed by " + overDelayMs + " ms after scheduled time)"; + } + } + message += ": " + delegate; + } finally { + timeoutFuture.setException(new TimeoutFutureException(message)); + } + } finally { + delegate.cancel(true); + } + } + } + } + + private static final class TimeoutFutureException extends TimeoutException { + private TimeoutFutureException(String message) { + super(message); + } + + @Override + public synchronized Throwable fillInStackTrace() { + setStackTrace(new StackTraceElement[0]); + return this; // no stack trace, wouldn't be useful anyway + } + } + + @Override + protected String pendingToString() { + ListenableFuture localInputFuture = delegateRef; + ScheduledFuture localTimer = timer; + if (localInputFuture != null) { + String message = "inputFuture=[" + localInputFuture + "]"; + if (localTimer != null) { + long delay = localTimer.getDelay(TimeUnit.MILLISECONDS); + // Negative delays look confusing in an error message + if (delay > 0) { + message += ", remaining delay=[" + delay + " ms]"; + } + } + return message; + } + return null; + } + + @Override + protected void afterDone() { + maybePropagateCancellationTo(delegateRef); + + Future localTimer = timer; + // Try to cancel the timer as an optimization. + // timer may be null if this call to run was by the timer task since there is no happens-before + // edge between the assignment to timer and an execution of the timer task. + if (localTimer != null) { + localTimer.cancel(false); + } + + delegateRef = null; + timer = null; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TrustedListenableFutureTask.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TrustedListenableFutureTask.java new file mode 100644 index 0000000000000..e4010e695e3e2 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/TrustedListenableFutureTask.java @@ -0,0 +1,187 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.RunnableFuture; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link RunnableFuture} that also implements the {@link ListenableFuture} interface. + * + *

This should be used in preference to {@link ListenableFutureTask} when possible for + * performance reasons. + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +class TrustedListenableFutureTask extends FluentFuture.TrustedFuture + implements RunnableFuture { + + static TrustedListenableFutureTask create(AsyncCallable callable) { + return new TrustedListenableFutureTask<>(callable); + } + + static TrustedListenableFutureTask create(Callable callable) { + return new TrustedListenableFutureTask<>(callable); + } + + /** + * Creates a {@code ListenableFutureTask} that will upon running, execute the given {@code + * Runnable}, and arrange that {@code get} will return the given result on successful completion. + * + * @param runnable the runnable task + * @param result the result to return on successful completion. If you don't need a particular + * result, consider using constructions of the form: {@code ListenableFuture f = + * ListenableFutureTask.create(runnable, null)} + */ + static TrustedListenableFutureTask create( + Runnable runnable, @ParametricNullness V result) { + return new TrustedListenableFutureTask<>(Executors.callable(runnable, result)); + } + + /* + * In certain circumstances, this field might theoretically not be visible to an afterDone() call + * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture. + * + *

{@code volatile} is required for j2objc transpiling: + * https://developers.google.com/j2objc/guides/j2objc-memory-model#atomicity + */ + private volatile InterruptibleTask task; + + TrustedListenableFutureTask(Callable callable) { + this.task = new TrustedFutureInterruptibleTask(callable); + } + + TrustedListenableFutureTask(AsyncCallable callable) { + this.task = new TrustedFutureInterruptibleAsyncTask(callable); + } + + @Override + public void run() { + InterruptibleTask localTask = task; + if (localTask != null) { + localTask.run(); + } + /* + * In the Async case, we may have called setFuture(pendingFuture), in which case afterDone() + * won't have been called yet. + */ + this.task = null; + } + + @Override + protected void afterDone() { + super.afterDone(); + + if (wasInterrupted()) { + InterruptibleTask localTask = task; + if (localTask != null) { + localTask.interruptTask(); + } + } + + this.task = null; + } + + @Override + protected String pendingToString() { + InterruptibleTask localTask = task; + if (localTask != null) { + return "task=[" + localTask + "]"; + } + return super.pendingToString(); + } + + private final class TrustedFutureInterruptibleTask extends InterruptibleTask { + private final Callable callable; + + TrustedFutureInterruptibleTask(Callable callable) { + this.callable = checkNotNull(callable); + } + + @Override + final boolean isDone() { + return TrustedListenableFutureTask.this.isDone(); + } + + @Override + @ParametricNullness + V runInterruptibly() throws Exception { + return callable.call(); + } + + @Override + void afterRanInterruptiblySuccess(@ParametricNullness V result) { + TrustedListenableFutureTask.this.set(result); + } + + @Override + void afterRanInterruptiblyFailure(Throwable error) { + setException(error); + } + + @Override + String toPendingString() { + return callable.toString(); + } + } + + private final class TrustedFutureInterruptibleAsyncTask + extends InterruptibleTask> { + private final AsyncCallable callable; + + TrustedFutureInterruptibleAsyncTask(AsyncCallable callable) { + this.callable = checkNotNull(callable); + } + + @Override + final boolean isDone() { + return TrustedListenableFutureTask.this.isDone(); + } + + @Override + ListenableFuture runInterruptibly() throws Exception { + return checkNotNull( + callable.call(), + "AsyncCallable.call returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)? %s", + callable); + } + + @Override + void afterRanInterruptiblySuccess(ListenableFuture result) { + setFuture(result); + } + + @Override + void afterRanInterruptiblyFailure(Throwable error) { + setException(error); + } + + @Override + String toPendingString() { + return callable.toString(); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncaughtExceptionHandlers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncaughtExceptionHandlers.java new file mode 100644 index 0000000000000..039de7a6eb014 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -0,0 +1,89 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Locale; +import java.util.logging.Logger; + +import static java.util.logging.Level.SEVERE; + +/** + * Factories for {@link UncaughtExceptionHandler} instances. + * + * @author Gregory Kick + * @since 8.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public final class UncaughtExceptionHandlers { + private UncaughtExceptionHandlers() {} + + /** + * Returns an exception handler that exits the system. This is particularly useful for the main + * thread, which may start up other, non-daemon threads, but fail to fully initialize the + * application successfully. + * + *

Example usage: + * + *

+   * public static void main(String[] args) {
+   *   Thread.currentThread().setUncaughtExceptionHandler(UncaughtExceptionHandlers.systemExit());
+   *   ...
+   * 
+ * + *

The returned handler logs any exception at severity {@code SEVERE} and then shuts down the + * process with an exit status of 1, indicating abnormal termination. + */ + public static UncaughtExceptionHandler systemExit() { + return new Exiter(Runtime.getRuntime()); + } + + @VisibleForTesting + static final class Exiter implements UncaughtExceptionHandler { + private static final Logger logger = Logger.getLogger(Exiter.class.getName()); + + private final Runtime runtime; + + Exiter(Runtime runtime) { + this.runtime = runtime; + } + + @Override + public void uncaughtException(Thread t, Throwable e) { + try { + logger.log( + SEVERE, String.format(Locale.ROOT, "Caught an exception in %s. Shutting down.", t), e); + } catch (RuntimeException | Error errorInLogging) { + // If logging fails, e.g. due to missing memory, at least try to log the + // message and the cause for the failed logging. + System.err.println(e.getMessage()); + System.err.println(errorInLogging.getMessage()); + } finally { + runtime.exit(1); + } + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncheckedExecutionException.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncheckedExecutionException.java new file mode 100644 index 0000000000000..a35bdd5cb3382 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncheckedExecutionException.java @@ -0,0 +1,67 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Unchecked variant of {@link java.util.concurrent.ExecutionException}. As with {@code + * ExecutionException}, the exception's {@linkplain #getCause() cause} comes from a failed task, + * possibly run in another thread. + * + *

{@code UncheckedExecutionException} is intended as an alternative to {@code + * ExecutionException} when the exception thrown by a task is an unchecked exception. However, it + * may also wrap a checked exception in some cases. + * + *

When wrapping an {@code Error} from another thread, prefer {@link ExecutionError}. When + * wrapping a checked exception, prefer {@code ExecutionException}. + * + * @author Charles Fry + * @since 10.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public class UncheckedExecutionException extends RuntimeException { + /* + * Ideally, this class would have exposed only constructors that require a non-null cause. We + * might try to move in that direction, but there are complications. See + * https://github.com/jspecify/jspecify-reference-checker/blob/61aafa4ae52594830cfc2d61c8b113009dbdb045/src/main/java/com/google/jspecify/nullness/NullSpecTransfer.java#L789 + */ + + /** Creates a new instance with {@code null} as its detail message. */ + protected UncheckedExecutionException() {} + + /** Creates a new instance with the given detail message. */ + protected UncheckedExecutionException(String message) { + super(message); + } + + /** Creates a new instance with the given detail message and cause. */ + public UncheckedExecutionException(String message, Throwable cause) { + super(message, cause); + } + + /** Creates a new instance with the given cause. */ + public UncheckedExecutionException(Throwable cause) { + super(cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncheckedTimeoutException.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncheckedTimeoutException.java new file mode 100644 index 0000000000000..aed2cd7b64108 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/UncheckedTimeoutException.java @@ -0,0 +1,50 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +/** + * Unchecked version of {@link java.util.concurrent.TimeoutException}. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +public class UncheckedTimeoutException extends RuntimeException { + public UncheckedTimeoutException() {} + + public UncheckedTimeoutException(String message) { + super(message); + } + + public UncheckedTimeoutException(Throwable cause) { + super(cause); + } + + public UncheckedTimeoutException(String message, Throwable cause) { + super(message, cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Uninterruptibles.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Uninterruptibles.java new file mode 100644 index 0000000000000..20a6559cae55f --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/Uninterruptibles.java @@ -0,0 +1,595 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.base.Preconditions; + +import java.time.Duration; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.apache.iotdb.google.common.base.Verify.verify; +import static org.apache.iotdb.google.common.util.concurrent.Internal.toNanosSaturated; + +/** + * Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is + * interrupted during such a call, the call continues to block until the result is available or the + * timeout elapses, and only then re-interrupts the thread. + * + * @author Anthony Zana + * @since 10.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Uninterruptibles { + + // Implementation Note: As of 3-7-11, the logic for each blocking/timeout + // methods is identical, save for method being invoked. + + /** Invokes {@code latch.}{@link CountDownLatch#await() await()} uninterruptibly. */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static void awaitUninterruptibly(CountDownLatch latch) { + boolean interrupted = false; + try { + while (true) { + try { + latch.await(); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)} + * uninterruptibly. + * + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static boolean awaitUninterruptibly(CountDownLatch latch, Duration timeout) { + return awaitUninterruptibly(latch, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)} + * uninterruptibly. + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + // CountDownLatch treats negative timeouts just like zero. + return latch.await(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)} + * uninterruptibly. + * + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static boolean awaitUninterruptibly(Condition condition, Duration timeout) { + return awaitUninterruptibly(condition, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)} + * uninterruptibly. + * + * @since 23.6 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + return condition.await(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static void joinUninterruptibly(Thread toJoin) { + boolean interrupted = false; + try { + while (true) { + try { + toJoin.join(); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)} + * uninterruptibly. + * + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static void joinUninterruptibly(Thread toJoin, Duration timeout) { + joinUninterruptibly(toJoin, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)} + * uninterruptibly. + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) { + Preconditions.checkNotNull(toJoin); + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + while (true) { + try { + // TimeUnit.timedJoin() treats negative timeouts just like zero. + NANOSECONDS.timedJoin(toJoin, remainingNanos); + return; + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code future.}{@link Future#get() get()} uninterruptibly. + * + *

Similar methods: + * + *

    + *
  • To retrieve a result from a {@code Future} that is already done, use {@link + * Futures#getDone Futures.getDone}. + *
  • To treat {@link InterruptedException} uniformly with other exceptions, use {@link + * Futures#getChecked(Future, Class) Futures.getChecked}. + *
  • To get uninterruptibility and remove checked exceptions, use {@link + * Futures#getUnchecked}. + *
+ * + * @throws ExecutionException if the computation threw an exception + * @throws CancellationException if the computation was cancelled + */ + @ParametricNullness + public static V getUninterruptibly(Future future) + throws ExecutionException { + boolean interrupted = false; + try { + while (true) { + try { + return future.get(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly. + * + *

Similar methods: + * + *

    + *
  • To retrieve a result from a {@code Future} that is already done, use {@link + * Futures#getDone Futures.getDone}. + *
  • To treat {@link InterruptedException} uniformly with other exceptions, use {@link + * Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}. + *
  • To get uninterruptibility and remove checked exceptions, use {@link + * Futures#getUnchecked}. + *
+ * + * @throws ExecutionException if the computation threw an exception + * @throws CancellationException if the computation was cancelled + * @throws TimeoutException if the wait timed out + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // java.time.Duration + @ParametricNullness + public static V getUninterruptibly(Future future, Duration timeout) + throws ExecutionException, TimeoutException { + return getUninterruptibly(future, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly. + * + *

Similar methods: + * + *

    + *
  • To retrieve a result from a {@code Future} that is already done, use {@link + * Futures#getDone Futures.getDone}. + *
  • To treat {@link InterruptedException} uniformly with other exceptions, use {@link + * Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}. + *
  • To get uninterruptibility and remove checked exceptions, use {@link + * Futures#getUnchecked}. + *
+ * + * @throws ExecutionException if the computation threw an exception + * @throws CancellationException if the computation was cancelled + * @throws TimeoutException if the wait timed out + */ + @J2ktIncompatible + @GwtIncompatible // TODO + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + @ParametricNullness + public static V getUninterruptibly( + Future future, long timeout, TimeUnit unit) throws ExecutionException, TimeoutException { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + // Future treats negative timeouts just like zero. + return future.get(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static E takeUninterruptibly(BlockingQueue queue) { + boolean interrupted = false; + try { + while (true) { + try { + return queue.take(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} uninterruptibly. + * + * @throws ClassCastException if the class of the specified element prevents it from being added + * to the given queue + * @throws IllegalArgumentException if some property of the specified element prevents it from + * being added to the given queue + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static void putUninterruptibly(BlockingQueue queue, E element) { + boolean interrupted = false; + try { + while (true) { + try { + queue.put(element); + return; + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + // TODO(user): Support Sleeper somehow (wrapper or interface method)? + /** + * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. + * + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static void sleepUninterruptibly(Duration sleepFor) { + sleepUninterruptibly(toNanosSaturated(sleepFor), TimeUnit.NANOSECONDS); + } + + // TODO(user): Support Sleeper somehow (wrapper or interface method)? + /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(sleepFor); + long end = System.nanoTime() + remainingNanos; + while (true) { + try { + // TimeUnit.sleep() treats negative timeouts just like zero. + NANOSECONDS.sleep(remainingNanos); + return; + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1, + * timeout, unit)} uninterruptibly. + * + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static boolean tryAcquireUninterruptibly(Semaphore semaphore, Duration timeout) { + return tryAcquireUninterruptibly(semaphore, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1, + * timeout, unit)} uninterruptibly. + * + * @since 18.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static boolean tryAcquireUninterruptibly( + Semaphore semaphore, long timeout, TimeUnit unit) { + return tryAcquireUninterruptibly(semaphore, 1, timeout, unit); + } + + /** + * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits, + * timeout, unit)} uninterruptibly. + * + * @since 28.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static boolean tryAcquireUninterruptibly( + Semaphore semaphore, int permits, Duration timeout) { + return tryAcquireUninterruptibly( + semaphore, permits, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits, + * timeout, unit)} uninterruptibly. + * + * @since 18.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static boolean tryAcquireUninterruptibly( + Semaphore semaphore, int permits, long timeout, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + // Semaphore treats negative timeouts just like zero. + return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)} + * uninterruptibly. + * + * @since 30.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static boolean tryLockUninterruptibly(Lock lock, Duration timeout) { + return tryLockUninterruptibly(lock, toNanosSaturated(timeout), TimeUnit.NANOSECONDS); + } + + /** + * Invokes {@code lock.}{@link Lock#tryLock(long, TimeUnit) tryLock(timeout, unit)} + * uninterruptibly. + * + * @since 30.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") // should accept a java.time.Duration + public static boolean tryLockUninterruptibly(Lock lock, long timeout, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + return lock.tryLock(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit) + * awaitTermination(long, TimeUnit)} uninterruptibly with no timeout. + * + * @since 30.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static void awaitTerminationUninterruptibly(ExecutorService executor) { + // TODO(cpovirk): We could optimize this to avoid calling nanoTime() at all. + verify(awaitTerminationUninterruptibly(executor, Long.MAX_VALUE, NANOSECONDS)); + } + + /** + * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit) + * awaitTermination(long, TimeUnit)} uninterruptibly. + * + * @since 30.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + public static boolean awaitTerminationUninterruptibly( + ExecutorService executor, Duration timeout) { + return awaitTerminationUninterruptibly(executor, toNanosSaturated(timeout), NANOSECONDS); + } + + /** + * Invokes {@code executor.}{@link ExecutorService#awaitTermination(long, TimeUnit) + * awaitTermination(long, TimeUnit)} uninterruptibly. + * + * @since 30.0 + */ + @J2ktIncompatible + @GwtIncompatible // concurrency + @SuppressWarnings("GoodTime") + public static boolean awaitTerminationUninterruptibly( + ExecutorService executor, long timeout, TimeUnit unit) { + boolean interrupted = false; + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while (true) { + try { + return executor.awaitTermination(remainingNanos, NANOSECONDS); + } catch (InterruptedException e) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + // TODO(user): Add support for waitUninterruptibly. + + private Uninterruptibles() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/WrappingExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/WrappingExecutorService.java new file mode 100644 index 0000000000000..39a4526fb5978 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/WrappingExecutorService.java @@ -0,0 +1,170 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; +import org.apache.iotdb.google.common.collect.ImmutableList; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.base.Throwables.throwIfUnchecked; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** + * An abstract {@code ExecutorService} that allows subclasses to {@linkplain #wrapTask(Callable) + * wrap} tasks before they are submitted to the underlying executor. + * + *

Note that task wrapping may occur even if the task is never executed. + * + *

For delegation without task-wrapping, see {@link ForwardingExecutorService}. + * + * @author Chris Nokleberg + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class WrappingExecutorService implements ExecutorService { + private final ExecutorService delegate; + + protected WrappingExecutorService(ExecutorService delegate) { + this.delegate = checkNotNull(delegate); + } + + /** + * Wraps a {@code Callable} for submission to the underlying executor. This method is also applied + * to any {@code Runnable} passed to the default implementation of {@link #wrapTask(Runnable)}. + */ + protected abstract Callable wrapTask(Callable callable); + + /** + * Wraps a {@code Runnable} for submission to the underlying executor. The default implementation + * delegates to {@link #wrapTask(Callable)}. + */ + protected Runnable wrapTask(Runnable command) { + Callable wrapped = wrapTask(Executors.callable(command, null)); + return () -> { + try { + wrapped.call(); + } catch (Exception e) { + restoreInterruptIfIsInterruptedException(e); + throwIfUnchecked(e); + throw new RuntimeException(e); + } + }; + } + + /** + * Wraps a collection of tasks. + * + * @throws NullPointerException if any element of {@code tasks} is null + */ + private ImmutableList> wrapTasks( + Collection> tasks) { + ImmutableList.Builder> builder = ImmutableList.builder(); + for (Callable task : tasks) { + builder.add(wrapTask(task)); + } + return builder.build(); + } + + // These methods wrap before delegating. + @Override + public final void execute(Runnable command) { + delegate.execute(wrapTask(command)); + } + + @Override + public final Future submit(Callable task) { + return delegate.submit(wrapTask(checkNotNull(task))); + } + + @Override + public final Future submit(Runnable task) { + return delegate.submit(wrapTask(task)); + } + + @Override + public final Future submit(Runnable task, @ParametricNullness T result) { + return delegate.submit(wrapTask(task), result); + } + + @Override + public final List> invokeAll(Collection> tasks) + throws InterruptedException { + return delegate.invokeAll(wrapTasks(tasks)); + } + + @Override + public final List> invokeAll( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { + return delegate.invokeAll(wrapTasks(tasks), timeout, unit); + } + + @Override + public final T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + return delegate.invokeAny(wrapTasks(tasks)); + } + + @Override + public final T invokeAny( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return delegate.invokeAny(wrapTasks(tasks), timeout, unit); + } + + // The remaining methods just delegate. + + @Override + public final void shutdown() { + delegate.shutdown(); + } + + @Override + public final List shutdownNow() { + return delegate.shutdownNow(); + } + + @Override + public final boolean isShutdown() { + return delegate.isShutdown(); + } + + @Override + public final boolean isTerminated() { + return delegate.isTerminated(); + } + + @Override + public final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegate.awaitTermination(timeout, unit); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/WrappingScheduledExecutorService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/WrappingScheduledExecutorService.java new file mode 100644 index 0000000000000..1a11c9efcab5e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/WrappingScheduledExecutorService.java @@ -0,0 +1,72 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * An abstract {@code ScheduledExecutorService} that allows subclasses to {@linkplain + * #wrapTask(Callable) wrap} tasks before they are submitted to the underlying executor. + * + *

Note that task wrapping may occur even if the task is never executed. + * + * @author Luke Sandberg + */ +@J2ktIncompatible +@GwtIncompatible +@ElementTypesAreNonnullByDefault +abstract class WrappingScheduledExecutorService extends WrappingExecutorService + implements ScheduledExecutorService { + final ScheduledExecutorService delegate; + + protected WrappingScheduledExecutorService(ScheduledExecutorService delegate) { + super(delegate); + this.delegate = delegate; + } + + @Override + public final ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return delegate.schedule(wrapTask(command), delay, unit); + } + + @Override + public final ScheduledFuture schedule( + Callable task, long delay, TimeUnit unit) { + return delegate.schedule(wrapTask(task), delay, unit); + } + + @Override + public final ScheduledFuture scheduleAtFixedRate( + Runnable command, long initialDelay, long period, TimeUnit unit) { + return delegate.scheduleAtFixedRate(wrapTask(command), initialDelay, period, unit); + } + + @Override + public final ScheduledFuture scheduleWithFixedDelay( + Runnable command, long initialDelay, long delay, TimeUnit unit) { + return delegate.scheduleWithFixedDelay(wrapTask(command), initialDelay, delay, unit); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/internal/InternalFutureFailureAccess.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/internal/InternalFutureFailureAccess.java new file mode 100644 index 0000000000000..7f1a78eb24c22 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/internal/InternalFutureFailureAccess.java @@ -0,0 +1,57 @@ +/* + * 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.iotdb.google.common.util.concurrent.internal; + +/** + * A future that, if it fails, may optionally provide access to the cause of the failure. + * + *

This class is used only for micro-optimization. Standard {@code Future} utilities benefit from + * this optimization, so there is no need to specialize methods to return or accept this type + * instead of {@code ListenableFuture}. + * + *

This class is GWT-compatible. + * + * @since {@code com.google.guava:failureaccess:1.0}, which was added as a dependency of Guava in + * Guava 27.0 + */ +public abstract class InternalFutureFailureAccess { + /** Constructor for use by subclasses. */ + protected InternalFutureFailureAccess() {} + + /** + * Usually returns {@code null} but, if this {@code Future} has failed, may optionally + * return the cause of the failure. "Failure" means specifically "completed with an exception"; it + * does not include "was cancelled." To be explicit: If this method returns a non-null value, + * then: + * + *

    + *
  • {@code isDone()} must return {@code true} + *
  • {@code isCancelled()} must return {@code false} + *
  • {@code get()} must not block, and it must throw an {@code ExecutionException} with the + * return value of this method as its cause + *
+ * + *

This method is {@code protected} so that classes like {@code + * org.apache.iotdb.google.common.util.concurrent.SettableFuture} do not expose it to their users + * as an instance method. In the unlikely event that you need to call this method, call {@link + * InternalFutures#tryInternalFastPathGetFailure(InternalFutureFailureAccess)}. + */ + protected abstract Throwable tryInternalFastPathGetFailure(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/internal/InternalFutures.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/internal/InternalFutures.java new file mode 100644 index 0000000000000..3188737664a41 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/internal/InternalFutures.java @@ -0,0 +1,49 @@ +/* + * 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.iotdb.google.common.util.concurrent.internal; + +/** + * Static utilities for {@link InternalFutureFailureAccess}. Most users will never need to use this + * class. + * + *

This class is GWT-compatible. + * + * @since {@code :failureaccess:1.0}, which was added as a dependency of Guava in Guava 27.0 + */ +public final class InternalFutures { + /** + * Usually returns {@code null} but, if the given {@code Future} has failed, may optionally + * return the cause of the failure. "Failure" means specifically "completed with an exception"; it + * does not include "was cancelled." To be explicit: If this method returns a non-null value, + * then: + * + *

    + *
  • {@code isDone()} must return {@code true} + *
  • {@code isCancelled()} must return {@code false} + *
  • {@code get()} must not block, and it must throw an {@code ExecutionException} with the + * return value of this method as its cause + *
+ */ + public static Throwable tryInternalFastPathGetFailure(InternalFutureFailureAccess future) { + return future.tryInternalFastPathGetFailure(); + } + + private InternalFutures() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/package-info.java new file mode 100644 index 0000000000000..11220bf62b651 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.util.concurrent; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/ElementTypesAreNonnullByDefault.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/ElementTypesAreNonnullByDefault.java new file mode 100644 index 0000000000000..40927ab1160de --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/ElementTypesAreNonnullByDefault.java @@ -0,0 +1,38 @@ +/* + * 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.iotdb.google.common.xml; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marks all "top-level" types as non-null in a way that is recognized by Kotlin. Note that this + * unfortunately includes type-variable usages, so we also provide {@link ParametricNullness} to + * "undo" it as best we can. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target(TYPE) +@interface ElementTypesAreNonnullByDefault {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/ParametricNullness.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/ParametricNullness.java new file mode 100644 index 0000000000000..cda9387b93605 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/ParametricNullness.java @@ -0,0 +1,87 @@ +/* + * 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.iotdb.google.common.xml; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates a "top-level" type-variable usage that takes its nullness from the type argument + * supplied by the user of the class. For example, {@code Multiset.Entry.getElement()} returns + * {@code @ParametricNullness E}, which means: + * + *
    + *
  • {@code getElement} on a {@code Multiset.Entry<@NonNull String>} returns {@code @NonNull + * String}. + *
  • {@code getElement} on a {@code Multiset.Entry<@Nullable String>} returns {@code @Nullable + * String}. + *
+ * + * This is the same behavior as type-variable usages have to Kotlin and to the Checker Framework. + * Contrast the method above to: + * + *
    + *
  • methods whose return type is a type variable but which can never return {@code null}, + * typically because the type forbids nullable type arguments: For example, {@code + * ImmutableList.get} returns {@code E}, but that value is never {@code null}. (Accordingly, + * {@code ImmutableList} is declared to forbid {@code ImmutableList<@Nullable String>}.) + *
  • methods whose return type is a type variable but which can return {@code null} regardless + * of the type argument supplied by the user of the class: For example, {@code + * ImmutableMap.get} returns {@code @Nullable E} because the method can return {@code null} + * even on an {@code ImmutableMap}. + *
+ * + *

Consumers of this annotation include: + * + *

    + *
  • Kotlin, for which it makes the type-variable usage (a) a Kotlin platform type when the type + * argument is non-nullable and (b) nullable when the type argument is nullable. We use this + * to "undo" {@link ElementTypesAreNonnullByDefault}. It is the best we can do for Kotlin + * under our current constraints. + *
  • NullAway, which will treat it + * identically to {@code Nullable} as of version 0.9.9. To treat it that way before then, + * you can set {@code + * -XepOpt:NullAway:CustomNullableAnnotations=org.apache.iotdb.google.common.base.ParametricNullness,...,org.apache.iotdb.google.common.util.concurrent.ParametricNullness}, + * where the {@code ...} contains the names of all the other {@code ParametricNullness} + * annotations in Guava. Or you might prefer to omit Guava from your {@code AnnotatedPackages} + * list. + *
  • J2ObjC + *
  • {@code NullPointerTester}, at least in the Android backport (where the type-use annotations + * {@code NullPointerTester} would need are not available) and in case of JDK-8202469 + *
+ * + *

This annotation is a temporary hack. We will remove it after we're able to adopt the JSpecify nullness annotations and tools no longer need + * it. + */ +@GwtCompatible +@Retention(RUNTIME) +@Target({FIELD, METHOD, PARAMETER}) +@interface ParametricNullness {} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/XmlEscapers.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/XmlEscapers.java new file mode 100644 index 0000000000000..116aba413a6a5 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/XmlEscapers.java @@ -0,0 +1,148 @@ +/* + * 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.iotdb.google.common.xml; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.escape.Escaper; +import org.apache.iotdb.google.common.escape.Escapers; + +/** + * {@code Escaper} instances suitable for strings to be included in XML attribute values and + * elements' text contents. When possible, avoid manual escaping by using templating systems and + * high-level APIs that provide autoescaping. For example, consider XOM or JDOM. + * + *

Note: Currently the escapers provided by this class do not escape any characters + * outside the ASCII character range. Unlike HTML escaping the XML escapers will not escape + * non-ASCII characters to their numeric entity replacements. These XML escapers provide the minimal + * level of escaping to ensure that the output can be safely included in a Unicode XML document. + * + *

For details on the behavior of the escapers in this class, see sections 2.2 and 2.4 of the XML specification. + * + * @author Alex Matevossian + * @author David Beaumont + * @since 15.0 + */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +public class XmlEscapers { + private XmlEscapers() {} + + private static final char MIN_ASCII_CONTROL_CHAR = 0x00; + private static final char MAX_ASCII_CONTROL_CHAR = 0x1F; + + // For each xxxEscaper() method, please add links to external reference pages + // that are considered authoritative for the behavior of that escaper. + + /** + * Returns an {@link Escaper} instance that escapes special characters in a string so it can + * safely be included in an XML document as element content. See section 2.4 of the XML specification. + * + *

Note: Double and single quotes are not escaped, so it is not safe to use this + * escaper to escape attribute values. Use {@link #xmlContentEscaper} if the output can appear in + * element content or {@link #xmlAttributeEscaper} in attribute values. + * + *

This escaper substitutes {@code 0xFFFD} for non-whitespace control characters and the + * character values {@code 0xFFFE} and {@code 0xFFFF} which are not permitted in XML. For more + * detail see section 2.2 of + * the XML specification. + * + *

This escaper does not escape non-ASCII characters to their numeric character references + * (NCR). Any non-ASCII characters appearing in the input will be preserved in the output. + * Specifically "\r" (carriage return) is preserved in the output, which may result in it being + * silently converted to "\n" when the XML is parsed. + * + *

This escaper does not treat surrogate pairs specially and does not perform Unicode + * validation on its input. + */ + public static Escaper xmlContentEscaper() { + return XML_CONTENT_ESCAPER; + } + + /** + * Returns an {@link Escaper} instance that escapes special characters in a string so it can + * safely be included in XML document as an attribute value. See section 3.3.3 of the XML + * specification. + * + *

This escaper substitutes {@code 0xFFFD} for non-whitespace control characters and the + * character values {@code 0xFFFE} and {@code 0xFFFF} which are not permitted in XML. For more + * detail see section 2.2 of + * the XML specification. + * + *

This escaper does not escape non-ASCII characters to their numeric character references + * (NCR). However, horizontal tab {@code '\t'}, line feed {@code '\n'} and carriage return {@code + * '\r'} are escaped to a corresponding NCR {@code " "}, {@code " "}, and {@code " "} + * respectively. Any other non-ASCII characters appearing in the input will be preserved in the + * output. + * + *

This escaper does not treat surrogate pairs specially and does not perform Unicode + * validation on its input. + */ + public static Escaper xmlAttributeEscaper() { + return XML_ATTRIBUTE_ESCAPER; + } + + private static final Escaper XML_ESCAPER; + private static final Escaper XML_CONTENT_ESCAPER; + private static final Escaper XML_ATTRIBUTE_ESCAPER; + + static { + Escapers.Builder builder = Escapers.builder(); + // The char values \uFFFE and \uFFFF are explicitly not allowed in XML + // (Unicode code points above \uFFFF are represented via surrogate pairs + // which means they are treated as pairs of safe characters). + builder.setSafeRange(Character.MIN_VALUE, '\uFFFD'); + // Unsafe characters are replaced with the Unicode replacement character. + builder.setUnsafeReplacement("\uFFFD"); + + /* + * Except for \n, \t, and \r, all ASCII control characters are replaced with the Unicode + * replacement character. + * + * Implementation note: An alternative to the following would be to make a map that simply + * replaces the allowed ASCII whitespace characters with themselves and to set the minimum safe + * character to 0x20. However this would slow down the escaping of simple strings that contain + * \t, \n, or \r. + */ + for (char c = MIN_ASCII_CONTROL_CHAR; c <= MAX_ASCII_CONTROL_CHAR; c++) { + if (c != '\t' && c != '\n' && c != '\r') { + builder.addEscape(c, "\uFFFD"); + } + } + + // Build the content escaper first and then add quote escaping for the + // general escaper. + builder.addEscape('&', "&"); + builder.addEscape('<', "<"); + builder.addEscape('>', ">"); + XML_CONTENT_ESCAPER = builder.build(); + builder.addEscape('\'', "'"); + builder.addEscape('"', """); + XML_ESCAPER = builder.build(); + builder.addEscape('\t', " "); + builder.addEscape('\n', " "); + builder.addEscape('\r', " "); + XML_ATTRIBUTE_ESCAPER = builder.build(); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/package-info.java new file mode 100644 index 0000000000000..e302d567a6647 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/xml/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.xml; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/PublicSuffixPatterns.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/PublicSuffixPatterns.java new file mode 100644 index 0000000000000..849a6df2e1888 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/PublicSuffixPatterns.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +// GENERATED FILE - DO NOT EDIT +package org.apache.iotdb.google.thirdparty.publicsuffix; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.collect.ImmutableMap; + +/** + * Do not use this class directly. For access to public-suffix information, use {@link + * org.apache.iotdb.google.common.net.InternetDomainName}. + * + *

A generated static class containing public members which provide domain name patterns used in + * determining whether a given domain name is an effective top-level domain (public suffix). + * + *

Because this class is used in GWT, the data members are stored in a space-efficient manner. + * See {@link TrieParser}. + * + * @since 16.0 + */ +@GwtCompatible +@Beta +public final class PublicSuffixPatterns { + private PublicSuffixPatterns() {} + + /** If a hostname is contained as a key in this map, it is a public suffix. */ + public static final ImmutableMap EXACT = + TrieParser.parseTrie( + "a&0&0trk9--nx?27qjf--nx?e9ebgn--nx?nbb0c7abgm--nx??1&2oa08--nx?apg6qpcbgm--nx?hbbgm--nx?rdceqa08--nx??2&8ugbgm--nx?eyh3la2ckx--nx?qbd9--nx??3&2wqq1--nx?60a0y8--nx??4x1d77xrck--nx?6&1f4a3abgm--nx?2yqyn--nx?5b06t--nx?axq--nx?ec7q--nx?lbgw--nx??883xnn--nx?9d2c24--nx?a&a?it??b!.&gro?lim?moc?sr,t&en?opsgolb,?ude?vog??abila?c?ihsot?m?n??c!.&b&a?m?n??c&b?g?q??ep?fn?k&s?y??ln?no?oc,p&i-on,ohsdaerpsym,?sn?t&n?opsgolb,?un?ysrab,?i&ma?r&emarp?fa??sroc??naiva?s??d&ats?n&eit?oh??om?sa?tl??eg?f&c?ob??g!emo?naripi?oy??hskihs?i&dem!.remarf,?hs?k!on??sa!.snduolc,??jnin?k&aso?dov?ede?usto??l!.&c,gro?moc?ofni?r&ep?nb,?t&en?ni??ude?vog??irgnahs?le&nisiuc?rbmuder???m!.&ca?gro?oc?sserp?ten?vog??ahokoy?e00sf7vqn--nx?m??n!.&ac?cc?eman?gro?ibom?loohcs?moc?ni?o&c?fni?rp??r&d?o??s&u?w??vt?xm??av?is?olecrab?tea??p!.&bog?ca?d&em?ls??g&ni?ro??mo&c?n??oba?ten?ude??c?g7hyabgm--nx?ra!.&461e?6pi?iru?nru?rdda-ni?siri???s??q!.&eman?gro?hcs?lim?moc?t&en?opsgolb,?ude?vog???r&az?emac?f4a3abgm--nx?n!d5uhf8le58r4w--nx??u&kas?tan???s!.&bup?dem?gro?hcs?moc?ten?ude?vog??ac!.uban.iu,?iv??t&ad?elhta?led?oyot??u!.&a&cinniv?emirc?i&hzhziropaz?stynniv??s&edo?sedo??tlay?vatlop??bs?cc,d&argovorik?o!roghzu??tl,?e&hzhziropaz?i,nvir?t??f&i?ni,?g&l?ro??hk?i&stvinrehc?ykstyn&lemhk?vypork???k&c?m?s&nagul?t&enod?ul??v&iknarf-onavi?orteporp&end?ind?????l&iponret?opotsa&bes?ves??p??m&k?oc?s?yrk??n&c?d?i?osrehk?v?ylov??o&c,nvor??p&d?p,z??r&c?imotihz?k?ymotyhz??sk?t&en?l?z??ude?v:c?e&alokin?ik??i&alokym?hinrehc?krahk?vl?yk??k?l?o&g!inrehc??krahk??r?,xc,y&ikstinlemhk?mus?s&akrehc?sakrehc?tvonrehc???z&ib,u????v!aj?bb?et?iv??waniko?x&a?iacal??yogan?z&.&bew?c&a?i&n?rga???gro?l&im?oohcs??m&on?t??o&c!.topsgolb,?gn??radnorg?sin?t&en?la??ude?vog?wal??zip!.korgn,???b&00ave5a9iabgm--nx?1&25qhx--nx?68quv--nx?e2kc1--nx??2xtbgm--nx?3&b2kcc--nx?jca1d--nx??4&6&1rfz--nx?qif--nx??96rzc--nx??88uvor--nx?a&0dc4xbgm--nx?c?her?n?ra?t??b!.&erots?gro?moc?o&c?fni??ten?ude?v&og?t??zib??a??c&j?s??d&hesa08--nx?mi??g?l!.&gro?moc?ten?ude?vog??m??s!.&gro?moc?ten?ude?vog???tc-retarebsnegmrev--nx?u&lc!.&elej,snduolc,ysrab,?smas??p!.ysrab,??wp-gnutarebsnegmrev--nx??c&1&1q54--nx?hbgw--nx??2e9c2czf--nx?4&4ub1km--nx?a1e--nx?byj9q--nx?erd5a9b1kcb--nx??8&4xx2g--nx?c9jrb2h--nx??9jr&b&2h--nx?54--nx?9s--nx??c&eg--nx?h3--nx?s2--nx???a!.&gro?lim?moc?rrd,ten?ude?vog??3a09--nx!.&ca1o--nx?gva1c--nx?h&ca1o--nx?za09--nx??ta1d--nx?ua08--nx????b&a?b?ci?f76a0c7ylqbgm--nx?sh??c!.&eugaelysatnaf,gnipparcs,liamwt,nwaps.secnatsni,revres-emag,s&nduolc,otohpym,seccaptf,?xsc,?0atf7b45--nx?a1l--nx??e!.&21k?bog?dem?esab,gro?l&aiciffo,im??moc?nif?o&fni?rp??ten?ude?vog??beuq?n?smoc??fdh?i&l&buperananab?ohtac??n&agro?ilc?osanap??sum?tic??l!.&gro?moc?oc?ten?ude?vog?yo,?l??m!.&mt?ossa??p1akcq--nx??n!.&mon?ossa??i?p??relcel?s!.&gro?moc?ten?ude?vog???t!.&e&m,w,?hc,?s?w??v!.&e0,gro?lim?moc?ten?ude?v&g:.d,,og????wp?yn??d&2urzc--nx?3&1wrpk--nx?c&4b11--nx?9jrcpf--nx???5xq55--nx?697uto--nx?75yrpk--nx?9ctdvkce--nx?a!.mon?d?er?olnwod??b2babgm--nx?c!.vog?g9a2g2b0ae0chclc--nx??e&m!bulc??r!k??sopxe?timil?w??fc?g!.&ude?vog???h&d3tbgm--nx?p?t??i!.&ased?bew?ca?etrof,hcs?lim?o&c!.topsgolb,?g??palf,ro?sepnop?ten?ym?zib??b?ordna?p?rdam??l&iub?og?row??m!.&ed,ot,pj,t&a,opsgolb,???n&a&b?l!.citats:.&setis,ved,?,raas???ob?uf??o&of?rp??r&a&c&tiderc?yalcrab??ugnav??ef506w4b--nx?k!.&oc,ude,?jh3a1habgm--nx??of??s!.&dem?gro?moc?ofni?ten?ude?v&og?t???m!kcrem???t!.topsgolb,excwkcc--nx?l??uolc!.&a&bura-vnej.&1ti,abura.rue.1ti,?tcepsrep,xo:.&ku,nt,?,?b&dnevar,ewilek:.sc,,?citsalej.piv,drayknil,elej,gnitsohdnert.&ed,hc,?letemirp:.ku,,m&edaid,ialcer.&ac,ku,su,??n&evueluk,woru,?r&epolroov,o&pav,tnemele,??tenraxa.1-se,ululetoj,wcs.&gnilebaltrams,koobelacs,latemerab.&1-&rap-rf,sma-ln,?2-rap-rf,?rap-rf.&3s,cnf:.snoitcnuf,,etisbew-3s,mhw,s8k:.sedon,,?s&8k,ecnatsni.&bup,virp,?ma-ln.&3s,etisbew-3s,mhw,s8k:.sedon,,??waw-lp.&3s,etisbew-3s,s8k:.sedon,,??xelpciffart,yawocne.ue,??za5cbgn--nx??e&1&53wlf--nx?7a1hbbgm--nx?ta3kg--nx??2a6a1b6b1i--nx?3ma0e1cvr--nx?418txh--nx?707b0e3--nx?a!.&ca?gro?hcs?lim?oc?t&en?opsgolb,?vog??09--nx??b!.&ca?etisbew321,gnitsohbew,nevueluk.yxorpze,pohsdaerpsym,snoitulostsohretni.duolc,topsgolb,?ortal?ut!uoy???c&0krbd4--nx!.&a2qbd8--nx?b8adbeh--nx?c6ytdgbd4--nx?d8lhbd5--nx???a&lp!.oc,?ps!.&lla4sx,rebu,tsafym,?artxe??sla??i!ffo??n&a&d?iler?nif?rusni!efil?srelevart???eics!.oby,??rofria??d!.&1sndnyd,42pi-nyd,7erauqs,amil4,b&ow-nrefeilgitsng--nx,rb-ni,vz-nelletsebgitsng--nx,?decalpb,e&daregtmueart,luhcsvresi,mohsnd,nihcamyek,tiesbew321,?hcierebsnoissuksid,keegnietsi,lsd-ni,m&oc,rofttalpluhcs,?n&-i-g-o-l,aw-ym,e&lletsebgitsnüg,sgnutiel,?i&emtsi,lreb-n&i,yd,??norblieh-sh.ti.segap,oitatsksid-ygolonys,pv&-n&i,yd,?nyd,?refeilgitsnüg,?orp-ytinummoc,p&h21,iog:ol,,ohsdaerpsym,?r&e&ntrapdeeps.remotsuc,su&-lautriv,lautriv,?t&adpusnd,tub-ni,uor-ym,?vres&-e&bucl,mohym,?bew-emoh:.nyd,,luhcs,??ogiv-&niem,ym,??s&d-&onys,ygolonys,?nd&-&dd,nufiat,sehcsimanyd,tenretni,yard,?isoc.nyd,ps,yard,?oper-&nvs,tig,?sndd:.&nyd,sndnyd,?,?topsgolb,vresi-&niem,tset,?xi2,y&awetag-&llawerif,ym,?srab,tic-amil,?zten&mitbel,sadtretteuf,??art!.oby,?i&sdoow?ug??on--nx??e!.&bil?dem?eif?gro?irp?kiir?moc!.topsgolb,?pia?ude?vog??ei?ffoc?gg?r&f?ged???f&a&c?s??il??g!.&gro?lim?moc?t&en?vp??ude?vog??a&f?gtrom?p!.&3xlh,detalsnart,grebedoc,kselp,sndp,tengam,xlh,y&cvrp,kcor,???rots?yov??elloc?na&hcxe?ro!.hcet,??roeg?ug??i!.&pohsdaerpsym,topsgolb,vog??tilop?v&bba?om???j!.&fo,gro?oc?ten???k!.&c&a?s??e&m?n??ibom?o&c!.topsgolb,?fni?g??ro??i&b?l?n???l&a&dmrif?s!rof???b&a?i&b?dua???c&aro?ric??dnik?g!oog??i&bom?ms??l&asal?erauqa??ppa?uhcs?yts!efil???m!.&4&32i,p&ct,v,??66c,ailisarb,b&dnevar,g-raegelif,?ca?duolcsd,e&d-raegelif,i&-raegelif,lpad:.tsohlacol,,?pcm,?g&ro?s-raegelif,?hctilg,kcatsegde,noitatsksid,o&bmoy,c?t&nigol,poh,??p&i&on,snart.etis,?j-raegelif,ohbew,?r&aegelif,idcm,ofsnd,?s&dym,ndd,ti?umhol,?t&en?s&acdnuos,ohon,??u&a-raegelif,de??v&irp?og??y&golonys,olpedew,srab,??a&g?n!.&reh.togrof,sih.togrof,???em?i&rp?twohs??orhc?w??n!goloc?i&lno!.&egats-oree,oree,ysrab,??w??o!.&derno:.gnigats,,ecivres,knilemoh,?hp?latipac?ts&der?e&gdirb?rif???z!.&66duolc,amil,sh,???ruoblem??om?p!.&bog?gro?lim?mo&c?n??t&en?opsgolb,?ude??irg?yks??r!.&mo&c?n??ossa?topsgolb,?a&c!htlaeh??pmoc?wtfos??bc?eh?if?ots!.&e&rawpohs,saberots,?yflles,??taeht?u&ces?sni?t&inruf?necca??za???s!.&a!bap.us,disnim321,?b!ibnal?rofmok??c!a??d!b?n&arb?ubroflanummok???e?f!noc,?g!ro??h!f??i!trap??k!shf??l?m!oc,t??n!mygskurbrutan??o?p!ohsdaerpsym,p??r!owebdluocti,?s!serp?yspoi,?t!opsgolb,?u?vhf?w?x!uvmok??y?z??a&c?el?hc??i&er?urc??nesemoh?roh?uoh??t&a&d?ts&e!laer??lla???is!.&e&lej,nilnigol,r&etnim,ocevon,?winmo,?k&rowtenoilof,wnf,?laicosnepo,n&eyb,oyc,?spvtsaf,thrs,xulel,ysrab,?bew!.remarf,??ov?ra?t&ioled?ol??utitsni??u&lb?qi&nilc?tuob???v!.&21e?b&ew?ib?og??ce&r?t??erots?gro?lim?m&o&c?n??rif??o&c?fni??rar?stra?t&en?ni??ude?vog??as?e3gerb2h--nx?i&l!.xlh,?rd?ssergorp??ol??w&kct--nx?r??xul?y!.&gro?lim?moc?ten?ude?vog????f&0f3rkcg--nx?198xim--nx?280xim--nx?7vqn--nx?a!.&gro?moc?ten?ude?vog???b!.vog?wa9bgm--nx??c!.topsgolb,a1p--nx!.&a14--nx,b8lea1j--nx,c&avc0aaa08--nx,ma09--nx,?f&a1a09--nx,ea1j--nx,?gva1c--nx,nha1h--nx,pda1j--nx,zila1h--nx,??ns??ea1j--nx?g?iam?l&a1d--nx?og??n!.&bew?cer?erots?m&oc?rif??ofni?re&hto?p??stra?ten???orp?p!.&gro?moc?ude???rus?t!.hcs,w??vd7ckaabgm--nx?w!.&hcs,zib,???g&2&4wq55--nx?8zrf6--nx??3&44sd3--nx?91w6j--nx!.&a5wqmg--nx?d&22svcw--nx?5xq55--nx??gla0do--nx?m1qtxm--nx?vta0cu--nx????455ses--nx?5mzt5--nx?69vqhr--nx?7&8a4d5a4prebgm--nx?rb2c--nx??a!.&gro?mo&c?n??oc?ten??vd??b!.&0?1?2?3?4?5?6?7?8?9?a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t!opsgolb,?u?v?w?x?y!srab,?z???c!b?za9a0cbgm--nx??e!.&eman?gro?ics?lim?moc!.topsgolb,?nue?ten?ude?vog??a??g!.&ayc,gro?lenap:.nomead,,oc?saak,ten???i&a?v??k!.&g&olb,ro??ku,lim?moc?oi,pj,su,ten?ude?v&og?t,???m!.&drp?gro?lim?m&o&c?n??t??oc?ude?vog??pk??n!.&dtl,eman?gro?hcs?i!bom??l&im?oc,?m&oc!.topsgolb,?rif,?neg,ogn,ten?ude?vog??aw?i!b!mulp??car?d&art?dew??h&sif?tolc??k&iv?oo&b?c???ls?n&aelc?iart??p!pohs??re&enigne?tac??t&ad?ekram?hgil?lusnoc?neg?ov?soh!.tfarcnepo,??vi&g?l???o!s??u&rehcisrev?smas?tarebsnegömrev???o&d?lb?og!.&duolc,etalsnart,???r&2n084qlj--nx?ebmoolb?o!.&77ndc.c:sr,,a&remacytirucesym,t&neimip,sivretla,?z,?bew-llams,d&ab-yrev-si,e&sufnocsim,vas-si,?nuof-si,oog-yrev-si,uolc&arfniarodef,mw,??e&a,cin-yrev-si,grof&loot,peh,?l&as-4-ffuts,poeparodef,?m&-morf,agevres,ohruoyslles,?n&ozdop,uma.elet,?r&ehwongniogyldlob,iwym,uces-77ndc.nigiro.lss,?t&adidnac-a-si,is&-ybboh,golb,???fehc-a-si,golbymdaer,k&eeg-a&-si,si,?h,nut,?l&i&amwt,ve-yrev-si,?lawerif&-ym,ym,?sd-ni,?m&acssecca,edom-elbac,?n&af&blm,cfu,egelloc,lfn,s&citlec-a-si,niurb-a-si,tap-a-si,?xos-a-si,?ibptth,o&itatsksid,rviop,?p&j,v-ni,??o&jodsnd,tp&az,oh,??p&i&-on,fles,?o&hbew,tksedeerf,?tf&e&moh,vres,?ym,??r&e&gatop,ppepteews,su-xunil-a-si,?gmtrec,vdmac,?s&a&ila&nyd,snd,?nymsd,?b&alfmw,bevres,?d&ikcet.3s,ylimaf,?eirfotatophcuoc,j,koob-daer,ltbup,nd&-won,deerf,emoh,golb,kcud,mood,nyd:.&emoh,og,?,ps,rvd,tog,uolc,?s&a-skcik,ndd,?tnemhcattaomb,u,?t&ce&jorparodef.&duolc,gts.so.ppa,so.ppa,?riderbew,?e&ews-yrev-si,nretni&ehtfodne,fodne,??hgink-a-si,oi-allizom,s&ixetn&od,seod,?o&h-emag,l-si,?rifyam,??ue:.&a&-q,c,?cm,dc,e&b,d,e,i,m,s,?g&b,n,?hc,i&f,s,?k&d,m,s,u,?l&a,i,n,p,?n&c,i,?o&n,r,ssa,?pj,r&f,g,h,k,t,?s&e,i:rap,,u,?t&a,en,i,l,m,ni,p,?u&a,de,h,l,r,?vl,y&c,m,?z&c,n,??,vresnyd,x&inuemoh,unilemoh,?y&limafxut,srab,???ub&mah?oj???s!.&delacsne,gro?moc?rep?t&en?opsgolb,?ude?vog??gb639j43us5--nx??t?u!.&c&a?s??en?gro?moc?o&c?g??ro?topsgolb,??v!.ta,a1c--nx??wsa08--nx??h&0ee5a3ld2ckx--nx?4wc3o--nx!.&a&2xyc3o--nx?3j0hc3m--nx?ve4b3c0oc21--nx??id1kzuc3h--nx?l8bxi8ifc21--nx?rb0ef1c21--nx???8&8yvfe--nx?a7maabgm--nx??b!.&gro?moc?ten?ude?vog??mg??c!.&7erauqs,amil4,duolc-drayknil,etisbew321,gniksnd,p&h21,ohsdaerpsym,?sndtog,topsgolb,wolf.e&a.1pla,nigneppa,?xi2,ytic-amil,?aoc?et?ir!euz??r&aes?uhc??sob?taw!s???d0sbgp--nx?f&2lpbgm--nx?k??g!.&gro?lim?moc?ude?vog???m!a1j--nx??ocir?p!.&gro?i?lim?moc?ogn?ten?ude?vog???s!.&g&nabhsah,ro??l&im?xv,?m&oc?roftalp.&cb,su,tne,ue,??pib,ten", + "?vog?won,yolpedew,?a&c?nom??i&d?f?ri???t!.&ca?enilno,im?ni?o&c?g??pohs,ro?ten??iaf!.oby,?laeh!.arh,?orxer?rae??vo!.lopdren,?zb??i&3tupk--nx?7a0oi--nx?a!.&ffo?gro?moc?ten?uwu,?1p--nx?bud?dnuyh?tnihc??b!.&gro?moc?oc?ro?ude??ahduba?o!m!.&duolcsd,ysrab,???s??c!.&ayb-tropora--nx?ca?d&e?m??esserp?gro?ln,moc?nif,o&c?g?ssa??ro?t&en?ni?roporéa??ude?vuog??cug?t??d&dk?ua??e&bhf--nx?piat??f!.&aw5-nenikkh--nx,dnala?i&ki,spak,?mroftalpduolc.if,nenikkäh,pohsdaerpsym,retnecatad.&omed,saap,?topsgolb,uvisitok321,yd,?onas??g!.&d&om?tl??gro?moc?ude?vog???h&c&atih?ra??s&abodoy?ibustim???juohs?k!.&gro?moc?ofni?ten?ude?vog?zib??b4gc--nx?iw!.remarf,?nisleh?s?uzus??l!.&aac,topsgolb,?drahcir?iamsi??maim?n!.&b&ew?og??ca?gro?lim?mo&c?n??ni?o&c?fni??pp?t&en?ni??ude?zib??airpic?i&hgrobmal?m??re??om?rarref?s!.&egaptig,ppatig,topsgolb,?ed??t&i&c?nifni??rahb??ut?v!.&21k?gro?moc?oc?ten???wik?xa&rp?t??yf??j&6pqgza9iabgm--nx?8da1tabbgl--nx?b!.&acirfa?eto?gro?m&oc?siruot??o&c!e??fni?noce?rga?tser??russa?s&etcetihcra?risiol?tacova??t&en?naruatser?opsgolb,?ude?vinu?yenom???d?f!.&ca?eman?gro?lim?moc?o&fni?rp??ten?vog?zib???nj?s?t!.&bew?c&a?in??eman?gro?lim?moc?o&c?g??t&en?ni?set??ude?vog?zib???yqx94qit--nx??k&8uxp3--nx?924tcf--nx?arfel?c&a&bdeef?lb??ebdnul?ilc?reme??d!.&e&disemmejh321,rots,?ger,mrif,oc,pohsdaerpsym,topsgolb,zib,?t??e&es?samet??h!.&a&4ya0cu--nx?5wqmg--nx??b3qa0do--nx?cni,d&2&2svcw--nx?3rvcl--nx??5xq55--nx?tl,?g&a0nt--nx?la0do--nx?ro??i&050qmg--nx?7a0oi--nx?xa0km--nx??m&1qtxm--nx?oc??npqic--nx?saaces,t&en?opsgolb,?ude?v&di?og?ta0cu--nx??xva0fz--nx?人&个?個?箇??司公?府政?絡&網?网??織&組?组??织&組?组??络&網?网??育&敎?教???n??i&tsob?vdnas??l!.&bew?c&a?os??dtl?gro?hcs?letoh?moc?nssa?ogn?prg?t&en?ni??ude?vog??at?cd?is??m!.&eman?fni?gro?moc?t&en?opsgolb,?ude?vog???n&ab!cfdh?etats?mmoc?t&en?fos??u??i!l!.&noyc,pepym,??p???oob?p!.&b&ew?og??gro?kog?m&af?oc??nog?ofni?pog?sog?ten?ude?vog?zib???row!ten!.&htumiza,nolt,o&c,vra,????s!.topsgolb,?t?u!.&c&a?lp??dtl?e&cilop?m??gro!.&gul:g,,sgul,yr&ettoly&lkeew,tiniffa,?tneelffar,???lenap-tnednepedni,n&noc,oissimmoc-&layor,tnednepedni,??o&c!.&bunsorter.tsuc,en&ilnoysrab,ozgniebllew,?krametyb.&hd,mv,?omida,p&i-on,ohsdaerpsym,?t&fihsreyal.j,opsgolb,?vres-hn,ysrab,??rpoc,?psoh,shn?t&en?nmyp,seuqni-tnednepedni,?vog!.&eci&ffoemoh,vres,?ipa,ngiapmac,??weiver-tnednepedni,y&riuqni-&cilbup,tnednepedni,?srab,????l&04sr4w--nx?a!.&gro?lim?moc?t&en?opsgolb,?ude?vog??bolg?c?ed?g!el??i&c&nanif!.oc,lpl??os??romem?tnedurp??n&if?oitanretni??t&i&gid!.sppaduolc:.nodnol,,?p&ac?soh???ned?ot??utum!nretsewhtron???c!.&bog?lim?oc?topsgolb,vog???dil?e&datic?n&ahc?nahc!rehtaew???t!ria?tam??vart??f&8f&pbgo--nx?tbgm--nx??a?n??g!.&gro?moc?oc?ten?ude?xx,zib,??h&d?op??i!.&21k?ca?fdi?gro?inum?oc!.&egapvar,redrotibat,t&ibatym,opsgolb,???ten?vog??a&f?m&e?g?toh???m?r??l&a&b&esab?t&eksab!.&sua,zn,??oof???c?mt??e&d?hs??ihmailliw?j??m!.&esserp?gro?moc?ten?ude?v&og?uog????n!.&etisbew321,no&med,rtsic,?oc,pohsdaerpsym,retsulc-gnitsoh,topsgolb,vog,yalphk,?o??o&a?btuf?l!.gmo,?o&c!.&ed,rotnemele,??hcs??rit?u??p!.&a&cin&diws?gel??d&g,ortso?urawon??i&dem?mraw?nydg,?k&elo&guld?rtso??slopolam?tsu?ytsyrut??l&ip?o&kzs?w&-awolats?oksnok????n&erapohs,img?zcel,?rog&-ai&bab?nelej??j?z??syn?tsaim?w&a&l&eib?i?o??zsraw??o&namil?tainop,??z&eiwolaib?mol???c&e&iw&alselob?o&nsos?rtso???le&im?zrogz???orw,p??d&em,ia?ragrats?uolc&inu,sds,??e&c&i&lrog?w&ilg,o&hc&arats?orp??klop?tak????yzreibok??i&csjuoniws?ksromop?saldop??l&ahdop?opo??napokaz,t&atselaer?iselpmis,?z&romop?swozam???g&alble?ezrbo&lok?nrat??ro??hcyzrblaw?i&csomohcurein?grat?klawus??k&e&rut?walcolw??in&byr?diws,sark,?le?o&nas?tsylaib??rob&el?lam??s&als?jazel?nadg,puls?rowezrp???l&colw?e&r?vart??i&am?m???m&o&c?dar?n?tyb??s&g?iruot??t!a???n&a&gaz?nzop,?i&bul?cezczs?lbul,molow?nok?zd&eb?obeiws???uleiw?y&tzslo?z&rtek?seic????o&c,fni?k&celo?zdolk??lkan?n&leim?pek?t&uk?yzczs??z&copo?eing?rowaj???rga?tua?w&ejarg?ogarm???p&e&eb,lks!emoh,??klwwortso?ohs!-ecremmoce,daerpsym,??romophcaz?sos?t&aiwop?en?opos,ra,sezc??ude?v&irp?og!.&a&io?p?s!w???bni&p?w??ci?dtiw?e&ko?ss&p?w???fiw?g&imu?u??hiiw?m&igu?rio?u!o???nds!ipz??o&ks?p!pu??s?wtsorats??p&a?sp!mk?pk?wk??u&m?p??wk?z??r&hcso?ksw?p?s??s&i?oiw?u?zu??talusnok?w&gzr?i&p?rg?w??m?o&o?pu??u!imzw???z&kw?ouw?????w&a&l&corw?sizdow??w??o&golg?k&ark,ul?zsurp??r&az?gew??t&rabul,sugua??z&coks?sezr????xes?y&buzsak?d&azczseib?ikseb??hcyt?n&jes?lod-zreimizak??pal?r&ogt?uzam??walup?zutrak??z&am-awar?c&aprak?iwol?zsogdyb??dalezc?ib?s&i&lak?p??uklo????l??r&as?f?s??s!.&gro?moc?ten?ude?vog???t!.vog??ubnatsi?x3b689qq6--nx?yc5rb54--nx??m&00tsb3--nx?1qtxm--nx?981rvj--nx?a!.&aayn,enummoc?gro?moc?o&c?idar,ken,?t&en?opsgolb,??c!bew??dretsma?e&rts?t!.&citsalej,esruocsid,???fma?xq--nx??b!.&gro?moc?ten?ude?vog??i??c!.&moc?oc?ten?vog???d!.&gro?moc?ten?ude?vog???f!.&gro?moc?oidar,ten?ude??i??g!vu96d8syzf--nx??h?i!.&ca?gro?moc?o&c!.&clp?dtl???r,?t&en?t??vt??k?rbg4--nx??k!.&drp?e&rianiretev?sserp??gro?lim?m&o&c?n??t??nicedem?ossa?pooc?s&eriaton?neicamrahp?sa??ude?v&og?uog????l&if?ohkcots??o!.&dem?gro?m&oc?uesum??o&c?rp??ten?ude?vog??b?c!.&0x,2aq,3pmevres,5sndd,a&c&-morf,ir&bafno,fa,??g&-morf,oy-sehcaet,?i-morf,m&-morf,all&-a-si,amai,??p&-morf,c-a-si,?r&emacytirucesym,odih,?s,tadtsudgniht,v-morf,w-morf,z,?b&ew&-sndnyd,arukas,draiw.segap,ottad,?ildts.ipa,?c&amytirucesemoh,d-morf,esyrcs,itsalej.omed,n&-morf,vym,?p&kroweht,ytirucesemoh,?q,rievres,s-morf,?d&aerotffuts,e&calpb,ifitrec-&si,ton-si,?llortnocduolc,rewopenignepw:.sj,,tsohecapsppa,?i&-morf,rgevissam.saap,?m-morf,n&-morf,abeht-htiw-si,?s-morf,uolc&-noitatsyalp,hr,iafaw.&d&ej,yr,?nol,?meaeboda,nevia,panqym:-&ahpla,ved,?,smetsystuo,tekcilc,ved&j,pw,??vreser,wetomer,?e&butuoyhtiw,ciffo-sndnyd,d:-morf,o&celgoog,n&il.srebmem,neve.&1-&su,ue,?2-&su,ue,?3-&su,ue,?4-&su,ue,????,erf&-sndnyd,sndd,?filflahevres,g&de-yltsaf,nahcxeevres,?i&hcet-a-si,p-sekil,?k&auqevres,irtsretnuocevres,?l&bitpa-no,googhtiw,?m&agevres,ina-otni-si,oh-&sndnyd,ta-sndnyd,??n&-morf,ilno&-evreser,ysrab,?og-si,?r&alfduolcyrt,ehwynanohtyp:.ue,,ihcec,?srun-a-si,t&i&nuarepo,s&-ybboh,aloy,elpmis,tipohs,xiw,??omer-sndnyd,upmocsma,ysgolb,?v&als-elcibuc-a-si,i&lsndd,tavresnoc-a-si,??z&amkcar,eelg,iig,??fehc-a-si,g&ni&gats-&raeghtua,swennwot,?ksndd,robsikrow,tsoh-bt.etis,?o&fgp,lb&-sndnyd,sihtsetirw,???h&n-morf,o-morf,?i&fiwehtno,h-morf,kiw-sndnyd,m-morf,p&aerocne,detsoh,?r-morf,w-morf,z&ihcppa,nilppa,??jn-morf,k&a&-morf,erfocsic,?cils-si,eeg&-a&-si,si,?sndd,?h,latsnaebcitsale:.&1-&htuos-pa,lartnec-&ac,ue,?ts&ae&-&as,su,?ht&ron-pa,uos-pa,??ew-&su,ue,vog-su,???2-ts&ae&-su,ht&ron-pa,uos-pa,??ew-&su,ue,??3-ts&aehtron-pa,ew-ue,??,o-morf,r&adhtiwtliub,ow&-&sndnyd,ta-sndnyd,?ten-orehkcats,??sedal,u,?l&a&-morf,colottad,rebil-a-si,?f-morf,i&-morf,am&-sndnyd,detsohpw,??l&ecelffaw,uf-ytnuob:.a&hpla,teb,?,?ppmswa,ru-&elpmis,taen,?ssukoreh,xegap,?m&n-morf,pml.ppa,rofe&pyt.orp,rerac-htlaeh,?sacrasevres,uirarret-yltsaf,?n&a&cilbuper-a-si,f&-sllub-a-si,racsan-a-si,?i&cisum-a-si,ratrebil-a-si,?tarukas,?c,dc&hsums,umpw,xirtrepmi,?eerg-a-si,i&-morf,jod,?m-morf,o&ehtnaptog,isam-al-a-tse,r&italik,tap-el-tse,?s&iam-al-a-tse,replausunu,??pj,t-morf,?o&bordym,c,hce-namtsop,jodsnd,m&-morf,ed-baltlow,?n:iloxip,,ttadym,?p&2pevres,aelutym,i&-sndnyd,fles,ogol,ruoy&esol,hctid,?ym&eerf,teg,??ohsdaerpsym,pa&-rettalp,anis:piv,,esaberif,k1,lortnocduolc,oifilauq,r&aegyks,oetem:.ue,,?t&ilmaerts,norfegap,?ukoreh,?t&fevres,thevres,??r&081,a:-morf,tskcor-a-si,,b,e&d&iv&erp-yb-detsoh.saap,orpnwo,?ner&.ppa,no,??e&bevres,nigne-na-si,?ggolb-a-si,h&caet-a-si,pargotohp-a-si,?krow-drah-a-si,n&gised-a-si,ia&rtlanosrep-a-si,tretne-na-si,??p&acsdnal-a-si,eekkoob-a-si,?retac-a-si,subq,tn&ecysrab,iap-a-si,uh-a-si,?vres&-&ki.&cpj-rev-duolcj,duolcj,?s&ndnyd,pvtsaf,??inim,nmad,sak,?y&alp-a-si,wal-a-si,?zilibomdeepsegap,?g,ituob,k,mgrp.nex,o&-morf,sivdalaicnanif-a-si,t&areleccalabolgswa,c&a-na-si,od-a-si,?susaym,??p-morf,u&as-o-nyd,e&tsoh.&duolc-gar,hc-duolc-gar,?ugolb-nom-tse,?omuhevres,??s&a&apod,ila&nyd,snd,?nymsd,vnacremarf,?bbevres,ci&p&-sndnyd,evres,?tcatytiruces,?dylimaf,e&cived-anelab,itilitu3,lahw-eht-sevas,mag-otni-si,t&i&iis,sro,?yskciuq,??fpi-&eralfduolc,fc,?i&ht2tniop,pa&elgoog,tneltneg,??jfac,k&-morf,aerf-ten,colb&egrof,pohsym,??m&-morf,cxolb,?n&d&-pmet,dyard,golb,htiwssem,mood,tog,?kselp,nyd,ootrac-otni-si,?o&-xobeerf,xobeerf,?ppa&-avnac,raeghtua,t&ikria,neg,??r&ac-otni-si,e&ntrap-paelut,tsohmaerd,??s&e&l-rof-slles,rtca-na-si,?ibodym,?tsaeb-cihtym.&a&llicno,zno,?ilay,lacarac,re&gitnef,motsuc,?sv,toleco,x:n&ihps,yl,?,?u,wanozama.&1-&ht&ron-ue.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??uos-&em.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??fa.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??pa&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,??ue.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,????la&nretxe-3s,rtnec-&ac&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,??ue&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,????ts&ae&-&as&-&3s,etisbew-3s,?.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??kcatslaud.3s,??pa.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??su:-etisbew-3s,.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??kcatslaud.3s,yawetag-scitylana,?,?ht&ron-pa&-&3s,etisbew-3s,?.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??kcatslaud.3s,yawetag-scitylana,??uos-pa&-&3s,etisbew-3s,?.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??kcatslaud.3s,????ew-&su&-&3s,etisbew-3s,?.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,???ue&-&3s,etisbew-3s,?.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??kcatslaud.3s,yawetag-scitylana,??vog-su-&3s,spif-3s,????2-ts&ae&-su&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,yawetag-scitylana,??ht&ron-pa&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,??uos-pa&-&3s,etisbew-3s,?.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??kcatslaud.3s,????ew-&su&-&3s,etisbew-3s,?.&9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??yawetag-scitylana,??ue&-3", + "s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,????3&-ts&aehtron-pa.9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??ew-ue&-3s,.&3s,9duolc&-swa.stessa-weivbew,.s&fv,tessa-weivbew,??etisbew-3s,kcatslaud.3s,???s,??yasdrocsid,?t&arcomed-a-si,c&-morf,etedatad.&ecnatsni,omed,??eel&-si,rebu-si,?hgilfhtiwletoh,i:batym,,m-morf,n&atnuocca-na-si,e&duts-a-si,r-ot-ecaps,tnocresu&buhtig,e&capsppa,donil.pi,lbavresbo.citats,?pl,???ops&edoc,golb,ppa,?s&i&hcrana-&a-si,na-si,?laicos-a-si,pareht-a-si,tra-na-si,xetn&od,seod,??oh&piym,sfn,??u&-morf,nyekcoh-asi,?v-morf,?u&-rof-slles,4,a-sppatikria,e,h,oynahtretramssi,r:ug-a-si,,?v&n-morf,rdlf,w-morf,?w&o&lpwons-yrt,zok,?ww100,?x&bsbf.sppa,em,i&nuemoh,rtrepmi,?obaniateb,t-morf,unilemoh,?y&a&bnx:.&2u,lacol-2u,?,l&erottad,pezam,?wetag-llawerif,?dnacsekil,fipohsym,k&-morf,niksisnd,?rot&ceridevitcaym,sitk,?u:goo,,w-morf,x&alagkeeg,orp&hsilbup,mapson.duolc,???zesdrocsid,?inu??m?or?tsla??p!.&eman,nwo,??raf!.jrots,etats??s?t!.&gro?lim?mo&c?n??oc?ten?ude?vog???u&esum?rof??z!.&ca?gro?hcs?lim?moc?o&c?fni??ten?ude?vog?zib????n&315rmi--nx?a&brud?cilbuper?f?grompj?hkaga?idraug?m?ol?ssin?u&hix?qna??varac?yalo??b!.&gro?moc?oc,ten?ude?vog??c??c!.&ah?bh?c&a?s??d&5xq55--nx?g?s?uolctnatsni,?eh?g&la0do--nx?ro??h&a?q?s??i&7a0oi--nx?h??j&b?f?t?x?z??kh?l&h?im?j??m&n?oc!.swanozama.&1-htron-nc.3s,be.1-&htron-nc,tsewhtron-nc,????n&h?l?s?y??om?qc?s&g?j?ppa-avnac,?t&cennockciuq.tcerid,en??ude?vog?wt?x&g?j?n?s??z&g?x??司公?絡網?络网??b??d&g!.ypnc,?ka??e&drag?erg?fuak?gawsklov?hctik?i&libommi?w??m?po?r!ednaalv??sier?ves??g!.&ca?gro?moc?ten?ude?vog??is&ed!.ssb,?irev???h!.&bog?cc,gro?lim?moc?ten?ude???i!.&ac?bew,c&a?in??dni?e&m?sabapus,?g&5?6?p?ro??i&a?hled??ku?l&evart?im??m&a?oc?rif??n&c?eg??o&c?fni?i?rp??p&ooc?u??r&ahib?d?e??s&c?er?nduolc,senisub?u??t&arajug?en!retni??ni?opsgolb,sop??ude?v&og?t??ysrab,zib??elknivlac?griv?ks?lreb?p?v?w?x??k!.&gro?ten?ude?vog???l&eok?ocnil??m!.&cyn,gro?ude?vog???o&dnol?i&hsaf?n&o?utiderc??siv!orue??t&a&cude!.oc,?dnuof?tsyalp??c&etorp?u&a?rtsnoc?????kin?las?mrom?nac?p&q?uoc??s&iam?pe?scire??t&ron?sob??zama??p!.&gro?oc?ten?ude?vog??k??r&e&c?yab??op!.eidni,??s!.&gro?moc?osrep?t&opsgolb,ra??ude?v&inu?uog????t!.&d&ni?uolcegnaro,?gro?ltni?m&oc!nim??siruot??nif?o&fni?srep??sne?t&an?en??vog??m??u&f?r!.&bdnevar,lper,retropno,s&h,revres,?tnempoleved,??stad?xamay?y??v!.&a&lnos?ohhnah&k?t???c&a?ouhphnib?uhphniv??di?e&man?rtneb?uhneihtauht??g&n&a&boac?ig&ah?cab?n&a?ei&k?t???uah??nad?rtcos?uqneyut??o&dmal?hpiah?lhniv?nkad?ud&hnib?iah????ro??h&ni&b&aoh?gnauq?hnin?iaht??d&hnib?man??mihcohohphnaht?n&cab?gnauq?yat??tah?vart??tlaeh??i&a!bney?coal?gngnauq?laig?ngnod??onah?rtgnauq??kalkad?m&an&ah?gnauq??oc?utnok??n&a&ehgn?gnol?kcab?uhthni&b?n???e&ibneid?y&gnuh?u&gniaht?hp????osgnal??o&fni?ht&nac?uhp??i?rp??pahtgnod?t&en?ni?opsgolb,?u&a&hcial?mac?tgnuv-airab??de?eilcab??vog?zib???wo&rc?t!epac????o&76i4orfy--nx?a!.&bp?de?go?oc?ti?vg??boat??b!.&a&ci&sum?tilop??i&c&arcomed?neic??golo&ce?ncet??m&edaca?onoce??rt&ap?sudni??vilob??n&egidni?icidem??serpme?tsiver?vitarepooc??b&ew?og??dulas?e&rbmon?tr&a?op&ed?snart????g&olb?ro??ikiw?l&a&noi&canirulp?seforp??rutan??im??moc?o&fni?lbeup?rga?tneimivom??saiciton?t&askt?en?ni??ude?vt??h?iew?olg??c!.&bew?cer?dr&c,rac,?esabapus,gro?ipym,l&im?per:.di,,?m&o&c!.topsgolb,?n??rif??ofni?s&egap&dael,l,?tra??t&4n,en?ilperdellawerif:.di,,ni??ude?vog??a?e?in?mara?s&edarb?ic???d!.&b&ew?og??dls?gro?lim?moc?t&en?ra??ude?vog??agoba?if?zd7acbgm--nx??e&c?d&iv?or???f!ni!.&e&g&delwonk-fo-l&errab,lerrab,?ellocevoli,?ht-skorg,rom-rof-ereh,tadpusn:d,,?llatiswonk,macrvd,ofni-v,p&i&-on,fles,?ohbew,?ruo-rof,s&iht-skorg,nd&-cimanyd,nyd,uolc,??tsrifyam,ysrab,zmurof,???g&el?n!am?ib???hwsohw?i!.&35nyd,8302,a&minifed,tad-b,?b&altig,uhtig,?czh,d&in,raobelgaeb,u&olc&iaznab.ppa,ropav,?rd,??e&c&apsinu.1rf-duolc,ivedniser,?donppad.sndnyd,egipa,lej,nilnigol,sufxob,t&i&beulb,snoehtnap,?newtu,ybeeb.saap,??gni&gatsniser.secived,tsohytsoh,?ilpu,k&coregrof.di,orgn:.&as,ni,p&a,j,?su,u&a,e,??,ramytefasresworb,?moc?n&aicisum,mtsp:.kcom,,yded,?ot&oq,pyrctfihs,?p&opilol,pa&-arusah,e&nalpkcab,tybeeb.1dkes,???r&e&tsneum-hf,vres&cisab,lautriv,??ial.sppa,?s&codehtdaer,gnihtbew,nemeis-om,pparevelc,t&acdnas,ekcit,??t&e&kcubtib,notorp,?i&belet,detfihs,gude,kecaps,?raedon.egats,s&ohg,udgniht.&cersid.&dvreser,tsuc,?dorp.tsuc,gnitset.&dvreser,tsuc,?ved.&dvreser,tsuc,????vgib.0ku,whs,x&bslprbv.g,cq,rotide,?y&olpedew,srab,??b?d&ar?u&a?ts???j?r?syhp??j!.&eman?gro?hcs?lim?moc?ten?ude?vog???ll&ag?o??m!.&gro?moc?ten?ude?vog??g?il?mi?orp??n!.&a&0&b-ekhgnark--nx?c-iehsrgev--nx?g-lksedlig--nx?k-negnanvk--nx??1&p-nedragy--nx?q-&asierrs--nx?grebsnt--nx?lado-rs--nx?n&egnidl--nx?orf-rs--nx??regnayh--nx?ssofenh--nx??r-datsgrt--nx?s-ladrjts--nx?v-y&senner--nx?vrejks--nx???3g-datsobegh--nx?4&5-&dnaleprj--nx?goksnerl--nx?tednalyh--nx??6-neladnjm--nx?s-&antouvachb--nx?impouvtalm--nx??y-&agrjnevvad--nx?ikhvlaraeb--nx???7k-antouvacchb--nx?8&k-rekie-erv--nx?l-ladrua-rs--nx?m-darehsdrk--nx??a!.sg??bct-eimeuvejsemn--nx?d&do?iisevvad?lov?narts?uas??f&1-&l--nx?s--nx??2-h--nx??g&10aq0-ineve--nx?av?ev?lot?r&ajn&evvad?u??ájn&evvad?u????h?iz-lf--nx?j&ddadab?sel??k&el?hoj&sarak?šárák??iiv&ag&na&el?g??ŋ&ael?ág???ran???l&f?lahrevo?o&ms?s??sennev?t-&ilm--nx?tom--nx??u&-edr--nx?s??øms??muar?n&0-tsr--nx?2-dob--nx?5-&asir--nx?tals--nx??a&r!-i-om?f?t??t??douvsatvid?kiv?m&os?øs??n&od?ød??ra?sen?t&aouvatheig?ouv&a&c&ch&ab?áb??h&ab?áb???n??i&ag?ág??sa&mo?ttvid??án???z-rey--nx?ær&f?t???o&p-&ladr--nx?sens--nx??q-nagv--nx?r-asns--nx?s-kjks--nx?v-murb--nx?w-&anr&f--nx?t--nx??ublk--nx???ppol?q&0-t&baol--nx?soum--nx?veib--nx??x-&ipphl--nx?r&embh--nx?imph--nx???y-tinks--nx??r&f-atsr--nx?g-&an&ms--nx?nd--nx??e&drf--nx?ngs--nx??murs--nx?netl--nx?olmb--nx?sorr--nx??h-&a&lms--nx?yrf--nx??emjt--nx??i&-&lboh--nx?rsir--nx?y&d&ar--nx?na--nx??ksa--nx?lem--nx?r&ul--nx?yd--nx????stu??j-&drav--nx?rolf--nx?sdav--nx??kua?l-&drojf--nx?lares--nx??m-tlohr--nx?n-esans--nx?olf?p-sdnil--nx?s-ladrl--nx?tih?v-rvsyt--nx??s&a&ns?ons??i&ar?er&dron?r&os?øs???ár??la&g?h??mor!t??sir?uf?åns??t&koulo&nka?ŋká??la?p-raddjb--nx?r-agrjnu--nx?s&aefr&ammah?ámmáh??orf?r&o?ø???u-vreiks--nx??u&h-dnusel--nx?i-&drojfk--nx?vleslm--nx??j-ekerom--nx?k-rekrem--nx?u-&dnalr--nx?goksr--nx?sensk--nx??v-nekyr--nx?w-&k&abrd--nx?ivjg--nx??oryso--nx??y-y&dnas--nx?mrak--nx?n&art--nx?nif--nx??reva--nx??z-smort--nx??v!.sg?ledatskork?reiks??wh-antouvn--nx?x&9-dlofts--nx.aoq-relv--nx?d-nmaherk--nx?f-dnalnks--nx?h-neltloh--nx?i-drgeppo--nx?j-gve&gnal--nx?lreb--nx??m-negnilr--nx?n-drojfvk--nx??y&7-ujdaehal--nx?8-antouvig--nx?b-&dlofrs--nx?goksmr--nx?kivryr--nx?retslj--nx??e-nejsom--nx?f-y&krajb--nx?re&dni--nx?tso--nx??stivk--nx??g-regark--nx?orf?ørf??z9-drojfstb--nx??b&25-akiivagael--nx?53ay7-olousech--nx?a&iy-gv--nx?le-tl&b--nx?s--nx??n0-ydr--nx??c&0-dnal-erdns--nx?z-netot-erts--nx??g&g-regnarav-rs--nx?o-nejssendnas--nx??ju-erdils-ertsy--nx?nj-dnalh-goksrua--nx?q&q-ladsmor-go-erm--nx.&ari-yreh--nx?ednas??s-neslahsladrjts--nx???ca&4s-atsaefrmmh--nx?8m-dnusynnrb--nx?il-tl--nx?le-slg--nx?n5-rdib--nx?op-drgl--nx?uw-ynnrb--nx??d&a&qx-tggrv--nx?reh!nnivk?sd&ork?ørk??uas??ts&e&bi?kkar?llyh?nnan??g&ort?ørt??k&alf?irderf??levev?mirg?obeg&ah?æh??r&ah?ejg????barm-jdddb--nx?ie!rah?s&etivk?ladman???lof&r&os?øs??ts&ev.ednas?o.relav?ø.relåv???n&a&l&-erd&n&os?øs??ron??adroh.so?dron.&a&g5-b--nx?ri-yreh--nx??ob?y&oreh?øreh??øb??e&m!lejh??pr&oj?øj??vi??gyb?n&aks?åks??o&h-goksrua?rf??r&o?ua?ø??tros?øh-goksrua??rts!e&devt?lab?mloh???s&ellil?naitsirk?rof???u&l!os??s!d&im?lejt??e&guah?l&a?å???kkoh?lavk?naitsirk?r&af?eg&e?ie???tef?y&onnorb?ønnørb?????r&a&blavs!.sg??g&eppo?la???o&j&f&a!dniv?k?vk??die?e&dnas?kkelf??llins?r&iel?ots??s&lab?t&ab?åb??yt??å!k??ævk??les??ts??åg&eppo?lå???ureksub.sen??e&ayb-yrettn--nx?d&ar?isemmejh321,lom?r&of?øf??år??g&gyr?nats??i&meuv&ejsem&aan?åån??sekaal??rjea??j&d&ef?oks??les??k&er&aom?åom??hgna&ark?årk??iregnir?kot!s??s&ig?uaf???l&bmab?kyb?l&av?ehtats??oh??m&it?ojt?øjt??n&arg?g&os?øs??meh?reil?te?ummok?yrb??r&dils-erts&ev?y&o?ø???ua?vod??sa&ans?åns??t&robraa?spaav??urg??f&62ats-ugsrop--nx?a&10-ujvrekkhr--nx?7k-tajjrv-attm--nx??o!.sg?h??s!.sg??v!.sg???g&5aly-yr&n--nx?v--nx??a&llor?ve&gnal?lreb???n&av!snellu??org??oks&die?m&or?ør??ner&ol?øl??r&o?ø???r&eb!adnar?edyps?s&die?elf?gnok?n&ot?øt????obspras??uahatsla?åve&gnal?lreb???h&0alu-ysm--nx?7&4ay8-akiivagg--nx?5ay7-atkoulok--nx??a!.sg???i&e&hsr&agev?ågev??rf??k&h&avlaraeb?ávlaraeb??s??lm&a?å??mpouvtal&am?ám??pph&al?ál??rrounaddleid?ssaneve?ššáneve??j&0aoq-ysgv--nx?94bawh-akhojrk--nx??k&a&b&ord?ørd??jks?lleis??iv!aklejps?l&am?evs?u??mag?nel?ojg?r&a&l?n??epok?iel?y&or?ør???s&ah?kel?om??øjg??kabene?ojsarak?ram&deh.&aoq-relv--nx?rel&av?åv??so??e&let.&ag5-b--nx?ob?øb??ra???åjks??l&a!d&anrus?d&numurb?ron??e&gnard?nte?s&meh?sin??ttin??g&is?nyl??kro?l&em?l&ejfttah?of??u&ag-ertdim?s???n&am?era?gos?i&b?nroh?r??kos?nus?oj??o-&dron?r&os?øs???ppo?r&a!l?nram??e&gne?l?v??is?o&jts?ts??u&a-&dron?r&os?øs???h??å?æl?øjts??s&e&jg?nivk?ryf??kav?mor-go-er&om.&ednas?yoreh??øm.&ednas?yøreh???uag??t&las?rajh?suan??v&l&a?e-rots??u-go-eron??yt??ksedlig?res&a?å???bib&eklof?seklyf??es!dah??h!.sg??i&m?syrt??l&ejf?ov&etsua?gnit?ksa?sdie???n!.sg??o!.sg?boh?g?h??r!.sg??å!ksedlig??øboh??m&a&rah?vk??f!.sg??h!.sg??i&e&h&dnort?rtsua?ssej??rkrejb??ksa??ol?t!.sg??u&dom?esum?r&ab?drejg?evle?os?uh?æb?øs??ttals???n&a&g&av?okssman?åv??jlis?or?r&g?rev???e&d&do&sen?ton??lah?r&agy&o?ø??ojfsam???g&iets?n&a&l&as?lab??n&avk?ævk??t&arg?ddosen??v&al?essov???i&d&ol?øl??l&ar?ær???yl??reb??iks?k&srot?y&or?ør???l&a&d&gnos?n&er?ojm?øjm??om??tloh??ug?åtloh??mmard?ojs&om?sendnas??ppolg?s&lahsladr&ojts?øjts??o??t&o&l?t-erts&ev?o?ø???roh?øl??vly&kkys?nav??yam-naj!.sg??øjs&om?sendnas???g&orf?ujb??i&dnaort?vnarg??kob?ladendua?maherk&a?å??n&it?urgsrop??orf-&dron?r&os?øs???r&aieb?evats??sfev?uaks?yrts??o&6axi-ygvtsev--nx?c,d&ob?rav??ievs?kssouf?l&m&ob?øb??ous&adna?ech&ac?áč???so!.sg???msdeks?niekotuak?r&egark?olf?y&oso?øso???s&dav?mort???p&ed?ohsdaerpsym,p&akdron?elk???r&a&d&dj&ab?áb??iab??jtif?luag?mah?vsyt??", + "e&gn&a&k&iel?ro??merb?n&at?mas??rav-r&os?øs??srop?talf?v&ats?el??y&oh?øh???ivsgnok??il?jkniets?k&a&nvej?rem?s&gnir?nellu???ie-er&den?v&o?ø???ram?sa?årem??la&jf?vh??m&b&ah?áh??mahellil??nnul?ts&l&oj?øj??ul??y&o?ø???imp&ah?áh??m!.sg??osir?t!.sg??ádiáb?ævsyt?øsir??s&adnil?en&dnas?e&dga?k&ri&b?k??som??ve??me&h?jg??nroh-go-ejve?s&a?ednil?k&o?ø??of?yt?å??tsev??gv?hf?igaval?o&r&or?ør??sman??so&fen&oh?øh??m?v??uh&lem?sreka.sen??å!dnil???t&a&baol?g&aov?grav??jjr&av-attam?áv-attám??l&a&b?s??ás??soum?ts?v&eib?our???e&dnaly&oh?øh??f?s&nyt?rokomsdeks?sen??vtpiks??in&aks?áks??loh&ar?år??n!.sg??o&m&a?å??psgolb,?s!.sg?efremmah?or?ør??terdi?á&baol?ggráv?lá&b?s??soum?veib???u&b!.sg?alk?e&dna?gnir?nner??les?ælk??dra&b?eb??g&nasrop?vi?ŋásrop??j&daehal&a?á??jedub?v&arekkhar?árekkhár???ksiouf?n&diaegadvoug?taed???v&irp?lesl&am?åm???y&b&essen?nart?sebel?tsev??o&d&ar?na!s??or??gavtsev?k&rajb?sa??lem?mrak?n&art?n&if?orb???r&a&mah?n?v??e&dni?t&so?ton??va??ul?yd??s&am?enner?gav?lrak?tivk??vrejks??ø&d&ar?na!s??ør??gåvtsev?k&rajb?sa??lem?mrak?n&art?n&if?ørb???r&e&dni?t&so?tøn??va??ul?yd?æ&n?v???s&enner?gåv?tivk?åm??vrejks???á&slág?tlá?vreiks??å&gåv?h?jddådåb?lf??ø&d&ob?rav??r&egark?olf??s&dav?mort????aki?i&sac?tal??u??o&b?f?g?hay?o?ttat??r!.&cer?erots?gro?m&o&c?n??rif?t??o&c,fni??pohs,stra?t&n?opsgolb,?www?ysrab,?e&a!.&a&ac?cgd?idem??bulc!orea??ci&ffartria?taborea??e&cn&a&l&lievrus-ria?ubma??netniam?rusni??erefnoc??gnahcxe?mordorea?ni&gne?lria?zagam??rawtfos??gni&d&art?ilg!arap?gnah???l&dnahdnuorg?ledom??noollab?retac?sael?t&lusnoc?uhcarap??vidyks??hcraeser?l&anruoj?euf?icnuoc?ortnoc!-ciffart-ria???n&gised?oi&nu?t&a&cifitrec?ercer?gi&tsevni-tnedicca?van??i&cossa!-regnessap??valivic??redef??cudorp?neverp-tnedicca????ograc?p&ihsnoipmahc?uorg!gnikrow???r&e&dart?enigne?korb?niart?trahc??o&htua?tacude???s&citsigol?e&civres?r??krow?serp!xe??tnega??t&farcr&ia?otor??hgil&f?orcim??liubemoh?n&atlusnoc?e&duts?m&esuma?n&iatretne?revog??piuqe????olip?ropria?si&lanruoj?tneics???w&erc?ohs??y&cnegreme?dobper?tefas????rref?z??p!.&a&aa?ca?pc??dem?ecartsnd.icb,gne?r&ab?uj??snduolc,t&acova?cca?hcer??wal?ysrab,???s!.&em?gro?hcs,moc?ten?ude?vog???t!.&0x,116,ayo,gro?lim?moc?nayn,sulpnpv,t&cennockciuq.tcerid,en??ude?v&dr,og???o&hp?m?v?yk??tol?ua??v&iv?lov??xas?ykot??p&a&ehc?g?m?s??eej?g!.&gro?ibom?moc?ossa?ppa,ten?ude???i&r!.nalc,?v?z??j!.&0o0o,a&3&5xq6f--nx?xqi0ostn--nx??5wtb6--nx?85uwuu--nx?9xtlk--nx?ad,b&ats,ihc!.&a&bihciakoy?don?ma&him?ye&ragan?tat???r&a&bom?gan?hihci??u&agedos?kas?ustak???s&os?ufomihs??t&amihcay?iran??w&a&g&im&anah?o??omak??kihci?zustum??ihsak??y&agamak?imonihci???e&akas?nagot??i&azni?esohc?h&asa?s&abanuf?ohc???ka&to?zok??musi?orihs?r&akihabihsokoy?o&dim?tak??ukujuk??usihs??nano&hc?yk??o&d&iakustoy?ustam??hsonhot?k&a&rihs?t??iba??nihsaran?sobimanim?tas&arihsimao?imot??uhc?yihcay??u&kujno?s&ayaru?t&imik?tuf???zarasik?????c&cah,ed,?g&as!.&a&gas?m&a&tamah?yik??ihsak??rat?t&a&gatik?hatik??ira!ihsin????e&kaira?nimimak??i&akneg?g&aruyk?o??h&c&amo?uo??siorihs??kaznak?modukuf?ra&gonihsoy?mi???nezih?u&k&at?ohuok??s&ot?tarak?????ihs!.&a&kok?m&a&hagan?yirom??ihsakat??rabiam?wagoton??e&miharot?nokih??houyr?i&azaihsin?esok?kustakat?moihsagih??na&mihcahimo?nok??o&hsia?mag?t&asoyot?ok?tir???us&ay?t&asuk?o??????k&aso!.&a&d&awihsik?eki??k&a&noyot?s&akaayahihc?oihsagih???oadat?uziak??m&ayas!akaso??odak??r&a&bustam?wihsak??ediijuf??t&akarih?i&k?us???wag&ayen?odoyihsagih???e&son?tawanojihs??honim?i&akas?h&cugirom?s&ayabadnot?i&a&kat?t??n??oyimusihsagih???k&a&rabi?sim??ustakat??muzi?r&ijat?otamuk???nan&ak?n&ah?es???o&ay?n&a&ganihcawak?simuzi?tak??eba?ikibah?oyot??t&anim?iad?omamihs??uhc??ust&oimuzi?tes????ou&kuf!.&a&d&amay?eos??g&no?ok?usak??hiku?k&awayim?uzii??ma&kan?y&asih?im???rawak?t&a&gon?ka&h?num?t???umo??wa&g&a&kan?nay?t??ias??ko!rih???y&ihsa?usak???e&m&ay?uruk??taruk?us??i&a&nohs?raihcat??goruk?h&cukuf?s&a&gih?hukuy??in???k&a&gako?muzim??iust?o?ustani??m&anim?otihsoynihs?u??r&ogo?ugasas??usu??ne&siek?zu&b?kihc???o&gukihc?h&ak?ot?ukihc??j&ono?ukihc??kayim?nihsukihc?to?uhc??u&fiazad?gnihs?stoyot????zihs!.&a&bmetog?d&amihs?eijuf?ihsoy?omihs??kouzihs?mihsim?ra&biah?honikam??tawi?wa&g&ekak?ukik??kijuf??yimonijuf??i&a&ra?sok??hcamirom?juf?kaz&eamo?ustam??ma&nnak?ta??nukonuzi?orukuf??nohenawak?o&nosus?ti??u&stamamah?z&a&mun?wak??i!ay?i&hs&agih?in??manim??mihs????????m&a&tias!.&a&d&ihsoy?ot?usah??k&a&dih?sa??o&arihs?s???m&a&tias?y&as?o&rom?tah??ustamihsagih???i&hsagurust?jawak??uri??ni?wa&g&e&ko?man??ikot?o??k&ara?i&hsoy?mak???ru?zorokot??y&a&g&amuk?ihsok?otah??kuf??imo??ziin??e&bakusak?ogawak?sogo?ttas?zokoy??i&baraw?h&cugawak?s&oyim?ubustam???iroy?k&ato?ihs?u&k?stawi???m&akoyr?i&hsoy?juf??uziimak???naznar?o&dakas?ihsay?jnoh?n&a&go?nim??imijuf?nah?oy??r&ihsayim?otagan??t&asim!ak??igus?omatik??zak??u&bihcihc!ihsagih??sonuok?ynah????y&ak&aw!.&a&d&ira?notimak??kadih?ma&h&arihs?im??y&a&kaw?tik??oduk???ru&ustakihcan?y??sauy?wa&g&a&dira?zok??orih??konik??yok?zok??e&banat?dawi??i&garustak?jiat?mani??naniak?o&bog?nimik?t&asim?omihs&ah?uk????ugnihs???o!.&a&jos?koasak?m&ay&ako?ust??ihsayah??r&abi?ukawaihsin??wi&aka?nam???e&gakay?kaw??i&gan?h&cu&kasa?otes??sahakat??k&asim?ihsaruk??miin??n&anemuk?ezib??o&hsotas?jnihs?n&amat?imagak??ohs?uhcibik?????ot!.&a&damay?got?koakat?may&etat?ot??nahoj?riat?waki&inakan?reman???eb&ayo?oruk??i&h&asa?ciimak?sahanuf??kuzanu?m&an&i?ot??ih???nezuyn?otnan?u&hcuf?stimukuf?z&imi?ou???????ihs&o&gak!.&a&m&ayuok?ihsogak??si?yonak??e&banawak?n&at&akan?imanim??uka??tomoonihsin??i&adnesamustas?k&azarukam?oih??m&ama?uzi??usuy??nesi?o&knik?os?tomustam??uzimurat???rih!.&a&ka&n?s??m&ayukuf?i&hsorihihsagih?j&ate?imakikaso????r&a&bohs?h&ekat?im???es??tiak?wiad??e&kato?ruk??i&h&ci&akustah?mono?nihs??s&inares?oyim???manimasa?uk??negokikesnij?o&gnoh?namuk??uhcuf????uk&ot!.&a&bihci?mi&hsu&kot?stamok??m??wagakan??egihsustam?i&gum?h&coganas?soyim??kijaw?m&anim?uzia??ukihsihs??nan&a?iak??o&nati?turan????uf!.&a&batuf?m&a&to?y&enak?irok???ihs&im?ukuf??os?uko??r&aboihsatik?uganat??ta&katik?mawak?rih??w&a&g&akus?emas?uy??k&a&mat?rihs?sa??ihsi??nah??ohs???e&gnabuzia?iman?ta&d?tii???i&adnab?enet?hs&agih?iimagak??k&a&wi?zimuzi??ubay??minuk?r&ook?ustamay???nihsiat?o&g&etomo?ihsin?nan?omihs??no!duruf?rih??rihsawani?ta&may?simuzia???u&rahim?stamakawuzia?zia&ihsin?nay???????nug!.&a&bawak?doyihc?k&anna?oi&hsoy?juf?mot???m&ayakat?ustagaihsagih??n&ihsatak?nak??r&ahonagan?nak?o?u&kati?mamat???t&amun?inomihs?o??w&akubihs?iem?ohs???i&hsa&beam?yabetat??kas&akat?esi??m&akanim?uzio??ogamust?rodim??o&jonakan?n&eu?oyikust??tnihs??u&komnan?stasuk?yrik????rep,?n&ibmab,nog,ob,?ppacihc,ra&n!.&a&bihsak?d&akatotamay?u!o???guraki?m&ay&atik&imak?omihs??irokotamay??oki??ra&hihsak?n??wa&geson?knet???e&kayim?ozamay?sog?ustim??i&a&rukas?wak??garustak?h&ciomihs?sinawak??jo?ka&mnak?toruk??makawak?nos?r&net?otakat?ugeh???o&d&na?oyo??gnas?jnihs?nihsoy!ihsagih??tomarawat?yrok????rikik,?t&ag&amay!.&a&dihsio?k&atarihs?ourust??may&a&kan?rum??enak?onimak??rukho?ta&ga&may?nuf??hakat?kas??wa&g&ekas?orumam??ki&hsin?m??z&anabo?enoy?ot???zuy??e&agas?bonamay?dii?nihsagih?o??i&a&gan?nohs??h&asa?sinawak??nugo??o&dnet?jnihs?ynan??ukohak???iin!.&a&ga?k&ium?oagan??munou!imanim??t&a&bihs?giin??ioy??w&a&gioti?kikes?zuy??irak??yijo??e&kustim?mabust??i&aniat?hcamakot?kaz&awihsak?omuzi??m&a&gat?karum??o???n&anust?esog??o&das?ihcot?jnas?k&ihay?oym??mak?naga?ries??u&ories?steoj?????i&k&a!.&a&go?k&asok?oimak??t&ago!rihcah??ika!atik???w&aki?oyk???e&mojog?natim?suranihsagih?t&ado?okoy???i&hsoyirom?magatak?naokimak??nesiad?o&hakin?jnoh!iruy??nuzak?rihson?tasi&juf?m??yjnoh??u&kobmes?oppah????in,?o!.&a&dakatognub?m&asah?ihsemih??su?t&ekat?i&h?o????e&onokok?ustimak??i&jih?k&asinuk?ias?usu??mukust??onoognub?u&fuy?juk?ppeb?suk?????nayn,?wa&ga&k!.&a&mihsoan?rihotok?waga&kihsagih?ya???emaguram?i&j&nonak?ustnez??kunas?monihcu??o&hsonot?nnam?yotim??u&st&amakat?odat??zatu????nak!.&a&dustam?kus&okoy?tarih??maz?nibe?r&a&gihsaimanim?h&esi?imagas??wa&do?guy???u&im?kamak???tikamay?wa&k&ia?oyik?umas??sijuf??yimonin??e&nokah?saya??i&akan?esiak?gusta?hsuz?kasagihc?o?ukust??o&nadah?sio?tamay?????kihsi!.&a&danihcu?gak?kihs?mijaw?t&abust?ikawak??wazanak??i&gurust?hcionon?mon?ukah??nasukah?o&anan?ton!akan???u&kohak?stamok?z&imana?us?????niko!.&a&han?m&arat?ijemuk?uru??n&e&dak?zi??no??ra&hihsin?rih??wa&kihsi?niko??yehi?zonig??e&osaru?seay??i&hsagih?jomihs?k&a&gihsi?not??ihsakot??m&a&ginuk?kihsug?maz??igo?otekat??nuga!noy???n&a&moti?timoy?wonig??i&jikan?k???o&gan?jnan?tiad&atik?imanim???u&botom?kusug&akan!atik??imot??rab&anoy?eah??????yp,zomim,?bus,c&204ugv--nx?462a0t7--nx?678z7vq5d--nx?94ptr5--nx?a?mpopilol,?d&-2,17sql1--nx?3thr--nx?5&20xbz--nx?40sj5--nx??7&87tlk--nx?ptlk--nx??861ti4--nx?a?e!tfarcdnah,?n&eirf&lrig,yob,?om,?ooftac,?e&16thr--nx?5&1a4m2--nx?9ny7k--nx??damydaer,eweep,garotsarukas.&10ksi.3s,20ksi.3s,?i&bmoz,m!.&a&bot?k&asustam?uzus??m&a&him?y&emak?im???ihs??nawuk?wi&em?k???e&bani?ogawak?si!imanim???i&arataw?gusim?h&asa?ciakkoy??k&a&mat?sosik?t??iat??raban??o&dat?hik?n&amuk?ihseru?o&du?mok????ust????kilbew,lasrepus,mihe!.&a&m&a&h&ataway?iin??yustam??ij&awu?imak???taki!man???ebot?i&anoh?kasam?rabami??n&ania?egokamuk?oot??o&jias?kihcu?nustam?uhcukokihs?yi!es???u&kohik?zo????n!.&arukas,lapo,n&erukom,riheg,?omomus,stnim,teniesa.resu,xob-liam,yrovi,zapot,?amihs!.&a&d&amah?ho?usam??kustay?m&a?ihsoni&hsin?ko???wakih??e&namihs?ustam??i&g&aka?usay??konikak?mikih??nannu?o&mu&kay?zi!ihsagih?uko???nawust?tasim??u&stog?yamat????nep,?rotsnoihsaf,srev,t&awi!.&a&bahay?d&amay?on??koirom?t&a&honat?katnezukir??imus??w&as&ijuf?uzim??ihs???e&hon&i&hci?n??uk??tawi??i&a&duf?murak?wak??h&custo?si&amak?ukuzihs???j&oboj?uk??k&a&m&anah?uzuk??sagenak??esonihci??m&akatik?uzia&rih?wi????o&kayim?no&rih?t??tanufo??uhso???isarap,saman,tococ,?ulbybab,?g&3zsiu--nx?71qstn--nx?l?olblooc,?h&03pv23--nx?13ynr--nx?22tsiu--nx?61qqle--nx?o-hu,sulb,?i&54urkm--nx?azosbew,ced,g&ayim!.&a&dukak?m&a&goihs?kihs??ihsustam!ihsagih??unawi??r&awago?iho??ta&bihs?rum??w&a&gano?kuruf??iat??y&imot?ukaw???e&mot?nimes??i&hsiorihs?ka&monihsi?s&awak?o???mak?r&ataw?o&muram?tan????o&az?jagat?t&asim?", + "omamay???u&fir?k&irnasimanim?uhsakihcihs?????ihcot!.&a&g&a&h?kihsa??ust??kom?m&ay&o?usarak??unak??r&a&boihsusan?watho??iho?ukas??t&akihsin?iay??wa&konimak?zenakat??y&imonustu?oihs???e&iiju?kustomihs?nufawi??i&akihci?g&etom?ihcot?on???o&k&ihsam?kin??nas?sioruk?tab??u&bim?san?????h&c&ia!.&a&dnah?m&a!h&akat?im??yuni??ihs&ibot?ust???r&a&hat?tihs??ik?u&ihsagih?kawi???t&ihc?o&k?yot???wa&koyot?zani??yi&monihci?rak???e&inak?k&aoyot?usa??manokot?noyot??i&a&gusak?kot?sia??eot?h&asairawo?cugo?s&ahoyot?oyim???k&a&mok?zako??ihssi??motay?rogamag??n&an&ikeh?ok??ihssin??o&got?ihsin?jna?rihsnihs?suf?tes??u&bo?raho?s&oyik?takihs??yrihc?zah????ok!.&a&dusay?kadih?mayotom?r&ah&im?usuy??umakan??sot!ihsin??wa&g&atik?odoyin??k&as?o????i&esieg?hco!k??jamu?k&a!sus??usto??ma&gak?k??rahan??o&mukus?n&i?ust!ihsagih???torum?yot!o???u&koknan?zimihsasot????ugamay!.&a&m&ayukot?ihso??toyot??e&bu?subat??i&gah?kesonomihs?nukawi?rakih??nanuhs?otagan?u&ba?foh?otim?stamaduk?uy?????s&anamay!.&a&dihsoyijuf?mayabat?r&ahoneu?ustakihsin??w&a&k&ayah?ijuf??suran??ohs???egusok?i&ak?h&cimakan?s&anamay?od???k&asarin?u&feuf?sto????o&k&akanamay?ihcugawakijuf??nihso?t&asimawakihci?ukoh??uhc??spla-imanim?u&b&nan?onim??fok?hsok?rust????ubon,??ix,ka&rabi!.&a&bukust?gok?kan!ihcatih??m&a&sak?timo?wi??ihsak?ustomihs??ni?r&a&hihcu?way??u&agimusak?ihcust???t&ag&amay?eman??oihcatih??w&ag&arukas?o??os??yi&moihcatih?rom???e&bomot?dirot?not?tadomihs??i&a&k&as?ot??rao??esukihc?gahakat?h&asa?catih??k&a&rabi?saguyr??ihsani?uy??ma?rukustamat??o&dnab?giad?him?kati?rihsijuf?soj?t&asorihs?im??yihcay??u&fius?kihsu?simak????sagan!.&a&m&abo?ihsust??natawak?r&abamihs?u&mo?ustam???wijihc?yahasi??i&akias?hies?k&asagan?i??masah??neznu?o&besas?darih?t&eso?og!imaknihs????ust&igot?onihcuk?uf????zayim!.&a&biihs?guyh?k&oebon?ustorom??mihsuk?r&emihsin?uatik??ta&katik?mim??wag&atik?odak??ya??e&banakat?sakog??i&hsayabok?kaza&kat?yim??m&animawak?ot&inuk?nihs????nanihcin?o&j&ik?onokayim??n&ibe?ust??tias??urahakat????ro&cep,moa!.&a&dawot?turust?wasim??e&hon&ihc&ah?ihs??nas?og?ukor??sario??i&anarih?ganayati?hsioruk?jehon?kasorih?makihsah?nawo?r&amodakan?omoa???o&gnihs?kkat??u&ragust?stum????ttot!.&a&r&ahawak?uotok??sa&kaw?sim???egok?irottot?nanihcin?o&ganoy?nih?tanimiakas??u&bnan?z&ay?ihc??????ukuf!.&a&deki?gurust?ma&bo?h&akat?im??yustak??sakaw??eabas?i&akas?ho?jiehie?ukuf??nezihce!imanim??ono????k&26rtl8--nx?4&3qtr5--nx?ytjd--nx??522tin--nx?797ti4--nx?ci&gid,ht,sevol,?ee,limybab,n&at,upatilol,??l&33ussp--nx?e&ccabew.&resu,sr,?llarap,?lik,oof,rigetuc,?m&11tqqq--nx?41s3c--nx?ef,sioge,?n&30sql1--nx?65zqhe--nx?a&ebyllej,i&lognom,viv,??iam,n7p7qrt0--nx?o&o&las,mflah,?ruk,staw,??o&131rot--nx?7qrbk--nx?aic,c?d&iakkoh!.&a&deki?gakihset?hcebihs?k&adih?u&fib?narihs???m&ayiruk?hot?ihs&orihatik?ukuf??oras?usta??r&ib&a!ka??o?uruf??ozo?u&gakihsagih?oyot???sakim?ta&gikust?mun??w&a&ga&k&an?uf??nus!imak???k&aru?i&h&asa?sagih??kat?mak??omihs?um??zimawi??ine?oyk??yot??e&a&mustam?nan??b&a&kihs?yak??o&noroh?to???ian?k&ihsam?ufoto??nakami?ppoko!ihsin??sotihc?tad!okah??uonikat??i&a&bib?mokamot?n&a&k&kaw?oroh??wi??eomak?ihsatu?okik?usta&moruk?sakan????eib?h&c&ioy?u&bmek?irihs???s&ase?ekka?oknar?uesom???jufirihsir?k&amamihs?i&at?n???m&atik?otoyot??oa&kihs?rihs??r&a&hs?kihsi?mot??ihs&aba?ir??otarib???n&a&hctuk?rorum?se?tokahs??uber??o&kayot?m&ire?ukay??naruf!ima&k?nim???orih?r&ih&ibo?suk??o&bah?h&i&b?hsimak??sa??pnan?yan??umen??t&asoyik?eko?ukoh???u&bassa?kotnihs?m&assaw?uo??pp&akiin?en&ioto?nuk??ip??rato?s&akat?t&eb&e?i&a?hs!a??robon??m&e?o&m?takan???no&h?tamah??o&mik?s?t??u&kir?ppihc?st???onihsnihs?ufuras??uaru??yru!koh??zimihs!ok?????nu,?g!iti,oyh!.&a&bmat?dnas?gusak?k&at?o&oyot?y??uzarakat??m&ayasas?irah??wa&g&ani?okak??k&i&hci?mak??oy???yi&hsa?monihsin???i&asak?hs&aka?i&at?nawak???j&awa!imanim??emih??k&a&goa?s&agama?ukuf??wihsin??i&hsog?m???mati?oia?rogimak??n&annas?esnonihs??o&gasa!kat??ka?n&ikat?o?ustat??rihsay?sihs?tomus?yas??u&bay?gnihs?????hih,konip,l&bs,ik,?mol,nagan!.&a&bukah?d&a&w?yim??e&ki?u??ii??k&a&s&ay?uki??zus??ihsoo?ousay??m&ay&akat?ii??i&hsukufosik?jii??ukihc??n&i!hsetat??uzii??r&ah?ugot??saim?t&agamay?oyim??w&a&g&a&kan?n??o??kustam?ziurak??onim!imanim??u&koo?s!omihs????ya&ko?rih???e&akas?nagamok?subo??i&gakat?h&asa?c&a!mo!nanihs???uonamay??sukagot??k&a&kas?mimanim?to??ia&atik?imanim??oa?uzihcom??m&akawak?ijuf?o!t???r&ato?ijoihs?omakat???n&ana?esnoawazon??o&hukas?n&a&gan?kan??i&hc?muza??ustat??romok?si&gan?k??tomustam??u&k&as?ohukihc??stamega????o&b,m,pac,?to&mamuk!.&a&gamay?rahihsin?sukama!imak??tamanim??enufim?i&hcukik?k&ihsam?u??nugo!imanim??romakat??o&ara?rihsustay?sa?t&amay?om&amuk?us??u!koyg???yohc??u&sagan?zo????yk!.&a&bmatoyk?k&ies?oemak?uzaw??mayi&h&cukuf?sagih??muk??nihsamay?rawatiju?t&away?ik???e&ba&nat!oyk??ya??di?ni??i&ju?kazamayo?manim??natnan?o&gnatoyk?kum?mak?rihsamayimanim?y&gakan?ka&koagan?s??oj???u&ruziam?z&ayim?ik??????wtc1--nx?ykot!.&a&d&i&hcam?mus??oyihc??k&atim?ihsustak??m&a&t!uko??yarumihsa&gih?sum???i&hs&agoa?ika?o!t??uzuok??ren???r&a&honih?wasago??iadok?umah??ssuf?t&ik?o??wa&g&anihs?ode??k&ara?ihcat???y&agates?ubihs???e&amok?donih?m&o?urukihsagih??soyik??i&enagok?gani?h&ca&da?tinuk??sabati??j&nubukok?oihcah??manigus??o&huzim?jihcah?n&akan?ih!sasum??urika??rugem?t&a&mayihsagih?nim??iat?ok??uhc?yknub??u&fohc?hcuf?kujnihs?????p&a&ehc,rc,?o&hs&eht,iiawak,yub,?lf,p&evol,ydnac,?rd&kcab,niar,???r&2xro6--nx?atselttil,e&d&nu,wohc,?h,ilf,pp&ep,irts,u,?t&aerg,tib,??g!r,?ks,o!on,?ufekaf,?s&9nvfe--nx?dom,p&ihc,oo,?remagten,sikhcnerf,u&bloohcs,ruci,srev,?xvp4--nx??t&a&cyssup,obgip,?e&rces,vlev,?hginyad,netnocresu,opsgolb,sidas,u&b,ollihc,??u&4rvp8--nx?fig!.&a&d&eki?ih??kimot?m&ayakat?ihsah??ne?raha&gi&kes?makak??sak??taga&may?tik??wa&g&ibi?ustakan??karihs!ihsagih????e&katim?uawak??i&gohakas?hc&apna?uonaw??k&ago?es?ot??m&anuzim?ijat??nak?urat??nanig?o&dog?jug?makonim?nim?roy?sihcih??u&fig?s&otom?t&amasak?oay??????hc,pup,stoknot,ynup,?wonsetihw,x&5ytlk--nx?irtam,?y&adynnus,dr,knarc,l&oh,rig,?moolg,ob,pp&ih,olf,?rgn&a,uh,?u6d27srjd--nx?vaeh,?z&72thr--nx?e&ej,lur,??井福?京東?分大?取鳥?口山?城&宮?茨??媛愛?山&富?岡?歌和??岡&福?静??島&児鹿?広?徳?福??崎&宮?長??川&奈神?石?香??庫兵?形山?手岩?木栃?本熊?根島?梨山?森青?潟新?玉埼?田秋?知&愛?高??縄沖?良奈?葉千?賀&佐?滋??道海北?都京?重三?野長?阜岐?阪大?馬群???k!.&art?gro?moc?per?ude?vog???l&eh?l??m!.uj,ac?j??nd?o&g?h&pih?s!.&esab,xilpoh,ysrab,???lnud?oc?t!.&lldtn,snd-won,???pa!.&0mroftalp,a&rusah,ted,?bew:erif,,e&erf-korgn,gatskrelc,kalfwons:.kniletavirp,,niln&igol,okoob,?tupmocegde,virdhsalfno,?ilressem,k&orgn,relc,?le&crev,napysae,?maerdepyt,n&aecolatigidno,ur:.a,,?poon,r&cne,emarf,?t&i&belet,lmaerts,?xenw,?yfilten,??ra&a?hs??u&ekam?llag?org!.esruocsid,cts?kouk?nayalo???vsr?xece4ibgm--nx??q&a!3a9y--nx??g?i!.&gro?lim?moc?ten?ude?vog???m?se??r&a!.&a&cisum?sanes??bog?gro?l&autum?im??moc!.topsgolb,?pooc?rut?t&e&b?n??ni??ude?vog??4d5a4prebgm--nx?b?c?eydoog?los?t&at?s!uen???ugaj??b!.&21g?a&b&a&coros?iuc??itiruc??cnogoas?dicerapa?gniram?i&naiog?ramatnas??n&erom?irdnol??op?p&acam?irolf?ma&j?s???rief?tsivaob??b!aj?ib?mi?sb??c&ba?e&r?t??js?sp?t!e???d&em?mb?n&f?i??rt??e&dnarganipmac?ficer?ht?llivnioj?rdnaotnas??f&dj?ed?gg?n&e?i???g&e&l!.&a&b,m,p,?bp,c&a,s,?e&c,p,s,?fd,gm,ip,jr,la,ma,nr,o&g,r,t,?p&a,s,?r&p,r,?s&e,m,r,?tm,??s??l&s?z??n&c?e?o??ol!b?f?v??pp?ro??hvp?i&du?kiw?nana?oretin?r&c?eurab??sp?te?xat??l&at&an?rof??el?im?sq??m&a?da?e&gatnoc?leb??f?ic?oc!.&duolclautriv.elacs.sresu,etiselpmis,topsgolb,???nce?o&ariebir?c&e?narboir?saso??d&o?ranreboas??e&g?t??i&b?dar?ecam?r??rp?t&a?erpoir???p&er?m!e?t??ooc?pa?se??qra?r&af?ga?o&davlas?j??tn?ut??s&a&ixac?mlap?nipmac??ed?u&anam?j?m???t&am?e&d?n?v??nc?o&f?n??ra?sf??u&caug9?de?ja?rg??v&da?ed?og!.&a&b?m?p??bp?c&a?s??e&c?p?s??fd?gm?ip?jr?la?ma?nr?o&g?r?t??p&a?s??r&p?r??s&e?m?r??tm???rs?t??xiv?z&hb?ls?o&c?f?????c!.&as?ca?de?if?o&c?g??ro???e&bew?ccos?dnik?e&b?n&igne?oip??rac??gni&arg?rheob??h&cor?sok?t&aew?orb???itnorf?k&col?o&p?rb???l&aed?ffeahcs??mal?nes?pinuj?t&a&eht?rebsnegömrev??law?nec?s&acnal?nom?ubkcolb??upmoc??v&o&csid?rdnal??resbo??wulksretlow?ywal?zifp??f!.&aterg?bew&-no,etis321,?drp?e&c&itsuj-reissiuh?narf-ne-setsitned-sneigrurihc,?lipuog,rianiretev??hny,i&cc?rgabmahc??m&o&c?n??t??n&eicamrahp?icedem??ossa?pohsdaerpsym,s&e&lbatpmoc-strepxe?riaton?tsitned-sneigrurihc?uova??o&-x&bf,obeerf,?x&bf,obeerf,???t&acova?o&or-ne,psgolb,?r&epxe-ertemoeg?op!orea????vuog?xobided,?avc7ylqbgm--nx?s??g!.&etiselpmis,gro?moc?t&en?opsgolb,?ude?vog???h!.&e&erf,man??mo&c?rf??topsgolb,zi??ur??i!.&a&61f4a3abgm--nx?rf4a3abgm--nx??ca?di?gro?hcs?oc?ten?vog?نار&يا?یا???a&h?per??ew?lf??k!.&c&a?s??e&n?p?r??gk?iggnoeyg?kub&gn&oeyg?uhc??noej??l&im?uoes??man&gn&oeyg?uhc??noej??n&as&lu?ub??o&e&hcni?jead??wgnag???o&c?g??ro?s&e?h?m??topsgolb,u&gead?j&ej?gnawg????cilf??l!.&gro?moc?ten?ude?vog???m!.&topsgolb,vog???n!.&gro?moc?ofni?ten?ude?vog?zib???o&htua?odtnorf?t&c&a?od??laer???p!.&alsi?ca?eman?forp?gro?moc?o&fni?rp??t&en?se??ude?vog?zib???s?t!.&21k?bew?cn!.vog??eman?gro?kst?l&e&b?t??im?op??moc!.topsgolb,?neg?ofni?pek?rd?sbb?ten?ude?v&a?og?t??zib??f?m??ubad?vd??s&8sqif--nx?9zqif--nx?a!.vog?birappnb?gev?lliv?mtsirhc?s??b!.&ew,gro?moc?ten?ude?vog??c?oj?s?u??c&i&hparg?p?t&sigolyrrek?ylana???od??d&a?d?ik?l?n&iwriaf?omaid??oogemoh?rac??e!.&b&ewim321,og??gro?mo&c!.topsgolb,?n??pohsdaerpsym,ude??civres!.enilnigol,?d&d2bgm--nx?oc??h&ctaw?guh??i&lppus?rtsudni?treporp!yrrek???jaiv?l&aw?cycrotom?gnis?pats??m&ag?oh?reh??nut?ohs?picer?r&it?ut&cip!.7331,?nev???s&i&rpretne?urc??ruoc??taicossa?vig??g!nidloh??h5c822qif--nx?i!.&ekacpuc,gro?moc?t&en?ni?opsgolb,?ude?vog??a09--nx?nnet?rap?targ??k&c&or!.&ecapsbew,snddym,ytic-amil,??us??hxda08--nx?row??l!.&c&a?s??ed,gro?o&c?fni??ten?ude?vog?zib??a&ed?tner??e&ssurb?toh!yrrek???lahsram?m?oot??m!.&bal,etisinim,gro?moc?ten?ude?vog??b?etsys!.tniopthgink,?ialc??n&a&f?gorf?ol??i&a&grab?mod??giro??o&it&acav?cudorp?ulos??puoc???o&dnoc?geuj?ppaz?t&ohp!.remarf,?ua???p!.&ces?gro?moc?olp?ten?ude?vog??i&hsralohcs?lihp?t??u??r!.&au,ca?gro?ni?oc?topsgolb,ude?vog?xo,yldnerb.pohs,?a&c?p?tiug??c?e&dliub!.etisduolc,?erac?gor?levart?mraf?n&niw?trap??wolf??ot&cartnoc?omatat??pj?uot??s!.&em?gro?hcs?moc?ten?ude?vog?zib??alg?e&n&isub", + "!.oc,?tif??rp!xe!nacirema???xnal??iws??t&a&e&b?ytic??ob??ek&cit?ram??fig?h&cay?gilf??n&atnuocca?e&mt&rapa?sevni??ve!.&nibook,oc,????rap??u!.&a&c!.&21k?bil?cc???g!.&21k?bil?cc???i!.&21k?bil?cc???l!.&21k?bil?cc???m!.&21k!.&hcorap?rthc?tvp???bil?cc???p!.&21k?bil?cc???si?v!.&21k?bil?cc???w!.&21k?bil?cc????c&d!.&21k?bil?cc???n!.&21k?bil?cc???s!.&21k?bil?cc????d&e&f?lacsne.xhp,?i!.&21k?bil?cc???m!.&21k?bil?cc???n!.&bil?cc???s!.&bil?cc???u&olcrim,rd,??e&d!.&21k?bil,cc???las-4-&dnal,ffuts,?m!.&21k?bil?cc???n!.&21k?bil?cc????h&n!.&21k?bil?cc???o!.&21k?bil?cc????i&h!.&bil?cc???m!.&21k?bil?c&c?et??goc?n&eg?otae??robra-nna?sum?tsd?wanethsaw???nd?r!.&bil?cc???v!.&21k?bil?cc???w!.&21k?bil?cc????jn!.&21k?bil?cc???k&a!.&21k?bil?cc???o!.&21k?bil?cc????l&a!.&21k?bil?cc???f!.&21k?bil?cc???i!.&21k?bil?cc????mn!.&21k?bil?cc???n&afflog,i!.&21k?bil?cc???m!.&21k?bil?cc???sn?t!.&21k?bil?cc????o&c!.&21k?bil?cc???m!.&21k?bil?cc???ttniop,?p&ion,rettalp,?r&a!.&21k?bil?cc???o!.&21k?bil?cc???p!.&21k?bil?cc????s&a!.&21k?bil?cc???dik?k!.&21k?bil?cc???m!.&21k?bil?cc???nd&deerf,uolc,??t&c!.&21k?bil?cc???m!.&21k?bil?cc???u!.&21k?bil?cc???v!.&21k?bil?cc????ug!.&21k?bil?cc???v&n!.&21k?bil?cc???w!.cc???x&ohparg,t!.&21k?bil?cc????y&b-si,k!.&21k?bil?cc???n!.&21k?bil?cc???w!.&21k?bil?cc????za!.&21k?bil?cc????ah!uab??bria?col?e!.ytrap.resu,?ineserf?lp?xe&l?n???vt?w!.&66duolc,gro?moc?s&ndnyd,tepym,?ten?ude?vog??a?e&iver?n!.elbaeciton,??odniw??y&alcrab?ot???t&0srzc--nx?a!.&amil4,ca!.hts??etiesbew321,gni&liamerutuf,tsoherutuf,?o&c!.topsgolb,?fni,?p&h21,ohsdaerpsym,?r&euefknuf.neiw,o??v&g?irp,?xi2,ytic-amil,zib,?c?e!s??hc?l!asite??mami?rcomed??b!.&gro?moc?ten?ude?vog??b?gl??c&atnoc?e&les?rid!txen????dimhcs?e!.&eman?gro?moc?ofni?ten?ude?vog?zib??b?em?grat?id?k&circ?ram??n!.&0rab,1rab,2rab,5inu,6vnyd,7&7ndc.r,erauqs,?a&l&-morf,moob,?minifed,remacytirucesym,tadsyawla,z,?b&boi,g,lyltsaf:.pam,,?c&i&nagro-gnitae,tats-oieboda,?paidemym,?d&e&calpb,ziamaka,?hiamaka,irgevissam.saap.&1-&gs,nol,rf,yn,?2-&nol,yn,??nab-eht-ni,uolc&meaeboda,nievas.c&di-etsedron,itsalej,?xednay:.e&garots,tisbew,?,??e&c&narusnihtlaehezitavirp,rofelacs.j,?gd&eiamaka,irbtib,?ht-no-eciffo,l&acs&liat.ateb,noom,?ibom-eruza,?m&ecnuob,itnuroieboda,ohtanyd,tcerider,?n&ilno-evreser,ozdop,?rehurht,s:abapus,,ti&s-repparcs,usegde,?zam&aym,kcar,??f&aeletis,crs.&cos,resu,?ehc-a-si,?g&ni&gats-&d&eziamaka,hiamaka,?e&gdeiamaka,tiusegde,?iamaka,nigiroiamaka,yekegde,?reesnes,sirkcilc,tsohnnylf,?olbevres,?iamaka,k&catsvano,eeg-a&-si,si,?u,?l&acolottad,iamwt,meteh,s&d-ni,s-77ndc,??m&ac&asac,ih,?urofniem,?n&a&f&agp,lhn,?i&bed,llerk,??dcduabkcalb,i:giroiamaka,,pv-ni,?o&c-morf,duppa,jodsnd,rp-ytinummoc,ttadym,?p&i&-&etsef,on,?emoh,fles,nwo,?j,mac-dnab-ta,o&-oidar-mah,h&bew,sdaerpsym,??pa&duolc,egde,?tfe&moh,vres,?usnd,?r&e&tsulcyduolc,vres-xnk,?vdslennahc:.u,,?s&a&ila&nyd,snd,?nymsd,?bbevres,dylimaf,e&gde-ndc,rauqs,suohsyub,t&isbeweruza,ys,??k&catstsaf,ekokohcs,?n&d&-won,aka,d,golb,npv,?oitcnufduolc,?ppacitatseruza:.&1,2:suts&ae,ew,?,3,aisatsae,eporuetsew,sulartnec,?,s&a-skcik,ecca&-citats,duolc,??t,?t&adies,ce&ffeym,jorprot:.segap,,lespohs,?e&nretnifodne,smem,?farcenimevres,i-&ekorb,s&eod,lles,teg,??n&essidym,orfduolc,?r0p3l3t,s&ixetnod,oh&-spv:.citsalej.&cir,lta,sjn,?,gnik,???u&h,nyd,r:eakust.citsalej,,?ved-naissalta.dorp.ndc,x&inuemoh,spym,tsale.&1ots-slj,2ots-slj,3ots-slj,?unilemoh,?y&awetag-llawerif,ekegde,ffijduolc:.&ed-1arf,su-1tsew,?,ltsaf.&dorp.&a,labolg,?lss.&a,b,labolg,?pam,slteerf,?n&-morf,ofipi,?srab,?z&a-morf,tirfym,???p?tcip?v??f&ig?osorcim??g!.&bog?dni?ed,g&olb,ro??lim?moc?ot,ten?ude???h!.&dem?gro?l&er?op??m&oc?rif??o&fni?rp?s&rep?sa???po&hs?oc??t&en?luda?ra??ude?vuog???i!.&a&2n-loritds--nx?7e-etsoaellav--nx?8&c-aneseclrof--nx?i-lrofanesec--nx??at?b?c!cul??dv?i&blo&-oipmet?oipmet??cserb?drabmol?g&gof?urep??l&gup?i&cis?me&-oigger?oigger???uig&-&aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf???aizenev&-iluirf?iluirf??ev&-iluirf?iluirf??v&-iluirf?iluirf????n&a&brev?cul?pmac?tac??idras?obrac&-saiselgi?saiselgi??resi??otsip?r&b&alac!-oigger?oigger??mu??dna&-&attelrab-inart?inart-attelrab??attelrabinart?inartattelrab?ssela??epmi?ugil??tnelav&-obiv?obiv??vap?z&e&nev?ps&-al?al???irog???l&iuqa!l??leib??m&or?rap??n!acsot?e&dom?is?sec&-&ilrof?ìlrof??ilrof?ìlrof???g&amor&-ailime?ailime??edras?olob??i&ssem?tal??ne!var??o&cna?merc?rev?vas???oneg?p?r!a&csep?rr&ac&-assam?assam??ef??von??etam?tsailgo!-lled?lled???s!ip?sam&-ararrac?ararrac??u&caris?gar???t!a&cilisab?recam??resac?soa!-&d&-&ellav?lav??ellav?lav??ellav??d&-&ellav?lav??ellav?lav??ellav??te&lrab&-&airdna-inart?inart-airdna??airdnainart?inartairdna??ssinatlac???udap?v!o&dap?neg?tnam???zn&airb&-a&lled-e-aznom?znom??a&lledeaznom?znom??eaznom??e&c&aip?iv??soc?top??om???b&-&23,46,61,?3c-lorit-ds-onitnert--nx?be-etsoa&-ellav--nx?dellav--nx??c!f-anesec-lrof--nx?m-lrof-anesec--nx??he-etsoa-d-ellav--nx?m!u??o2-loritds-nezob--nx?sn-loritds&-nasl&ab--nx?ub--nx??nitnert--nx??v!6-lorit-dsnitnert--nx?7-loritds&-nitnert--nx?onitnert--nx???z&r-lorit-ds&-nitnert--nx?onitnert--nx??s-loritds-onitnert--nx???c&f?is?l?m?p?r?v??d&p?u!olcnys,??e&c!cel?inev?nerolf??f?g!apemoh321,ida&-&a&-onitnert?onitnert??otla!-onitnert?onitnert???a&-onitnert?onitnert??otla!-on&azlob?itnert??onitnert????hcram?l?m!or??n&idu?o&n&edrop?isorf??torc???p?r?s&erav?ilom??t!nomeip?s&eirt?oa!-&d-e&ellav?éllav??e&ellav?éllav???de&ellav?éllav??e&ellav?éllav?????v?znerif??g&a?b?f?il?o?p?r?up?vf??hc?i&b?c?dol?f?l!lecrev?opan?rof&-anesec?anesec???m?n&a&part?rt&-attelrab-airdna?attelrabairdna???imir?ret??p?r!a&b?ilgac?ssas???s!idnirb??t&ei&hc?r??sa??v??l&a!c??b?c?o&m?rit&-&d&eus&-&nitnert?onitnert??nitnert?onitnert??us&-&nitnert?onitnert??nitnert?onitnert??üs&-&nitnert?onitnert??nitnert?onitnert???s&-onitnert?onitnert???d&eus!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??us&-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert??üs!-&n&asl&ab?ub??ezob?itnert??onitnert??nitnert?onitnert???s&-onitnert?onitnert?????m&ac?f?i!t.nepo.citsalej.duolc,?ol?r??n&a!lim?sl&ab?ub???b?c?e!en.cj,v?zob??irut?m!p??p?r?t??o&a!v??b!retiv??c!cel??enuc?g!ivor??i&dem&-onadipmac?onadipmac??pmet&-aiblo?aiblo??rdnos?zal??l?m!a&greb?ret??oc?re&f?lap???n!a&dipmac&-oidem?oidem??lim?tsiro?zlob??ecip&-ilocsa?ilocsa??i&bru&-orasep?orasep??lleva?rot?tnert??r&elas?ovil??ulleb??p?r!a&sep&-onibru?onibru??znatac??oun??s!ivert?sabopmac??t!arp?e&nev?ssorg??n&arat?e&girga?rt?veneb????zz&era?urba???p&a?ohsdaerpsym,s?t??qa?r&a!m?s??b!a??c?f?g?k?me?o?p?s?t?v??s&a&b?iselgi&-ainobrac?ainobrac???b?c?elpan?i?m?o&t?x&bi,obdaili,??s?t?v??t&a?b?c?l?m?nomdeip?o!psgolb,?p?v??u&de?l?n?p??v&a?og?p?s?t?v??y&drabmol?ellav&-atsoa?atsoa??licis?nacsut??z&al?b?c?p??ìlrof&-anesec?anesec???derc?er?f?m?utni??je3a3abgm--nx?kh?l!.&topsgolb,vog??uda??m!.&gro?moc!.topsgolb,?ten?ude???n&a&morockivdnas?ruatser?tnuocca??e&g?m&eganam!.retuor,?piuqe??r??i!.ue?m?opdlog??opud?uocsid??o&b?cs!.&ude,vog:.ecivres,,??d?g?h?j?oferab?p&edemoh?s???p!.&bewanigap321,emon?gro?lbup?moc?t&en?ni?opsgolb,?ude?vog???r&a!m&law?s???epxe?op&er?pus!.ysrab,?s???s!.&a&daxiabme?rarik,?e&motoas?picnirp?rots??gro?lim?moc?o&c?dalusnoc?hon,?ten?ude??a&cmoc?f??e&b?r?uq??i!rolf?tned??o&h!.&duolc&p,rim,?e&lej,tiseerf,?flah,l&enapysae,rupmet,?s&pvtsaf,seccaduolc,?tsafym,vedumpw,??p!sua???urt??t!.&eman?gro?ibom?levart?m&oc?uesum??o&c?fni?r&ea?p???pooc?sboj?t&en?ni??ude?vog?zib??ayh?n?o!bba?irram???uognah?xen?y!.gro,?ztej??u&2&5te9--nx?yssp--nx??a!.&a&s?w??civ?d&i?lq??fnoc?gro?moc!.&pohsdaerpsym,stelduolc.lem,topsgolb,??nsa?ofni?sat?t&ca?en?n??ude!.&a&s?w??ci&lohtac?v??dlq?sat?t&ca?n??wsn!.sloohcs????vog!.&a&s?w??civ?dlq?sat???wsn?zo??ti??c!.&fni?gro?moc?ten?ude?vog??i??d&e!.tir.segap-tig,?iab??e!.&dcym,enozgniebllew,noitatsksid,odagod.citsalej,s&nd&ps,uolc,?ppatikria,?ysrab,??g!.&bew?gro?m&aug?oc??ofni?ten?ude?vog???h!.&0002?a&citore?idem?kitore??edszot?gro?ilus?letoh?m&alker?lif?t?urof??naltagni?o&c?ediv?fni?levynok?nisac??pohs?rarga?s&a&kal?zatu??emag?wen??t&lob?opsgolb,rops??virp?xe&s?zs??ytic?zsagoj??os?sut??l!.&etisbew321,topsgolb,??m!.&ca?gro?moc?oc?ro?ten?vog???n!.&duolcesirpretne,eni&esrem,m,?tenkcahs,?em!.ysrab,??o&ggnaw?y!c???r!.&3kl,a&i&kymlak,rikhsab,vodrom,?yegyda,?bps,ca,duolcrim,e&niram,rpcm,?g&bc,nitsohurger.citsalej,ro,?ianatsuk,k&ihclan,s&m,rogitayp,??li&amdlc.bh,m,?moc,natsegad,onijym,pp,ri&b,d&cm:.spv,,orue,?midalv,?s&ar,itym,?t&en,ias321,ni,opsgolb,set,?u&4an,de,?vo&g,n,?ynzorg,zakvakidalv,?myc?p?ug??s!.&a&d&golov,nagarak,?gulak,i&groeg,kymlak,lerak,nemra,rikhsab,ssakahk,vodrom,zahkba,?lut,rahkub,vut,yegyda,znep,?bps,da&baghsa,rgonilest,?gunel,i&anatsuk,hcos,ovan,ttailgot,?k&alhsygnam,ihclan,s&legnahkra,m,n&a&mrum,yrb,?i&buytka,nbo,??tiort,vorkop,??l&ocarak,ybmaj,?na&gruk,jiabreza,ts&egad,hkazak-&htron,tsae,???ovonavi,r&adonsark,imidalv,?t&enxe,nek&hsat,mihc,??vo&hsalab,n,?ynzorg,z&akvakidalv,emret,??t&amok?i&juf?masih????v!.&em,g&olb,ro??moc?nc,ten?ude?ved,??ykuyr??v&b?c!.&emon?gro?moc?t&ni?opsgolb,?ude???ed!.&2r,ated,e&docotua,erf-korgn,nilnigol,?gnigats-oned,hcetaidem,korgn,lecrev,o&ned,tpyrctfihs,?ppa-rettalp,s&egap,rekrow,?vr&esi,uc,?weiverpbuhtig,ylf,??ih?l!.&di?fnoc?gro?lim?moc?nsa?ten?ude?vog???m!.&eman?gro?lim?m&oc?uesum??o&fni?r&ea?p???pooc?t&en?ni??ude?vog?zib???o&g?m??rt?s!.&bog?der?gro?moc?ude???t!.&arukas,bew-eht-no,morf,naht-&esrow,retteb,?sndnyd,?d?i?won??uqhv--nx??w&a!.moc?hs?l??b!.&gro?oc???c!.&gro?moc?ten?ude??cp??e&iver!.oby,?n?s??g?k!.&bme?dni?gro?moc?ten?ude?vog???m!.&ca?gro?m&oc?uesum??oc?pooc?t&en?ni??ude?vog?zib??b??o&csom?h!s??n?w??p!.&344x,de?en?o&c?g??ro?snduolc,ualeb???r!.&ca?gro?lim?oc?pooc?ten?vog??n??t!.&a46oa0fz--nx?b&82wrzc--nx?ulc??emag?gro?l&im?ru,?moc!.reliamym,?t&en?opsgolb,?ude?v&di?og?ta0cu--nx??zibe?業商?織組?路網???z!.&ca?gro?lim?oc?vog????x&a!.&cm,eb,gg,s&e,u,?tac,ue,yx,?t??c!.&hta,ofni,vog???e&d&ef?nay??ma!nab??rof?s??ilften?jt?m!.&bog?gro?moc?t&en?opsgolb,?ude??g?ma2ibgy--nx??o&b!x??f?rex??rbgn--nx?s!.vog??x&am&jt?kt??x???y&4punu--nx?7rr03--nx?a&d!i&loh?rfkcalb??ot!.emyfilauqerp,??g?lp?p!ila??rot?ssin?wdaorb??b!.&duolcym,fo?hcetai", + "dem,lim?moc!.topsgolb,?vog??ab?gur??c!.&ca?dtl?gro?lim?m&oc!.&ecrofelacs.j,topsgolb,??t??orp?s&egolke?serp??ten?vog?zib??amrahp?nega??d&dadog?uts??e&kcoh?ltneb?n&dys?om?rotta??snikcm??g!.&eb,gro?moc?oc?ten?ude?vog??olonhcet!.oc,?rene??hpargotohp?id?k!.&gro?moc?ten?ude??s??l!.&clp?d&em?i??gro?hcs?moc?ten?ude?vog??f?imaf!nacirema??l&a?il??ppus??m!.&eman?gro?lim?moc?t&en?opsgolb,?ude?vog?zib??edaca!.laiciffo,?ra??n&a&ffit?pmoc??os??o&j?s??p!.&gro?lim?moc?pooc?ten?ude?vog???r&e&corg?grus?llag?viled??lewej?otcerid?tnuoc?uxul??s!.&gro?lim?moc?ten?ude?vog??pil??t&efas?i&c?ledif?n&ifx?ummoc!.&bdnevar,gon,murofym,???r&ahc?uces??srevinu??laer?r&ap!.oby,?eporp??uaeb??u!.&bug?gro?lim?moc!.topsgolb,?ten?ude??b!tseb???van!dlo??xes??z&a!.&eman?gro?lim?moc?o&fni?rp??pp?t&en?ni??ude?vog?zib???b!.&az,gro?jsg,moc?ten?ude?vog???c!.&4e,inum.duolc.&rsu,tlf,?m&laer,urtnecatem.motsuc,?oc,topsgolb,??d!.&cos?gro?lop?m&oc?t??ossa?t&en?ra??ude?vog???ib!.&duolcsd,e&ht-rof,mos-rof,rom-rof,?izoj,liartevitca,nafamm,p&i&-on,fles,?ohbew,tfym,?retteb-rof,snd&nyd,uolc,?xro,?g??k!.&duolcj,gro?lim?moc?t&en?ropeletzak.saapu,?ude?vog???m!.&ca?gro?lim?oc?ten?ude?v&da?og????n!.&asq-irom--nx?ca?gro?htlaeh?i&r&c?o&am?ām???wi!k???keeg?l&im?oohcs??neg?oc!.topsgolb,?t&en?nemailrap?vog???a!niflla???rawhcs?s!.&ca?gro?oc???t!.&c&a?s??e&m?n??ibom?l&etoh?im??o&c?fni?g??ro?vt???u!.&gro?moc?oc?ten??rwon??yx!.&e&nozlacol,tisgolb,?gnitfarc,otpaz,??zub??λε?υε?авксом?брс!.&гро?до?ка?р&бо?п!у?????г&б?ро??дкм?зақ?итед?килотак?леб?мок?н&йално?ом??рку?сур!.&арамас,бпс,гро,зиб,ичос,ксм,м&ок,ырк,?рим,я,??тйас?фр?юе?յահ?לארשי!.&בושי?הימדקא?ל&הצ?שממ????םוק?اي&روس?سيلم?ناتيروم??بر&ع?غملا??ة&كبش?ي&دوعسلا?روس??یدوعسلا??ت&ا&راما?لاصتا??را&ب?ڀ?ھب???ر&ئازجلا?ازاب?صم?طق??سنوت?عقوم?قارع?ك&تيب?يلوثاك??موك?ن&ا&تس&كاپ?کاپ??دوس?ر&يا?یا??مع?يلعلا??درالا?ميلا?ي&رحبلا?طسلف???ه&ارمه?يدوعسلا??وكمارا?يبظوبا?ۃیدوعسلا?टेन?त&राभ?ोराभ??नठगंस?मॉक?्मतराभ?ত&রাভ?ৰাভ??ালংাব?ਤਰਾਭ?તરાભ?ତରାଭ?ாயித்நஇ?ைக்ஙலஇ?்ரூப்பக்ஙிச?్తరాభ?ತರಾಭ?ംതരാഭ?ාකංල?มอค?ยทไ!.&จิกรุธ?ต็นเ?ร&ก์คงอ?าหท??ลาบฐัร?าษกึศ???ວາລ?ეგ?なんみ?アトス?トンイポ?ドウラク?ムコ?ル&グーグ?ーセ??ン&ゾマア?ョシッァフ??业企?东广?乐娱?你爱我?信中?务政?动移?博微?卦八?厅餐?司公?品食?善慈?团集?国中?國中?址网?坡加新?城商?尚时?山佛?店&商?网?酒大里嘉??府政?康健?息信?戏游?拉里格香?拿大?教主天?机手?构机!织组??标商?歌谷?浦利飞?港香!.&人個?司公?府政?絡網?織組?育教???湾台?灣&台?臺??物购?界世?益公?看点?科盈訊電?站网?籍書?线在?络网?网文中?聘招?販通?逊马亚?通联?里嘉?锡马淡?門澳?门澳?闻新?電家?국한?넷닷?성삼?컴닷??"); + + /** + * If a hostname is not a key in the EXCLUDE map, and if removing its leftmost component results + * in a name which is a key in this map, it is a public suffix. + */ + public static final ImmutableMap UNDER = + TrieParser.parseTrie( + "ac.vedwa,d&b?i.ym.ssr,uolc.&etiso&isnes,tnegam,?iaznab,rehcnar-no,scitats,??e&b.lrusnart,d.&ecapsrebu,yksurf,?noz.notirt,t&atse.etupmoc,is.&areduolc,hsmroftalp,tst,???g&oog.tnetnocresu,p??h&c.tenerif:.cvs,,k?trae.sppad:.zzb,,?k&c?f?nil.bewd,rowten.secla,u.hcs??ln.lrusnart,m&f.resu,j?m?oc.&duolcmeaeboda.ved,edo&c.redliub:->s,ved,?,nil.recnalabedon,?ico-remotsuc:.&ico,pco,sco,?,lrihwyap,mme0,osseccandcved,s&ecapsnaecolatigid,t&cejbo&edonil,rtluv,?nemelepiuq,?wanozama.&1-etupmoc,ble,etupmoc,??t&neyoj.snc,opsppa.r,???n&c.moc.swanozama.&ble,etupmoc,?ur.&dliub,e&doc,sabatad,?noitargim,??o&c.pato,i.&duolciaznab.sdraykcab,elacsnoom,nroca-no,oir-no,reniatnoceruza,s&3k-no,olots,?xcq.sys,y5s,??p&j.&a&mahokoy?yogan??ebok?i&adnes?kasawak??oroppas?uhsuykatik??n?pa.&knalfhtron,repoleved,tegeb,??r&b.mon?e??s&edoc.owo,noitulos.rehid,w.rosivda,?t&a.&ofnistro.&nednuk,xe,?smcerutuf:.&ni,xe,?,?en.&cimonotpyrc,hvo.&gnitsoh,saapbew,???u&e.lrusnart,r.onijym.&gni&dnal,tsoh,?murtceps,spv,??ved.&e&gats>s,lcl,?rahbew,?gts,lcl,treclacol.resu,yawetag,?z&c.murtnecatem.duolc,yx.tibelet,??"); + + /** + * The elements in this map would pass the UNDER test, but are known not to be public suffixes and + * are thus excluded from consideration. Since it refers to elements in UNDER of the same type, + * the type is actually not important here. The map is simply used for consistency reasons. + */ + public static final ImmutableMap EXCLUDED = + TrieParser.parseTrie( + "kc.www?pj.&a&mahokoy.ytic?yogan.ytic??ebok.ytic?i&adnes.ytic?kasawak.ytic??oroppas.ytic?uhsuykatik.ytic???"); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/PublicSuffixType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/PublicSuffixType.java new file mode 100644 index 0000000000000..dbe1e7c2577ed --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/PublicSuffixType.java @@ -0,0 +1,70 @@ +/* + * 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.iotdb.google.thirdparty.publicsuffix; + +import org.apache.iotdb.google.common.annotations.Beta; +import org.apache.iotdb.google.common.annotations.GwtCompatible; + +/** + * Do not use this class directly. For access to public-suffix information, use {@link + * org.apache.iotdb.google.common.net.InternetDomainName}. + * + *

Specifies the type of a top-level domain definition. + * + * @since 23.3 + */ +@Beta +@GwtCompatible +public enum PublicSuffixType { + + /** Public suffix that is provided by a private company, e.g. "blogspot.com" */ + PRIVATE(':', ','), + /** Public suffix that is backed by an ICANN-style domain name registry */ + REGISTRY('!', '?'); + + /** The character used for an inner node in the trie encoding */ + private final char innerNodeCode; + + /** The character used for a leaf node in the trie encoding */ + private final char leafNodeCode; + + PublicSuffixType(char innerNodeCode, char leafNodeCode) { + this.innerNodeCode = innerNodeCode; + this.leafNodeCode = leafNodeCode; + } + + char getLeafNodeCode() { + return leafNodeCode; + } + + char getInnerNodeCode() { + return innerNodeCode; + } + + /** Returns a PublicSuffixType of the right type according to the given code */ + static PublicSuffixType fromCode(char code) { + for (PublicSuffixType value : values()) { + if (value.getInnerNodeCode() == code || value.getLeafNodeCode() == code) { + return value; + } + } + throw new IllegalArgumentException("No enum corresponding to given code: " + code); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/TrieParser.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/TrieParser.java new file mode 100644 index 0000000000000..86413d8af1efa --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/thirdparty/publicsuffix/TrieParser.java @@ -0,0 +1,125 @@ +/* + * 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.iotdb.google.thirdparty.publicsuffix; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.annotations.VisibleForTesting; +import org.apache.iotdb.google.common.base.Joiner; +import org.apache.iotdb.google.common.collect.ImmutableMap; + +import java.util.Deque; + +import static org.apache.iotdb.google.common.collect.Queues.newArrayDeque; + +/** Parser for a map of reversed domain names stored as a serialized radix tree. */ +@GwtCompatible +final class TrieParser { + + private static final Joiner DIRECT_JOINER = Joiner.on(""); + + /** + * Parses a serialized trie representation of a map of reversed public suffixes into an immutable + * map of public suffixes. The encoded trie string may be broken into multiple chunks to avoid the + * 64k limit on string literal size. In-memory strings can be much larger (2G). + */ + static ImmutableMap parseTrie(CharSequence... encodedChunks) { + String encoded = DIRECT_JOINER.join(encodedChunks); + return parseFullString(encoded); + } + + @VisibleForTesting + static ImmutableMap parseFullString(String encoded) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + int encodedLen = encoded.length(); + int idx = 0; + + while (idx < encodedLen) { + idx += doParseTrieToBuilder(newArrayDeque(), encoded, idx, builder); + } + + return builder.buildOrThrow(); + } + + /** + * Parses a trie node and returns the number of characters consumed. + * + * @param stack The prefixes that precede the characters represented by this node. Each entry of + * the stack is in reverse order. + * @param encoded The serialized trie. + * @param start An index in the encoded serialized trie to begin reading characters from. + * @param builder A map builder to which all entries will be added. + * @return The number of characters consumed from {@code encoded}. + */ + private static int doParseTrieToBuilder( + Deque stack, + CharSequence encoded, + int start, + ImmutableMap.Builder builder) { + + int encodedLen = encoded.length(); + int idx = start; + char c = '\0'; + + // Read all the characters for this node. + for (; idx < encodedLen; idx++) { + c = encoded.charAt(idx); + + if (c == '&' || c == '?' || c == '!' || c == ':' || c == ',') { + break; + } + } + + stack.push(reverse(encoded.subSequence(start, idx))); + + if (c == '!' || c == '?' || c == ':' || c == ',') { + // '!' represents an interior node that represents a REGISTRY entry in the map. + // '?' represents a leaf node, which represents a REGISTRY entry in map. + // ':' represents an interior node that represents a private entry in the map + // ',' represents a leaf node, which represents a private entry in the map. + String domain = DIRECT_JOINER.join(stack); + + if (domain.length() > 0) { + builder.put(domain, PublicSuffixType.fromCode(c)); + } + } + + idx++; + + if (c != '?' && c != ',') { + while (idx < encodedLen) { + // Read all the children + idx += doParseTrieToBuilder(stack, encoded, idx, builder); + + if (encoded.charAt(idx) == '?' || encoded.charAt(idx) == ',') { + // An extra '?' or ',' after a child node indicates the end of all children of this node. + idx++; + break; + } + } + } + + stack.pop(); + return idx - start; + } + + private static CharSequence reverse(CharSequence s) { + return new StringBuilder(s).reverse(); + } +} diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/IoTDBBuiltInUDFTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/IoTDBBuiltInUDFTest.java index 81320b29ef234..236fb6116279b 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/IoTDBBuiltInUDFTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/udf/IoTDBBuiltInUDFTest.java @@ -38,6 +38,7 @@ import org.apache.iotdb.commons.udf.builtin.UDTFSqrt; import org.apache.iotdb.commons.udf.builtin.UDTFTan; import org.apache.iotdb.commons.udf.builtin.UDTFTanh; +import org.apache.iotdb.google.common.collect.ImmutableList; import org.apache.iotdb.udf.api.UDTF; import org.apache.iotdb.udf.api.access.Row; import org.apache.iotdb.udf.api.collector.PointCollector; @@ -46,7 +47,6 @@ import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters; import org.apache.iotdb.udf.api.type.Type; -import com.google.common.collect.ImmutableList; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; diff --git a/iotdb-core/relational-grammar/pom.xml b/iotdb-core/relational-grammar/pom.xml index 6e7012b63ffa5..56a9359874205 100644 --- a/iotdb-core/relational-grammar/pom.xml +++ b/iotdb-core/relational-grammar/pom.xml @@ -30,10 +30,6 @@ IoTDB: Core: Relational-Antlr-Parser Relational Antlr parser for IoTDB. - - com.google.guava - guava - org.antlr antlr4-runtime diff --git a/iotdb-core/relational-grammar/src/main/java/org/apache/iotdb/db/relational/grammar/sql/RelationalSqlKeywords.java b/iotdb-core/relational-grammar/src/main/java/org/apache/iotdb/db/relational/grammar/sql/RelationalSqlKeywords.java index 5d936aced9a65..4c2d1d75d698f 100644 --- a/iotdb-core/relational-grammar/src/main/java/org/apache/iotdb/db/relational/grammar/sql/RelationalSqlKeywords.java +++ b/iotdb-core/relational-grammar/src/main/java/org/apache/iotdb/db/relational/grammar/sql/RelationalSqlKeywords.java @@ -19,22 +19,21 @@ package org.apache.iotdb.db.relational.grammar.sql; -import com.google.common.collect.ImmutableSet; import org.antlr.v4.runtime.Vocabulary; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.google.common.base.Strings.nullToEmpty; - public final class RelationalSqlKeywords { private static final Pattern IDENTIFIER = Pattern.compile("'([A-Z_]+)'"); private RelationalSqlKeywords() {} public static Set sqlKeywords() { - final ImmutableSet.Builder names = ImmutableSet.builder(); + final Set names = new LinkedHashSet<>(); final Vocabulary vocabulary = RelationalSqlLexer.VOCABULARY; for (int i = 0; i <= vocabulary.getMaxTokenType(); i++) { final String name = nullToEmpty(vocabulary.getLiteralName(i)); @@ -43,6 +42,10 @@ public static Set sqlKeywords() { names.add(matcher.group(1)); } } - return names.build(); + return Collections.unmodifiableSet(names); + } + + static String nullToEmpty(String string) { + return (string == null) ? "" : string; } } diff --git a/library-udf/pom.xml b/library-udf/pom.xml index ef592895cc395..c13ac893d922c 100644 --- a/library-udf/pom.xml +++ b/library-udf/pom.xml @@ -31,6 +31,7 @@ 17 17 + 32.1.2-jre @@ -73,6 +74,7 @@ com.google.guava guava + ${guava.version} junit diff --git a/pom.xml b/pom.xml index 331a45246138b..b8d0421db289e 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,6 @@ 4.2.0 1.84 - 2.9.3 3.3.0 Release 1.5.0 @@ -81,7 +80,6 @@ 1.16 1.28.0 2.13.1 - 32.1.2-jre 2.2.224 4.5.14 4.4.16 @@ -274,11 +272,6 @@ powermock-reflect ${powermock.version} - - com.google.guava - guava - ${guava.version} - org.java-websocket Java-WebSocket @@ -401,6 +394,10 @@ javax.inject javax.inject + + com.google.guava + guava + @@ -570,11 +567,6 @@ jetty-ee10-servlet ${jetty.version} - - com.github.ben-manes.caffeine - caffeine - ${caffeine.version} - org.apache.httpcomponents httpcore @@ -1008,6 +1000,10 @@ ${maven.multiModuleProjectDirectory}/src/main/forbidden-apis/secure-erase + + org/apache/iotdb/google/common/io/Files*.class + org/apache/iotdb/google/common/io/FileBackedOutputStream*.class + @@ -1212,6 +1208,8 @@ maven-checkstyle-plugin checkstyle.xml + suppressions.xml + checkstyle.suppressions.file true diff --git a/suppressions.xml b/suppressions.xml new file mode 100644 index 0000000000000..8d65ee7395e2f --- /dev/null +++ b/suppressions.xml @@ -0,0 +1,26 @@ + + + + + + +

This class compares primitive {@code double} values in methods such as + * {@link #compareAndSet} by comparing their bitwise representation using {@link + * Double#doubleToRawLongBits}, which differs from both the primitive double {@code ==} operator and + * from {@link Double#equals}, as if implemented by: + * + *

This workaround should be removed at a distant future time when we no longer support Java + * versions earlier than 8. + */ + private static final class TypeVariableInvocationHandler implements InvocationHandler { + private static final ImmutableMap typeVariableMethods; + + static { + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Method method : TypeVariableImpl.class.getMethods()) { + if (method.getDeclaringClass().equals(TypeVariableImpl.class)) { + try { + method.setAccessible(true); + } catch (AccessControlException e) { + // OK: the method is accessible to us anyway. The setAccessible call is only for + // unusual execution environments where that might not be true. + } + builder.put(method.getName(), method); + } + } + typeVariableMethods = builder.buildKeepingLast(); + } + + private final TypeVariableImpl typeVariableImpl; + + TypeVariableInvocationHandler(TypeVariableImpl typeVariableImpl) { + this.typeVariableImpl = typeVariableImpl; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + Method typeVariableMethod = typeVariableMethods.get(methodName); + if (typeVariableMethod == null) { + throw new UnsupportedOperationException(methodName); + } else { + try { + return typeVariableMethod.invoke(typeVariableImpl, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + } + } + + private static final class TypeVariableImpl { + + private final D genericDeclaration; + private final String name; + private final ImmutableList bounds; + + TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) { + disallowPrimitiveType(bounds, "bound for type variable"); + this.genericDeclaration = checkNotNull(genericDeclaration); + this.name = checkNotNull(name); + this.bounds = ImmutableList.copyOf(bounds); + } + + public Type[] getBounds() { + return toArray(bounds); + } + + public D getGenericDeclaration() { + return genericDeclaration; + } + + public String getName() { + return name; + } + + public String getTypeName() { + return name; + } + + @Override + public String toString() { + return name; + } + + @Override + public int hashCode() { + return genericDeclaration.hashCode() ^ name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { + // equal only to our TypeVariable implementation with identical bounds + if (obj != null + && Proxy.isProxyClass(obj.getClass()) + && Proxy.getInvocationHandler(obj) instanceof TypeVariableInvocationHandler) { + TypeVariableInvocationHandler typeVariableInvocationHandler = + (TypeVariableInvocationHandler) Proxy.getInvocationHandler(obj); + TypeVariableImpl that = typeVariableInvocationHandler.typeVariableImpl; + return name.equals(that.getName()) + && genericDeclaration.equals(that.getGenericDeclaration()) + && bounds.equals(that.bounds); + } + return false; + } else { + // equal to any TypeVariable implementation regardless of bounds + if (obj instanceof TypeVariable) { + TypeVariable that = (TypeVariable) obj; + return name.equals(that.getName()) + && genericDeclaration.equals(that.getGenericDeclaration()); + } + return false; + } + } + } + + static final class WildcardTypeImpl implements WildcardType, Serializable { + + private final ImmutableList lowerBounds; + private final ImmutableList upperBounds; + + WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) { + disallowPrimitiveType(lowerBounds, "lower bound for wildcard"); + disallowPrimitiveType(upperBounds, "upper bound for wildcard"); + this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds); + this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds); + } + + @Override + public Type[] getLowerBounds() { + return toArray(lowerBounds); + } + + @Override + public Type[] getUpperBounds() { + return toArray(upperBounds); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WildcardType) { + WildcardType that = (WildcardType) obj; + return lowerBounds.equals(Arrays.asList(that.getLowerBounds())) + && upperBounds.equals(Arrays.asList(that.getUpperBounds())); + } + return false; + } + + @Override + public int hashCode() { + return lowerBounds.hashCode() ^ upperBounds.hashCode(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("?"); + for (Type lowerBound : lowerBounds) { + builder.append(" super ").append(JavaVersion.CURRENT.typeName(lowerBound)); + } + for (Type upperBound : filterUpperBounds(upperBounds)) { + builder.append(" extends ").append(JavaVersion.CURRENT.typeName(upperBound)); + } + return builder.toString(); + } + + private static final long serialVersionUID = 0; + } + + private static Type[] toArray(Collection types) { + return types.toArray(new Type[0]); + } + + private static Iterable filterUpperBounds(Iterable bounds) { + return Iterables.filter(bounds, Predicates.not(Predicates.equalTo(Object.class))); + } + + private static void disallowPrimitiveType(Type[] types, String usedAs) { + for (Type type : types) { + if (type instanceof Class) { + Class cls = (Class) type; + checkArgument(!cls.isPrimitive(), "Primitive type '%s' used as %s", cls, usedAs); + } + } + } + + /** Returns the {@code Class} object of arrays with {@code componentType}. */ + static Class getArrayClass(Class componentType) { + // TODO(user): This is not the most efficient way to handle generic + // arrays, but is there another way to extract the array class in a + // non-hacky way (i.e. using String value class names- "[L...")? + return Array.newInstance(componentType, 0).getClass(); + } + + // TODO(benyu): Once behavior is the same for all Java versions we support, delete this. + enum JavaVersion { + JAVA6 { + @Override + GenericArrayType newArrayType(Type componentType) { + return new GenericArrayTypeImpl(componentType); + } + + @Override + Type usedInGenericType(Type type) { + checkNotNull(type); + if (type instanceof Class) { + Class cls = (Class) type; + if (cls.isArray()) { + return new GenericArrayTypeImpl(cls.getComponentType()); + } + } + return type; + } + }, + JAVA7 { + @Override + Type newArrayType(Type componentType) { + if (componentType instanceof Class) { + return getArrayClass((Class) componentType); + } else { + return new GenericArrayTypeImpl(componentType); + } + } + + @Override + Type usedInGenericType(Type type) { + return checkNotNull(type); + } + }, + JAVA8 { + @Override + Type newArrayType(Type componentType) { + return JAVA7.newArrayType(componentType); + } + + @Override + Type usedInGenericType(Type type) { + return JAVA7.usedInGenericType(type); + } + + @Override + String typeName(Type type) { + try { + Method getTypeName = Type.class.getMethod("getTypeName"); + return (String) getTypeName.invoke(type); + } catch (NoSuchMethodException e) { + throw new AssertionError("Type.getTypeName should be available in Java 8"); + /* + * Do not merge the 2 catch blocks below. javac would infer a type of + * ReflectiveOperationException, which Animal Sniffer would reject. (Old versions of + * Android don't *seem* to mind, but there might be edge cases of which we're unaware.) + */ + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + }, + JAVA9 { + @Override + Type newArrayType(Type componentType) { + return JAVA8.newArrayType(componentType); + } + + @Override + Type usedInGenericType(Type type) { + return JAVA8.usedInGenericType(type); + } + + @Override + String typeName(Type type) { + return JAVA8.typeName(type); + } + + @Override + boolean jdkTypeDuplicatesOwnerName() { + return false; + } + }; + + static final JavaVersion CURRENT; + + static { + if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) { + if (new TypeCapture>() {}.capture() + .toString() + .contains("java.util.Map.java.util.Map")) { + CURRENT = JAVA8; + } else { + CURRENT = JAVA9; + } + } else if (new TypeCapture() {}.capture() instanceof Class) { + CURRENT = JAVA7; + } else { + CURRENT = JAVA6; + } + } + + abstract Type newArrayType(Type componentType); + + abstract Type usedInGenericType(Type type); + + final ImmutableList usedInGenericType(Type[] types) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (Type type : types) { + builder.add(usedInGenericType(type)); + } + return builder.build(); + } + + String typeName(Type type) { + return Types.toString(type); + } + + boolean jdkTypeDuplicatesOwnerName() { + return true; + } + } + + /** + * Per issue 1635, + * In JDK 1.7.0_51-b13, {@link TypeVariableImpl#equals(Object)} is changed to no longer be equal + * to custom TypeVariable implementations. As a result, we need to make sure our TypeVariable + * implementation respects symmetry. Moreover, we don't want to reconstruct a native type variable + * {@code } using our implementation unless some of its bounds have changed in resolution. This + * avoids creating unequal TypeVariable implementation unnecessarily. When the bounds do change, + * however, it's fine for the synthetic TypeVariable to be unequal to any native TypeVariable + * anyway. + */ + static final class NativeTypeVariableEquals { + static final boolean NATIVE_TYPE_VARIABLE_ONLY = + !NativeTypeVariableEquals.class.getTypeParameters()[0].equals( + newArtificialTypeVariable(NativeTypeVariableEquals.class, "X")); + } + + private Types() {} +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/package-info.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/package-info.java new file mode 100644 index 0000000000000..f52e9001ab054 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/reflect/package-info.java @@ -0,0 +1,20 @@ +/* + * 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.iotdb.google.common.reflect; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractCatchingFuture.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractCatchingFuture.java new file mode 100644 index 0000000000000..e1147646dc114 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractCatchingFuture.java @@ -0,0 +1,243 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtCompatible; +import org.apache.iotdb.google.common.base.Function; +import org.apache.iotdb.google.common.util.concurrent.internal.InternalFutureFailureAccess; +import org.apache.iotdb.google.common.util.concurrent.internal.InternalFutures; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +import static org.apache.iotdb.google.common.base.Preconditions.checkNotNull; +import static org.apache.iotdb.google.common.util.concurrent.Futures.getDone; +import static org.apache.iotdb.google.common.util.concurrent.MoreExecutors.rejectionPropagatingExecutor; +import static org.apache.iotdb.google.common.util.concurrent.NullnessCasts.uncheckedCastNullableTToT; +import static org.apache.iotdb.google.common.util.concurrent.Platform.isInstanceOfThrowableClass; +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** Implementations of {@code Futures.catching*}. */ +@GwtCompatible +@ElementTypesAreNonnullByDefault +@SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |. +abstract class AbstractCatchingFuture + extends FluentFuture.TrustedFuture implements Runnable { + static ListenableFuture create( + ListenableFuture input, + Class exceptionType, + Function fallback, + Executor executor) { + CatchingFuture future = new CatchingFuture<>(input, exceptionType, fallback); + input.addListener(future, rejectionPropagatingExecutor(executor, future)); + return future; + } + + static ListenableFuture create( + ListenableFuture input, + Class exceptionType, + AsyncFunction fallback, + Executor executor) { + AsyncCatchingFuture future = new AsyncCatchingFuture<>(input, exceptionType, fallback); + input.addListener(future, rejectionPropagatingExecutor(executor, future)); + return future; + } + + /* + * In certain circumstances, this field might theoretically not be visible to an afterDone() call + * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture. + */ + ListenableFuture inputFuture; + Class exceptionType; + F fallback; + + AbstractCatchingFuture( + ListenableFuture inputFuture, Class exceptionType, F fallback) { + this.inputFuture = checkNotNull(inputFuture); + this.exceptionType = checkNotNull(exceptionType); + this.fallback = checkNotNull(fallback); + } + + @Override + public final void run() { + ListenableFuture localInputFuture = inputFuture; + Class localExceptionType = exceptionType; + F localFallback = fallback; + if (localInputFuture == null | localExceptionType == null | localFallback == null + // This check, unlike all the others, is a volatile read + || isCancelled()) { + return; + } + inputFuture = null; + + // For an explanation of the cases here, see the comments on AbstractTransformFuture.run. + V sourceResult = null; + Throwable throwable = null; + try { + if (localInputFuture instanceof InternalFutureFailureAccess) { + throwable = + InternalFutures.tryInternalFastPathGetFailure( + (InternalFutureFailureAccess) localInputFuture); + } + if (throwable == null) { + sourceResult = getDone(localInputFuture); + } + } catch (ExecutionException e) { + throwable = e.getCause(); + if (throwable == null) { + throwable = + new NullPointerException( + "Future type " + + localInputFuture.getClass() + + " threw " + + e.getClass() + + " without a cause"); + } + } catch (RuntimeException | Error e) { // this includes cancellation exception + throwable = e; + } + + if (throwable == null) { + /* + * The cast is safe: There was no exception, so the assignment from getDone must have + * succeeded. + */ + set(uncheckedCastNullableTToT(sourceResult)); + return; + } + + if (!isInstanceOfThrowableClass(throwable, localExceptionType)) { + setFuture(localInputFuture); + // TODO(cpovirk): Test that fallback is not run in this case. + return; + } + + @SuppressWarnings("unchecked") // verified safe by isInstanceOfThrowableClass + X castThrowable = (X) throwable; + T fallbackResult; + try { + fallbackResult = doFallback(localFallback, castThrowable); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + setException(t); + return; + } finally { + exceptionType = null; + fallback = null; + } + + setResult(fallbackResult); + } + + @Override + protected String pendingToString() { + ListenableFuture localInputFuture = inputFuture; + Class localExceptionType = exceptionType; + F localFallback = fallback; + String superString = super.pendingToString(); + String resultString = ""; + if (localInputFuture != null) { + resultString = "inputFuture=[" + localInputFuture + "], "; + } + if (localExceptionType != null && localFallback != null) { + return resultString + + "exceptionType=[" + + localExceptionType + + "], fallback=[" + + localFallback + + "]"; + } else if (superString != null) { + return resultString + superString; + } + return null; + } + + /** Template method for subtypes to actually run the fallback. */ + @ParametricNullness + abstract T doFallback(F fallback, X throwable) throws Exception; + + /** Template method for subtypes to actually set the result. */ + abstract void setResult(@ParametricNullness T result); + + @Override + protected final void afterDone() { + maybePropagateCancellationTo(inputFuture); + this.inputFuture = null; + this.exceptionType = null; + this.fallback = null; + } + + /** + * An {@link AbstractCatchingFuture} that delegates to an {@link AsyncFunction} and {@link + * #setFuture(ListenableFuture)}. + */ + private static final class AsyncCatchingFuture + extends AbstractCatchingFuture< + V, X, AsyncFunction, ListenableFuture> { + AsyncCatchingFuture( + ListenableFuture input, + Class exceptionType, + AsyncFunction fallback) { + super(input, exceptionType, fallback); + } + + @Override + ListenableFuture doFallback( + AsyncFunction fallback, X cause) throws Exception { + ListenableFuture replacement = fallback.apply(cause); + checkNotNull( + replacement, + "AsyncFunction.apply returned null instead of a Future. " + + "Did you mean to return immediateFuture(null)? %s", + fallback); + return replacement; + } + + @Override + void setResult(ListenableFuture result) { + setFuture(result); + } + } + + /** + * An {@link AbstractCatchingFuture} that delegates to a {@link Function} and {@link + * #set(Object)}. + */ + private static final class CatchingFuture + extends AbstractCatchingFuture, V> { + CatchingFuture( + ListenableFuture input, + Class exceptionType, + Function fallback) { + super(input, exceptionType, fallback); + } + + @Override + @ParametricNullness + V doFallback(Function fallback, X cause) throws Exception { + return fallback.apply(cause); + } + + @Override + void setResult(@ParametricNullness V result) { + set(result); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractExecutionThreadService.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractExecutionThreadService.java new file mode 100644 index 0000000000000..c3258b637084e --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -0,0 +1,274 @@ +/* + * 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.iotdb.google.common.util.concurrent; + +import org.apache.iotdb.google.common.annotations.GwtIncompatible; +import org.apache.iotdb.google.common.annotations.J2ktIncompatible; + +import java.time.Duration; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.apache.iotdb.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException; + +/** + * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown} + * methods. This class uses a single thread to execute the service; consider {@link AbstractService} + * if you would like to manage any threading manually. + * + * @author Jesse Wilson + * @since 1.0 + */ +@GwtIncompatible +@J2ktIncompatible +@ElementTypesAreNonnullByDefault +public abstract class AbstractExecutionThreadService implements Service { + private static final Logger logger = + Logger.getLogger(AbstractExecutionThreadService.class.getName()); + + /* use AbstractService for state management */ + private final Service delegate = + new AbstractService() { + @Override + protected final void doStart() { + Executor executor = MoreExecutors.renamingDecorator(executor(), () -> serviceName()); + executor.execute( + () -> { + try { + startUp(); + notifyStarted(); + // If stopAsync() is called while starting we may be in the STOPPING state in + // which case we should skip right down to shutdown. + if (isRunning()) { + try { + AbstractExecutionThreadService.this.run(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + try { + shutDown(); + } catch (Exception ignored) { + restoreInterruptIfIsInterruptedException(ignored); + // TODO(lukes): if guava ever moves to java7, this would be a good + // candidate for a suppressed exception, or maybe we could generalize + // Closer.Suppressor + logger.log( + Level.WARNING, + "Error while attempting to shut down the service after failure.", + ignored); + } + notifyFailed(t); + return; + } + } + + shutDown(); + notifyStopped(); + } catch (Throwable t) { + restoreInterruptIfIsInterruptedException(t); + notifyFailed(t); + } + }); + } + + @Override + protected void doStop() { + triggerShutdown(); + } + + @Override + public String toString() { + return AbstractExecutionThreadService.this.toString(); + } + }; + + /** Constructor for use by subclasses. */ + protected AbstractExecutionThreadService() {} + + /** + * Start the service. This method is invoked on the execution thread. + * + *

If this type is a type variable or wildcard, upper bounds that are themselves type variables + * aren't included (their super interfaces and superclasses are). + */ + public final TypeSet getTypes() { + return new TypeSet(); + } + + /** + * Returns the generic form of {@code superclass}. For example, if this is {@code + * ArrayList}, {@code Iterable} is returned given the input {@code + * Iterable.class}. + */ + public final TypeToken getSupertype(Class superclass) { + checkArgument( + this.someRawTypeIsSubclassOf(superclass), + "%s is not a super class of %s", + superclass, + this); + if (runtimeType instanceof TypeVariable) { + return getSupertypeFromUpperBounds(superclass, ((TypeVariable) runtimeType).getBounds()); + } + if (runtimeType instanceof WildcardType) { + return getSupertypeFromUpperBounds(superclass, ((WildcardType) runtimeType).getUpperBounds()); + } + if (superclass.isArray()) { + return getArraySupertype(superclass); + } + @SuppressWarnings("unchecked") // resolved supertype + TypeToken supertype = + (TypeToken) resolveSupertype(toGenericType(superclass).runtimeType); + return supertype; + } + + /** + * Returns subtype of {@code this} with {@code subclass} as the raw class. For example, if this is + * {@code Iterable} and {@code subclass} is {@code List}, {@code List} is + * returned. + */ + public final TypeToken getSubtype(Class subclass) { + checkArgument( + !(runtimeType instanceof TypeVariable), "Cannot get subtype of type variable <%s>", this); + if (runtimeType instanceof WildcardType) { + return getSubtypeFromLowerBounds(subclass, ((WildcardType) runtimeType).getLowerBounds()); + } + // unwrap array type if necessary + if (isArray()) { + return getArraySubtype(subclass); + } + // At this point, it's either a raw class or parameterized type. + checkArgument( + getRawType().isAssignableFrom(subclass), "%s isn't a subclass of %s", subclass, this); + Type resolvedTypeArgs = resolveTypeArgsForSubclass(subclass); + @SuppressWarnings("unchecked") // guarded by the isAssignableFrom() statement above + TypeToken subtype = (TypeToken) of(resolvedTypeArgs); + checkArgument( + subtype.isSubtypeOf(this), "%s does not appear to be a subtype of %s", subtype, this); + return subtype; + } + + /** + * Returns true if this type is a supertype of the given {@code type}. "Supertype" is defined + * according to the rules for type + * arguments introduced with Java generics. + * + * @since 19.0 + */ + public final boolean isSupertypeOf(TypeToken type) { + return type.isSubtypeOf(getType()); + } + + /** + * Returns true if this type is a supertype of the given {@code type}. "Supertype" is defined + * according to the rules for type + * arguments introduced with Java generics. + * + * @since 19.0 + */ + public final boolean isSupertypeOf(Type type) { + return of(type).isSubtypeOf(getType()); + } + + /** + * Returns true if this type is a subtype of the given {@code type}. "Subtype" is defined + * according to the rules for type + * arguments introduced with Java generics. + * + * @since 19.0 + */ + public final boolean isSubtypeOf(TypeToken type) { + return isSubtypeOf(type.getType()); + } + + /** + * Returns true if this type is a subtype of the given {@code type}. "Subtype" is defined + * according to the rules for type + * arguments introduced with Java generics. + * + * @since 19.0 + */ + public final boolean isSubtypeOf(Type supertype) { + checkNotNull(supertype); + if (supertype instanceof WildcardType) { + // if 'supertype' is , 'this' can be: + // Foo, SubFoo, . + // if 'supertype' is , nothing is a subtype. + return any(((WildcardType) supertype).getLowerBounds()).isSupertypeOf(runtimeType); + } + // if 'this' is wildcard, it's a suptype of to 'supertype' if any of its "extends" + // bounds is a subtype of 'supertype'. + if (runtimeType instanceof WildcardType) { + // is of no use in checking 'from' being a subtype of 'to'. + return any(((WildcardType) runtimeType).getUpperBounds()).isSubtypeOf(supertype); + } + // if 'this' is type variable, it's a subtype if any of its "extends" + // bounds is a subtype of 'supertype'. + if (runtimeType instanceof TypeVariable) { + return runtimeType.equals(supertype) + || any(((TypeVariable) runtimeType).getBounds()).isSubtypeOf(supertype); + } + if (runtimeType instanceof GenericArrayType) { + return of(supertype).isSupertypeOfArray((GenericArrayType) runtimeType); + } + // Proceed to regular Type subtype check + if (supertype instanceof Class) { + return this.someRawTypeIsSubclassOf((Class) supertype); + } else if (supertype instanceof ParameterizedType) { + return this.isSubtypeOfParameterizedType((ParameterizedType) supertype); + } else if (supertype instanceof GenericArrayType) { + return this.isSubtypeOfArrayType((GenericArrayType) supertype); + } else { // to instanceof TypeVariable + return false; + } + } + + /** + * Returns true if this type is known to be an array type, such as {@code int[]}, {@code T[]}, + * {@code []>} etc. + */ + public final boolean isArray() { + return getComponentType() != null; + } + + /** + * Returns true if this type is one of the nine primitive types (including {@code void}). + * + * @since 15.0 + */ + public final boolean isPrimitive() { + return (runtimeType instanceof Class) && ((Class) runtimeType).isPrimitive(); + } + + /** + * Returns the corresponding wrapper type if this is a primitive type; otherwise returns {@code + * this} itself. Idempotent. + * + * @since 15.0 + */ + public final TypeToken wrap() { + if (isPrimitive()) { + @SuppressWarnings("unchecked") // this is a primitive class + Class type = (Class) runtimeType; + return of(Primitives.wrap(type)); + } + return this; + } + + private boolean isWrapper() { + return Primitives.allWrapperTypes().contains(runtimeType); + } + + /** + * Returns the corresponding primitive type if this is a wrapper type; otherwise returns {@code + * this} itself. Idempotent. + * + * @since 15.0 + */ + public final TypeToken unwrap() { + if (isWrapper()) { + @SuppressWarnings("unchecked") // this is a wrapper class + Class type = (Class) runtimeType; + return of(Primitives.unwrap(type)); + } + return this; + } + + /** + * Returns the array component type if this type represents an array ({@code int[]}, {@code T[]}, + * {@code []>} etc.), or else {@code null} is returned. + */ + public final TypeToken getComponentType() { + Type componentType = Types.getComponentType(runtimeType); + if (componentType == null) { + return null; + } + return of(componentType); + } + + /** + * Returns the {@link Invokable} for {@code method}, which must be a member of {@code T}. + * + * @since 14.0 + */ + public final Invokable method(Method method) { + checkArgument( + this.someRawTypeIsSubclassOf(method.getDeclaringClass()), + "%s not declared by %s", + method, + this); + return new Invokable.MethodInvokable(method) { + @Override + Type getGenericReturnType() { + return getCovariantTypeResolver().resolveType(super.getGenericReturnType()); + } + + @Override + Type[] getGenericParameterTypes() { + return getInvariantTypeResolver().resolveTypesInPlace(super.getGenericParameterTypes()); + } + + @Override + Type[] getGenericExceptionTypes() { + return getCovariantTypeResolver().resolveTypesInPlace(super.getGenericExceptionTypes()); + } + + @Override + public TypeToken getOwnerType() { + return TypeToken.this; + } + + @Override + public String toString() { + return getOwnerType() + "." + super.toString(); + } + }; + } + + /** + * Returns the {@link Invokable} for {@code constructor}, which must be a member of {@code T}. + * + * @since 14.0 + */ + public final Invokable constructor(Constructor constructor) { + checkArgument( + constructor.getDeclaringClass() == getRawType(), + "%s not declared by %s", + constructor, + getRawType()); + return new Invokable.ConstructorInvokable(constructor) { + @Override + Type getGenericReturnType() { + return getCovariantTypeResolver().resolveType(super.getGenericReturnType()); + } + + @Override + Type[] getGenericParameterTypes() { + return getInvariantTypeResolver().resolveTypesInPlace(super.getGenericParameterTypes()); + } + + @Override + Type[] getGenericExceptionTypes() { + return getCovariantTypeResolver().resolveTypesInPlace(super.getGenericExceptionTypes()); + } + + @Override + public TypeToken getOwnerType() { + return TypeToken.this; + } + + @Override + public String toString() { + return getOwnerType() + "(" + Joiner.on(", ").join(getGenericParameterTypes()) + ")"; + } + }; + } + + /** + * The set of interfaces and classes that {@code T} is or is a subtype of. {@link Object} is not + * included in the set if this type is an interface. + * + * @since 13.0 + */ + public class TypeSet extends ForwardingSet> implements Serializable { + + private transient ImmutableSet> types; + + TypeSet() {} + + /** Returns the types that are interfaces implemented by this type. */ + public TypeSet interfaces() { + return new InterfaceSet(this); + } + + /** Returns the types that are classes. */ + public TypeSet classes() { + return new ClassSet(); + } + + @Override + protected Set> delegate() { + ImmutableSet> filteredTypes = types; + if (filteredTypes == null) { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList> collectedTypes = + (ImmutableList) TypeCollector.FOR_GENERIC_TYPE.collectTypes(TypeToken.this); + return (types = + FluentIterable.from(collectedTypes) + .filter(TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD) + .toSet()); + } else { + return filteredTypes; + } + } + + /** Returns the raw types of the types in this set, in the same order. */ + public Set> rawTypes() { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList> collectedTypes = + (ImmutableList) TypeCollector.FOR_RAW_TYPE.collectTypes(getRawTypes()); + return ImmutableSet.copyOf(collectedTypes); + } + + private static final long serialVersionUID = 0; + } + + private final class InterfaceSet extends TypeSet { + + private final transient TypeSet allTypes; + private transient ImmutableSet> interfaces; + + InterfaceSet(TypeSet allTypes) { + this.allTypes = allTypes; + } + + @Override + protected Set> delegate() { + ImmutableSet> result = interfaces; + if (result == null) { + return (interfaces = + FluentIterable.from(allTypes).filter(TypeFilter.INTERFACE_ONLY).toSet()); + } else { + return result; + } + } + + @Override + public TypeSet interfaces() { + return this; + } + + @Override + public Set> rawTypes() { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList> collectedTypes = + (ImmutableList) TypeCollector.FOR_RAW_TYPE.collectTypes(getRawTypes()); + return FluentIterable.from(collectedTypes).filter(Class::isInterface).toSet(); + } + + @Override + public TypeSet classes() { + throw new UnsupportedOperationException("interfaces().classes() not supported."); + } + + private Object readResolve() { + return getTypes().interfaces(); + } + + private static final long serialVersionUID = 0; + } + + private final class ClassSet extends TypeSet { + + private transient ImmutableSet> classes; + + @Override + protected Set> delegate() { + ImmutableSet> result = classes; + if (result == null) { + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList> collectedTypes = + (ImmutableList) + TypeCollector.FOR_GENERIC_TYPE.classesOnly().collectTypes(TypeToken.this); + return (classes = + FluentIterable.from(collectedTypes) + .filter(TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD) + .toSet()); + } else { + return result; + } + } + + @Override + public TypeSet classes() { + return this; + } + + @Override + public Set> rawTypes() { + // Java has no way to express ? super T when we parameterize TypeToken vs. Class. + @SuppressWarnings({"unchecked", "rawtypes"}) + ImmutableList> collectedTypes = + (ImmutableList) TypeCollector.FOR_RAW_TYPE.classesOnly().collectTypes(getRawTypes()); + return ImmutableSet.copyOf(collectedTypes); + } + + @Override + public TypeSet interfaces() { + throw new UnsupportedOperationException("classes().interfaces() not supported."); + } + + private Object readResolve() { + return getTypes().classes(); + } + + private static final long serialVersionUID = 0; + } + + private enum TypeFilter implements Predicate> { + IGNORE_TYPE_VARIABLE_OR_WILDCARD { + @Override + public boolean apply(TypeToken type) { + return !(type.runtimeType instanceof TypeVariable + || type.runtimeType instanceof WildcardType); + } + }, + INTERFACE_ONLY { + @Override + public boolean apply(TypeToken type) { + return type.getRawType().isInterface(); + } + } + } + + /** + * Returns true if {@code o} is another {@code TypeToken} that represents the same {@link Type}. + */ + @Override + public boolean equals(Object o) { + if (o instanceof TypeToken) { + TypeToken that = (TypeToken) o; + return runtimeType.equals(that.runtimeType); + } + return false; + } + + @Override + public int hashCode() { + return runtimeType.hashCode(); + } + + @Override + public String toString() { + return Types.toString(runtimeType); + } + + /** Implemented to support serialization of subclasses. */ + protected Object writeReplace() { + // TypeResolver just transforms the type to our own impls that are Serializable + // except TypeVariable. + return of(new TypeResolver().resolveType(runtimeType)); + } + + /** + * Ensures that this type token doesn't contain type variables, which can cause unchecked type + * errors for callers like {@link TypeToInstanceMap}. + */ + final TypeToken rejectTypeVariables() { + new TypeVisitor() { + @Override + void visitTypeVariable(TypeVariable type) { + throw new IllegalArgumentException( + runtimeType + "contains a type variable and is not safe for the operation"); + } + + @Override + void visitWildcardType(WildcardType type) { + visit(type.getLowerBounds()); + visit(type.getUpperBounds()); + } + + @Override + void visitParameterizedType(ParameterizedType type) { + visit(type.getActualTypeArguments()); + visit(type.getOwnerType()); + } + + @Override + void visitGenericArrayType(GenericArrayType type) { + visit(type.getGenericComponentType()); + } + }.visit(runtimeType); + return this; + } + + private boolean someRawTypeIsSubclassOf(Class superclass) { + for (Class rawType : getRawTypes()) { + if (superclass.isAssignableFrom(rawType)) { + return true; + } + } + return false; + } + + private boolean isSubtypeOfParameterizedType(ParameterizedType supertype) { + Class matchedClass = of(supertype).getRawType(); + if (!someRawTypeIsSubclassOf(matchedClass)) { + return false; + } + TypeVariable[] typeVars = matchedClass.getTypeParameters(); + Type[] supertypeArgs = supertype.getActualTypeArguments(); + for (int i = 0; i < typeVars.length; i++) { + Type subtypeParam = getCovariantTypeResolver().resolveType(typeVars[i]); + // If 'supertype' is "List" + // and 'this' is StringArrayList, + // First step is to figure out StringArrayList "is-a" List where = String. + // String is then matched against , the supertypeArgs[0]. + if (!of(subtypeParam).is(supertypeArgs[i], typeVars[i])) { + return false; + } + } + // We only care about the case when the supertype is a non-static inner class + // in which case we need to make sure the subclass's owner type is a subtype of the + // supertype's owner. + return Modifier.isStatic(((Class) supertype.getRawType()).getModifiers()) + || supertype.getOwnerType() == null + || isOwnedBySubtypeOf(supertype.getOwnerType()); + } + + private boolean isSubtypeOfArrayType(GenericArrayType supertype) { + if (runtimeType instanceof Class) { + Class fromClass = (Class) runtimeType; + if (!fromClass.isArray()) { + return false; + } + return of(fromClass.getComponentType()).isSubtypeOf(supertype.getGenericComponentType()); + } else if (runtimeType instanceof GenericArrayType) { + GenericArrayType fromArrayType = (GenericArrayType) runtimeType; + return of(fromArrayType.getGenericComponentType()) + .isSubtypeOf(supertype.getGenericComponentType()); + } else { + return false; + } + } + + private boolean isSupertypeOfArray(GenericArrayType subtype) { + if (runtimeType instanceof Class) { + Class thisClass = (Class) runtimeType; + if (!thisClass.isArray()) { + return thisClass.isAssignableFrom(Object[].class); + } + return of(subtype.getGenericComponentType()).isSubtypeOf(thisClass.getComponentType()); + } else if (runtimeType instanceof GenericArrayType) { + return of(subtype.getGenericComponentType()) + .isSubtypeOf(((GenericArrayType) runtimeType).getGenericComponentType()); + } else { + return false; + } + } + + /** + * {@code A.is(B)} is defined as {@code Foo.isSubtypeOf(Foo)}. + * + *

See the Guava User Guide article on {@code Splitter}. + * + * @author Julien Silland + * @author Jesse Wilson + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 1.0 + */ +@GwtCompatible(emulated = true) +@ElementTypesAreNonnullByDefault +public final class Splitter { + private final CharMatcher trimmer; + private final boolean omitEmptyStrings; + private final Strategy strategy; + private final int limit; + + private Splitter(Strategy strategy) { + this(strategy, false, CharMatcher.none(), Integer.MAX_VALUE); + } + + private Splitter(Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) { + this.strategy = strategy; + this.omitEmptyStrings = omitEmptyStrings; + this.trimmer = trimmer; + this.limit = limit; + } + + /** + * Returns a splitter that uses the given single-character separator. For example, {@code + * Splitter.on(',').split("foo,,bar")} returns an iterable containing {@code ["foo", "", "bar"]}. + * + * @param separator the character to recognize as a separator + * @return a splitter, with default settings, that recognizes that separator + */ + public static Splitter on(char separator) { + return on(CharMatcher.is(separator)); + } + + /** + * Returns a splitter that considers any single character matched by the given {@code CharMatcher} + * to be a separator. For example, {@code + * Splitter.on(CharMatcher.anyOf(";,")).split("foo,;bar,quux")} returns an iterable containing + * {@code ["foo", "", "bar", "quux"]}. + * + * @param separatorMatcher a {@link CharMatcher} that determines whether a character is a + * separator + * @return a splitter, with default settings, that uses this matcher + */ + public static Splitter on(final CharMatcher separatorMatcher) { + checkNotNull(separatorMatcher); + + return new Splitter( + new Strategy() { + @Override + public SplittingIterator iterator(Splitter splitter, final CharSequence toSplit) { + return new SplittingIterator(splitter, toSplit) { + @Override + int separatorStart(int start) { + return separatorMatcher.indexIn(toSplit, start); + } + + @Override + int separatorEnd(int separatorPosition) { + return separatorPosition + 1; + } + }; + } + }); + } + + /** + * Returns a splitter that uses the given fixed string as a separator. For example, {@code + * Splitter.on(", ").split("foo, bar,baz")} returns an iterable containing {@code ["foo", + * "bar,baz"]}. + * + * @param separator the literal, nonempty string to recognize as a separator + * @return a splitter, with default settings, that recognizes that separator + */ + public static Splitter on(final String separator) { + checkArgument(separator.length() != 0, "The separator may not be the empty string."); + if (separator.length() == 1) { + return Splitter.on(separator.charAt(0)); + } + return new Splitter( + new Strategy() { + @Override + public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + int separatorLength = separator.length(); + + positions: + for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) { + for (int i = 0; i < separatorLength; i++) { + if (toSplit.charAt(i + p) != separator.charAt(i)) { + continue positions; + } + } + return p; + } + return -1; + } + + @Override + public int separatorEnd(int separatorPosition) { + return separatorPosition + separator.length(); + } + }; + } + }); + } + + /** + * Returns a splitter that considers any subsequence matching {@code pattern} to be a separator. + * For example, {@code Splitter.on(Pattern.compile("\r?\n")).split(entireFile)} splits a string + * into lines whether it uses DOS-style or UNIX-style line terminators. + * + * @param separatorPattern the pattern that determines whether a subsequence is a separator. This + * pattern may not match the empty string. + * @return a splitter, with default settings, that uses this pattern + * @throws IllegalArgumentException if {@code separatorPattern} matches the empty string + */ + @J2ktIncompatible + @GwtIncompatible // java.util.regex + public static Splitter on(Pattern separatorPattern) { + return on(new JdkPattern(separatorPattern)); + } + + private static Splitter on(final CommonPattern separatorPattern) { + checkArgument( + !separatorPattern.matcher("").matches(), + "The pattern may not match the empty string: %s", + separatorPattern); + + return new Splitter( + new Strategy() { + @Override + public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) { + final CommonMatcher matcher = separatorPattern.matcher(toSplit); + return new SplittingIterator(splitter, toSplit) { + @Override + public int separatorStart(int start) { + return matcher.find(start) ? matcher.start() : -1; + } + + @Override + public int separatorEnd(int separatorPosition) { + return matcher.end(); + } + }; + } + }); + } + + /** + * Returns a splitter that considers any subsequence matching a given pattern (regular expression) + * to be a separator. For example, {@code Splitter.onPattern("\r?\n").split(entireFile)} splits a + * string into lines whether it uses DOS-style or UNIX-style line terminators. This is equivalent + * to {@code Splitter.on(Pattern.compile(pattern))}. + * + * @param separatorPattern the pattern that determines whether a subsequence is a separator. This + * pattern may not match the empty string. + * @return a splitter, with default settings, that uses this pattern + * @throws IllegalArgumentException if {@code separatorPattern} matches the empty string or is a + * malformed expression + */ + @J2ktIncompatible + @GwtIncompatible // java.util.regex + public static Splitter onPattern(String separatorPattern) { + return on(Platform.compilePattern(separatorPattern)); + } + + /** + * Returns a splitter that divides strings into pieces of the given length. For example, {@code + * Splitter.fixedLength(2).split("abcde")} returns an iterable containing {@code ["ab", "cd", + * "e"]}. The last piece can be smaller than {@code length} but will never be empty. + * + *